1 /*
2 cmc.c - Cyan's Megadrive ROM copier support for uCON64
3 
4 Copyright (c) 1999 - 2004                      Cyan Helkaraxe
5 Portions copyright (c) 2004, 2015 - 2017, 2021 dbjh
6 
7 Special thanks to dbjh for helping with the uCON64 integration
8 of this software, and providing the wrapping code.
9 
10 CMC version: 2.5
11 For hardware version 1.x
12 
13 Copies Sega Megadrive/Genesis cartridges into .BIN format ROM files.
14 
15 
16 This program is free software; you can redistribute it and/or modify
17 it under the terms of the GNU General Public License as published by
18 the Free Software Foundation; either version 2 of the License, or
19 (at your option) any later version.
20 
21 This program is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 GNU General Public License for more details.
25 
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 */
30 
31 /*
32 Additional information:
33 This software is distributed in accordance with the GNU General
34 Public License. The author of the hardware design/documentation and
35 software, Cyan Helkaraxe, retains the copyright over the 'cmc' code
36 ('software'), the hardware design and construction documentation itself.
37 Cyan grants you the rights of the GNU General Public License over
38 the software, as stated above. The copyright does not affect your
39 rights in relation to the 'cmc' code under the GNU General Public
40 License in any way.
41 Cyan Helkaraxe does NOT grant you any rights relating to the hardware
42 design/documentation, over and above the right to build the device for
43 personal, not-for-profit use. Likewise, the same applies to the ROM
44 copier construction documentation, available at
45 http://www.emulationzone.org/projects/cyan/docs/ for not-for-profit
46 personal use.
47 
48 Obviously, feel free to make changes to this software, but if you do so,
49 *please* clearly mark the fact it is modified, to avoid confusion.
50 A define is provided below this commented area, which should be edited
51 as described. This is to inform users which version of the code they are
52 using at runtime, and if they encounter a problem, it makes it far easier
53 for me to help debug it. I have no evil nefarious intentions. =P
54 
55 Obviously, your changes must adhere to the GNU GPL, and please keep the
56 copyright, Cyan's name, e-mail and web site address in the comments
57 somewhere.
58 
59 If you wish to do anything with the hardware design or the documentation
60 that goes beyond personal use, get in touch with Cyan first;
61 cyan@emulationzone.org
62 
63 Disclaimer / Warranty:
64 This is to emphasise, not replace, any warranty information provided in
65 the GNU GPL or uCON64.
66 There is no warranty whatsoever, for either the software or accompanying
67 hardware/design/documentation. This software is provided free of charge,
68 AS-IS, in the hope that it may be useful.
69 Use it at your own risk. The author does not make any guarantee that you
70 will be able to use this software or accompanying
71 hardware/design/documentation, or that it will perform smoothly. There is
72 a possibility that damage or loss (including but not limited to financial
73 loss, hardware or software damage, data corruption, privacy violation,
74 or personal injury, suffering, legal action, imprisonment or death) may
75 arise through the use of this software or the accompanying
76 hardware/design/documentation, in addition to many other possible outcomes.
77 You take sole responsibility for any outcomes; by using this software or
78 accompanying hardware/design/ documentation, you agree that the author will
79 not be held responsible for anything that may occur. If your jurisdiction
80 does not allow the author to be isolated from responsibility, then you
81 must *not* use this software or accompanying hardware/design/documentation.
82 The author does not condone software piracy. You may not use this software
83 to engage in piracy. The author is not responsible for anything you choose
84 to use this software for, although the author strongly recommends that you
85 use the software for the purpose for which it was intended to be used --
86 primarily as an educational tool, and secondarily to make backup copies of
87 your expensive/rare game cartridges, or a similarly harmless and legal
88 purpose.
89 Note that although the author isn't aware of any patent violations caused
90 by this software/hardware/design/documentation, it is possible that
91 there may be patents covering parts of this device. It is your
92 responsibility to check this before building or using the hardware or
93 software, and Cyan may not be held responsible in the event of an
94 infringement.
95 
96 That being said, if you do encounter any problems with the hardware or
97 software, then feel free to get in touch with the author; use the subject
98 line 'ROM Copier Technical Support'. No promises or guarantees are made,
99 however.
100 
101 Also note that the author is not affiliated with anyone involved with uCON64;
102 therefore, only correspondence relating to this particular file (the 'cmc'
103 code) or the accompanying hardware design should be directed to Cyan.
104 If you have more general uCON64 questions, Cyan is *not* the person to ask.
105 Likewise, the terms "the author" and "software" in this file (cmc.c, and
106 additionally cmc.h), along with similar terms, apply only to Cyan, and the
107 CMC software you see in this file. The disclaimer above, for example, relates
108 exclusively to the CMC code.
109 
110 All trademarks, indicated or otherwise, are the property of their
111 respective owners.
112 */
113 
114 /*
115   NOTE!
116   Please edit the following line, and remove the word "original" from it
117   if you have made any modifications to this file. This reduces user
118   confusion.
119 */
120 #define INTRO_TEXT "Cyan's Megadrive Copier             (c) 1999-2004 Cyan Helkaraxe\n" \
121                    "Software version 2.5, designed for hardware version 1.x\n\n"
122 
123 
124 #ifdef  HAVE_CONFIG_H
125 #include "config.h"
126 #endif
127 #ifdef  _MSC_VER
128 #pragma warning(push)
129 #pragma warning(disable: 4668) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives'
130 #endif
131 #include <stdlib.h>
132 #ifdef  _MSC_VER
133 #pragma warning(pop)
134 #endif
135 #include <string.h>
136 #include "misc/archive.h"
137 #include "misc/bswap.h"
138 #include "misc/misc.h"
139 #include "misc/term.h"
140 #include "ucon64.h"
141 #include "ucon64_misc.h"
142 #include "backup/cmc.h"
143 
144 
145 #ifdef  USE_PARALLEL
146 
147 #define RSLO 0x40                               // reset line 1 d7
148 #define RSHI 0x01                               // reset line 2 d0
149 #define CNLO 0x10                               // counter line 1 d4
150 #define CNHI 0x04                               // counter line 2 d2
151 #define INLO 0x40                               // input 1 ack (int disabled)
152 #define INHI 0x10                               // input 2 selectin
153 
154 #define MBYTE (1024 * 1024)
155 #define DEFAULT_SPEED 3
156 
157 
158 /************************
159  *  Internal functions  *
160  ************************/
161 
162 static inline void
cyan_write_copier(unsigned char data,unsigned short parport)163 cyan_write_copier (unsigned char data, unsigned short parport)
164 // write a value to the data register of the parallel port
165 {
166   outportb (parport + PARPORT_DATA, data);
167 }
168 
169 
170 static inline unsigned char
cyan_read_copier(unsigned short parport)171 cyan_read_copier (unsigned short parport)
172 // read a value from the status register of the parallel port
173 {
174   return inportb (parport + PARPORT_STATUS);
175 }
176 
177 
178 static inline unsigned char
cyan_verify_copier(unsigned short parport)179 cyan_verify_copier (unsigned short parport)
180 // read a value back from the data register for verification
181 {
182   return inportb (parport + PARPORT_DATA);
183 }
184 
185 
186 /**** non-hardware, non-accessing ****/
187 
188 static unsigned long
cyan_calculate_rom_size(unsigned char * buffer,int test_mode)189 cyan_calculate_rom_size (unsigned char *buffer, int test_mode)
190 /*
191   Calculate the ROM size, by looking at the ROM size entry in the ROM 'header',
192   and the overall structure.
193   This function always returns a value rounded to a power of two between 128
194   kbytes and 4 Mbytes. It also inspects the ROM for 0's or ffh's. If test_mode
195   is 1 it causes an error on that condition, frees the buffer and exits.
196 */
197 {
198   unsigned long i = 0x80000000, reported_size;
199 
200   // look at reported size
201   reported_size = (buffer[0x1a4] << 24) +
202                   (buffer[0x1a5] << 16) +
203                   (buffer[0x1a6] << 8) +
204                    buffer[0x1a7] + 1;
205   // cap
206   // there is a minimum valid size for ROMs, according to some sources
207   if (reported_size < MBIT)
208     reported_size = MBIT;
209   if (reported_size > 4 * MBYTE)
210     reported_size = 4 * MBYTE;
211   // round
212   if (reported_size & (reported_size - 1))
213     {
214       while (!(reported_size & 0x80000000))
215         {
216           i >>= 1;
217           reported_size = (reported_size << 1) | 1;
218         }
219       reported_size = i << 1;
220     }
221   // calculate real size
222   for (i = 2 * MBYTE; i >= 65536; i >>= 1)
223     if (memcmp (buffer, buffer + i, i))
224       {
225         i >>= 1;
226         break;
227       }
228   i <<= 2;
229   if (reported_size < i)
230     reported_size = i;                          // pick the safest (largest) of the two
231   if (i == MBIT)
232     {
233       for (i = 0; i < MBIT; i++)
234         if ((buffer[i] != 0xff) && (buffer[i] != 0x00))
235           break;
236       if (i == MBIT)
237         {
238           FILE *output;
239 
240           if (test_mode)
241             {
242               output = stderr;
243               fputs ("\nERROR:   ", stderr);
244             }
245           else
246             {
247               output = stdout;
248               fputs ("\nWARNING: ", stdout);
249             }
250 
251           //     "WARNING: "
252           fputs (         "The ROM file appears to consist of nothing but 0x00 / 0xff values.\n"
253                  "         This usually indicates a serious problem. Perhaps your parallel port\n"
254                  "         isn't configured correctly, or there is some problem with the ROM\n"
255                  "         copier. Is it getting power? Is a cartridge inserted? Is it properly\n"
256                  "         attached to the PC?\n",
257                  output);
258           if (test_mode)
259             {
260               free (buffer);
261               exit (1);
262             }
263         }
264     }
265   return reported_size;
266 }
267 
268 
269 static int
cyan_checksum_rom(unsigned char * buffer)270 cyan_checksum_rom (unsigned char *buffer)
271 // return 0 on success, -1 on failure
272 {
273   unsigned char *buffer2 = buffer;
274   unsigned short reported_sum, running_sum = 0;
275 
276   reported_sum = (buffer2[0x18e] << 8) + buffer2[0x18f];
277   buffer2 += ((buffer2[0x1a4] << 24) +
278               (buffer2[0x1a5] << 16) +
279               (buffer2[0x1a6] << 8) +
280               (buffer2[0x1a7] & 0xfe)) + 2;
281   if (buffer2 > buffer + 4 * MBYTE)
282     buffer2 = buffer + 4 * MBYTE;
283   buffer += 0x200;
284   if (buffer2 < buffer + 2)
285     return -1;
286 
287   while (buffer2 != buffer)
288     {
289       running_sum += *--buffer2;
290       running_sum += (*--buffer2) << 8;
291     }
292 
293   return (running_sum & 0xffff) != reported_sum ? -1 : 0;
294 }
295 
296 
297 static inline unsigned long
cyan_get_address(unsigned long b)298 cyan_get_address (unsigned long b)
299 // return the true address (word -- 0 - 2 M) based on word input
300 {
301   return ((b & 0x000800) >> 11) |               // bit 0
302          ((b & 0x002000) >> 12) |               // bit 1
303          ((b & 0x004000) >> 12) |               // bit 2
304          ((b & 0x000020) >> 2) |                // bit 3
305          ((b & 0x100000) >> 16) |               // bit 4
306          ((b & 0x020000) >> 12) |               // bit 5
307          ((b & 0x000400) >> 4) |                // bit 6
308          ((b & 0x000001) << 7) |                // bit 7
309          ((b & 0x000002) << 7) |                // bit 8
310          ((b & 0x000010) << 5) |                // bit 9
311          ((b & 0x000040) << 4) |                // bit 10
312          ((b & 0x040000) >> 7) |                // bit 11
313          ((b & 0x080000) >> 7) |                // bit 12
314          ((b & 0x000080) << 6) |                // bit 13
315          ((b & 0x008000) >> 1) |                // bit 14
316          ((b & 0x010000) >> 1) |                // bit 15
317          ((b & 0x001000) << 4) |                // bit 16
318          ((b & 0x000004) << 15) |               // bit 17
319          ((b & 0x000008) << 15) |               // bit 18
320          ((b & 0x000200) << 10) |               // bit 19
321          ((b & 0x000100) << 12);                // bit 20
322 }
323 
324 
325 /**** non-hardware, indirectly accessing ****/
326 
327 static inline void
cyan_delay(int speed,unsigned short parport)328 cyan_delay (int speed, unsigned short parport)
329 // Delays a certain amount of time depending on speed selected. 0=long delay,
330 //  used for reset and hi counter.
331 {
332   int i;
333 
334   switch (speed)
335     {
336     case 0:
337       for (i = 0; i < 128; i++)
338         cyan_read_copier (parport);
339     case 1:                                     // falling through
340       for (i = 0; i < 64; i++)
341         cyan_read_copier (parport);
342     case 2:                                     // falling through
343       for (i = 0; i < 12; i++)
344         cyan_read_copier (parport);
345     case 3:                                     // falling through
346       cyan_read_copier (parport);
347       cyan_read_copier (parport);
348     }
349 }
350 
351 
352 static void
cyan_reset(unsigned short parport)353 cyan_reset (unsigned short parport)
354 // resets the copier
355 {
356   cyan_delay (0, parport);
357   // zero all data outputs first, before going into SPP mode
358   cyan_write_copier (0, parport);
359   // reset the port to SPP, float all control lines high
360   cyan_write_copier (0, parport + PARPORT_CONTROL);
361   cyan_delay (0, parport);
362   cyan_write_copier (RSLO | RSHI, parport);     // both reset lines hi
363   cyan_delay (0, parport);
364   cyan_write_copier (0, parport);               // both reset lines lo
365   cyan_delay (0, parport);
366 }
367 
368 
369 static inline unsigned short
cyan_get_word(int speed,unsigned short parport)370 cyan_get_word (int speed, unsigned short parport)
371 // gets a byte pair from the ROM and return two bytes in big endian byte order
372 {
373   unsigned short value = 0;
374   unsigned char tempz;
375 
376   cyan_write_copier (0, parport);
377   cyan_delay (speed, parport);
378   tempz = cyan_read_copier (parport);
379   value |= tempz & INLO;                        // bit 6
380   value |= (tempz & INHI) << 8;                 // bit 12
381 
382   cyan_write_copier (CNLO, parport);
383   cyan_delay (speed, parport);
384   tempz = cyan_read_copier (parport);
385   value |= (tempz & INLO) >> 5;                 // bit 1
386   value |= (tempz & INHI) << 9;                 // bit 13
387 
388   cyan_write_copier (0, parport);
389   cyan_delay (speed, parport);
390   tempz = cyan_read_copier (parport);
391   value |= (tempz & INLO) << 3;                 // bit 9
392   value |= (tempz & INHI) << 10;                // bit 14
393 
394   cyan_write_copier (CNLO, parport);
395   cyan_delay (speed, parport);
396   tempz = cyan_read_copier (parport);
397   value |= (tempz & INLO) >> 1;                 // bit 5
398   value |= (tempz & INHI) << 11;                // bit 15
399 
400   cyan_write_copier (0, parport);
401   cyan_delay (speed, parport);
402   tempz = cyan_read_copier (parport);
403   value |= (tempz & INLO) >> 4;                 // bit 2
404   value |= (tempz & INHI) << 4;                 // bit 8
405 
406   cyan_write_copier (CNLO, parport);
407   cyan_delay (speed, parport);
408   tempz = cyan_read_copier (parport);
409   value |= (tempz & INLO) << 4;                 // bit 10
410   value |= (tempz & INHI) >> 4;                 // bit 0
411 
412   cyan_write_copier (0, parport);
413   cyan_delay (speed, parport);
414   tempz = cyan_read_copier (parport);
415   value |= (tempz & INLO) >> 2;                 // bit 4
416   value |= (tempz & INHI) << 3;                 // bit 7
417 
418   cyan_write_copier (CNLO, parport);
419   cyan_delay (speed, parport);
420   tempz = cyan_read_copier (parport);
421   value |= (tempz & INLO) >> 3;                 // bit 3
422   value |= (tempz & INHI) << 7;                 // bit 11
423 
424   return me2be_16 (value);
425 }
426 
427 
428 static inline int
check_exit(void)429 check_exit (void)
430 // check for user abort
431 {
432   int temp;
433 
434   if (ucon64.frontend)
435     return 0;
436   if (!kbhit ())
437     return 0;
438   temp = getch ();
439   if (temp == 'q' || (temp == 27))
440     return 1;
441   return 0;
442 }
443 
444 
445 static unsigned char *
cyan_read_rom(int speed,unsigned short parport,unsigned char * buffer)446 cyan_read_rom (int speed, unsigned short parport, unsigned char *buffer)
447 /*
448   Read the ROM and return a pointer to a 4 MB area of memory containing all ROM
449   data. Designed to be used from inside cyan_copy_rom(), although it can be
450   called elsewhere if a raw (but decoded) dump is required.
451 */
452 {
453   unsigned long q;
454   time_t t;
455 
456   // allocate the dump area
457   if (!buffer)
458     if ((buffer = (unsigned char *) malloc (4 * MBYTE)) == NULL)
459       {
460         fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], 4 * MBYTE);
461         exit (1);
462       }
463 
464   cyan_reset (parport);                         // reset the copier
465 
466   t = time (NULL);
467   // copy routine
468   for (q = 0; q < 2 * MBYTE; )                  // loop through all words
469     {
470       // get a (16-bit) word from the ROM
471       ((unsigned short *) buffer)[cyan_get_address (q)] = cyan_get_word (speed, parport);
472 
473       // periodically update progress bar, without hammering ucon64_gauge()
474       if (!(q & (0xfff >> (5 - speed))))
475         {
476           if (check_exit ())
477             {
478               free (buffer);
479               puts ("\n"
480                     "Copy aborted.\n"
481                     "Don't forget to turn the ROM copier off and never insert or remove a cartridge\n"
482                     "with the power on");
483               break;
484             }
485           ucon64_gauge (t, q * 2, 4 * MBYTE);
486         }
487 
488       if (!(++q & 0x3ff)) // advance loop counter and carry to hi counter (11 bits)
489         {
490           cyan_delay (0, parport);
491           cyan_write_copier (CNHI, parport);
492           cyan_delay (0, parport);
493           cyan_write_copier (0, parport);
494           cyan_delay (0, parport);
495         }
496     }
497 
498   // make sure it's left in a state where it's safe to remove the cart
499   cyan_reset (parport);
500 
501   if (q != 2 * MBYTE)
502     return NULL;
503 
504   ucon64_gauge (t, q * 2, 4 * MBYTE);           // make the progress bar reach 100%
505 
506   return buffer;
507 }
508 
509 
510 static void
cyan_test_parport(unsigned short parport)511 cyan_test_parport (unsigned short parport)
512 // Test the parallel port to see if it appears to be functioning correctly, and
513 //  terminate if there's an error.
514 {
515   unsigned short temp;
516 
517   cyan_reset (parport);
518   fputs ("Basic parallel port test: ", stdout);
519   fflush (stdout);
520   cyan_write_copier (170, parport);
521   cyan_delay (0, parport);
522   temp = cyan_verify_copier (parport) & 170;
523   cyan_reset (parport);
524 
525   // even in unidirectional mode, the parallel port is bidirectional; at
526   //  least, for a few short moments before the driver IC smokes
527   if ((cyan_verify_copier (parport) & 170) != 0 || temp != 170)
528     {
529       puts ("FAILED");
530       fputs ("ERROR: Parallel port error\n"
531              "       Check that your parallel port is configured properly, in the BIOS, OS,\n"
532              "       and uCON64, and check for short circuits on the parallel port connector.\n"
533              "       Also ensure that the ROM copier is getting power, and a cartridge is\n"
534              "       inserted\n",
535              stderr);
536       exit (1);
537     }
538   else
539     puts ("Passed");
540 
541   // discharge caps to see if we've got power
542   cyan_reset (parport);
543   cyan_reset (parport);
544   cyan_write_copier (CNLO + CNHI, parport);
545   cyan_delay (0, parport);
546   for (temp = 0; temp < 1000; temp++)
547     {
548       cyan_write_copier (0, parport);
549       cyan_delay (3, parport);
550       cyan_write_copier (CNLO + CNHI, parport);
551       cyan_delay (3, parport);
552     }
553   cyan_reset (parport);
554   cyan_reset (parport);
555 
556   fputs ("Parallel port output test: ", stdout);
557   fflush (stdout);
558   cyan_write_copier (255, parport);
559   cyan_delay (0, parport);
560   temp = (cyan_verify_copier (parport) != 255);
561   cyan_write_copier (0, parport);
562   cyan_delay (0, parport);
563   temp |= (cyan_verify_copier (parport) != 0);
564   cyan_write_copier (CNLO, parport);
565   cyan_delay (0, parport);
566   temp |= (cyan_verify_copier (parport) != CNLO);
567   cyan_write_copier (CNHI, parport);
568   cyan_delay (0, parport);
569   temp |= (cyan_verify_copier (parport) != CNHI);
570   cyan_write_copier (RSLO, parport);
571   cyan_delay (0, parport);
572   temp |= (cyan_verify_copier (parport) != RSLO);
573   cyan_write_copier (RSHI, parport);
574   cyan_delay (0, parport);
575   temp |= (cyan_verify_copier (parport) != RSHI);
576   cyan_reset (parport);
577 
578   // if it's still okay after that, then try reading the first set of inputs
579   //  with lines high and low
580   if (!temp)
581     {
582       fputs ("Passed\n"
583              "Input crosstalk test: ",
584              stdout);
585       fflush (stdout);
586       temp = cyan_read_copier (parport) & (INLO | INHI);
587       cyan_write_copier (255 - CNLO, parport);
588       cyan_delay (0, parport);
589       temp = (temp != (cyan_read_copier (parport) & (INLO | INHI)));
590       cyan_reset (parport);
591     }
592 
593   if (temp)
594     {
595       puts ("FAILED");
596       fputs ("ERROR: Parallel port error\n"
597              "Possible causes: ROM copier not getting power (check or replace battery)\n"
598              "                 Short circuit or bad connection (on parallel port or board)\n"
599              "                 Cartridge not inserted properly (or not inserted at all)\n"
600              "                 Parallel port not configured correctly\n"
601              "                 Orange, grey or green wire(s) soldered to the wrong locations\n"
602              "                 Chips inserted backwards\n"
603              "NOTE: Don't forget the ROM copier needs to be turned on before starting!\n",
604              stderr);
605       exit (1);
606     }
607   else
608     puts ("Passed");
609 }
610 
611 
612 static int
cyan_test_copier(int test,int speed,unsigned short parport)613 cyan_test_copier (int test, int speed, unsigned short parport)
614 {
615   unsigned char *buffer1, *buffer2 = NULL;
616   int count = 1;
617 
618   fputs (INTRO_TEXT, stdout);
619   parport_print_info ();
620 
621   switch (test)
622     {
623     // reliability test -- NOTE: this test may be required to run for 8 hours or more
624     case 1:
625       printf ("Reliability test mode selected, speed %d\n", speed);
626       cyan_test_parport (parport);
627       puts ("\n"
628             "Entering non-stop reliability test mode (press escape or q to exit, and turn\n"
629             "ROM copier off immediately afterwards)\n"
630             "\n"
631             "Copy process will continue indefinitely until an error is encountered, at\n"
632             "which point the program will terminate.\n"
633             "A large number of passes suggests that the copier is working reliably at the\n"
634             "selected speed\n");
635       printf ("                                                                          P %2d",
636               count);
637       fflush (stdout);
638       if (ucon64.frontend)
639         fputc ('\n', stdout);
640       buffer1 = cyan_read_rom (speed, parport, NULL);
641       if (!buffer1)                             // user abort
642         return 0;
643 
644       // detect if ROM is all 0x00 or 0xff and print an error if so
645       cyan_calculate_rom_size (buffer1, 1);
646 
647       for (;;)
648         {
649           clear_line ();                        // remove last gauge
650           printf ("   Pass %2d OK\n", count);
651           count++;
652 
653           // verify checksum of first pass
654           if (count == 2)                       // check only in first iteration
655             if (cyan_checksum_rom (buffer1))    // verify checksum
656               puts ("\n"
657                     "WARNING: Checksum of ROM does not appear to be correct.\n"
658                     "         This may be normal for this ROM, or it may indicate a bad copy\n");
659 
660           printf ("                                                                          P %2d",
661                   count);
662           fflush (stdout);
663           if (ucon64.frontend)
664             fputc ('\n', stdout);
665           buffer2 = cyan_read_rom (speed, parport, buffer2);
666           if (!buffer2)
667             {
668               free (buffer1);
669               return 0;
670             }
671           if (memcmp (buffer1, buffer2, 4 * MBYTE))
672             {
673               // error
674               printf ("\n"
675                       "\n"
676                       "Error detected on pass number %d\n"
677                       "\n",
678                       count);
679               if (count == 2)
680                 puts ("A failure this early suggests a critical fault, such as a misconfigured or\n"
681                       "incompatible parallel port, extremely poor wiring, or power supply problems --\n"
682                       "you may wish to replace the battery or try another power supply, and use\n"
683                       "shorter cables.\n"
684                       "Try lowering the speed and running this test again, as a too high speed can\n"
685                       "often cause these symptoms.\n"
686                       "Alternatively, it may have been a one-time glitch; re-run the test to be sure.\n"
687                       "When (if?) you find a lower speed which works reliably, use that speed for\n"
688                       "copying ROMs\n");
689               else
690                 puts ("The first couple of passes were successful. This indicates that you have a\n"
691                       "minor intermittent problem; most likely power supply problems, bad wiring, or\n"
692                       "some kind of one-time glitch.\n"
693                       "You may wish to replace the battery or try another power supply, and use\n"
694                       "shorter cables.\n"
695                       "Make sure no electrical appliances turn on or off during the copy.\n"
696                       "Re-run the test to be sure; it is recommended that you use a lower speed\n");
697               free (buffer1);
698               free (buffer2);
699               return 1;
700             }
701         }
702       break;
703     // manual test
704     case 2:
705       cyan_reset (parport);
706       cyan_write_copier (CNHI, parport);
707       cyan_delay (0, parport);
708       cyan_write_copier (0, parport);
709       cyan_delay (0, parport);
710 
711       if (speed != DEFAULT_SPEED)
712         puts ("Ignoring specified speed; test bench mode does not require a speed setting");
713       // print screen
714       puts ("Entering manual test bench mode\n"
715             "\n"
716             "Probe the board and verify that the counters are being clocked, and are\n"
717             "counting correctly. The upper counter should be one count ahead of the lower\n"
718             "counter, with both clocked at the same rate.\n"
719             "Inject logic levels into the multiplexers to verify that the data bits are\n"
720             "being read correctly:\n"
721             "*=high .=low, layout: L H L H L H L H (L=low multiplexer, H=high multiplexer)\n"
722             "NOTE: The signals in question are the chip native signals; D0 below corresponds\n"
723             "to D0 on the multiplexer, NOT D0 on the cartridge port. Likewise with the\n"
724             "address lines. The input lines are in counter order, left to right.\n"
725             "Press escape or q to exit; be sure to turn the ROM copier off immediately after\n"
726             "exiting, to reset the device.\n"
727             "\n"
728             "If the above didn't make any sense to you, press escape or q and turn the ROM\n"
729             "copier off immediately!\n"
730             "This test is designed for advanced users only\n");
731       for (;;)
732         {
733           const char *status[2] = {"* ", ". "};
734 
735           fputc ('\r', stdout);
736 
737           cyan_write_copier (0, parport);
738           cyan_delay (1, parport);
739           count = cyan_read_copier (parport);
740           fputs (status[((count ^ INLO) >> 6) & 1], stdout);
741           fputs (status[((count ^ INHI) >> 4) & 1], stdout);
742 
743           cyan_write_copier (CNLO | CNHI, parport);
744           cyan_delay (1, parport);
745           count = cyan_read_copier (parport);
746           fputs (status[((count ^ INLO) >> 6) & 1], stdout);
747           fputs (status[((count ^ INHI) >> 4) & 1], stdout);
748 
749           cyan_write_copier (0, parport);
750           cyan_delay (1, parport);
751           count = cyan_read_copier (parport);
752           fputs (status[((count ^ INLO) >> 6) & 1], stdout);
753           fputs (status[((count ^ INHI) >> 4) & 1], stdout);
754 
755           cyan_write_copier (CNLO | CNHI, parport);
756           cyan_delay (1, parport);
757           count = cyan_read_copier (parport);
758           fputs (status[((count ^ INLO) >> 6) & 1], stdout);
759           fputs (status[((count ^ INHI) >> 4) & 1], stdout);
760 
761           cyan_write_copier (0, parport);
762           cyan_delay (1, parport);
763           count = cyan_read_copier (parport);
764           fputs (status[((count ^ INLO) >> 6) & 1], stdout);
765           fputs (status[((count ^ INHI) >> 4) & 1], stdout);
766 
767           cyan_write_copier (CNLO | CNHI, parport);
768           cyan_delay (1, parport);
769           count = cyan_read_copier (parport);
770           fputs (status[((count ^ INLO) >> 6) & 1], stdout);
771           fputs (status[((count ^ INHI) >> 4) & 1], stdout);
772 
773           cyan_write_copier (0, parport);
774           cyan_delay (1, parport);
775           count = cyan_read_copier (parport);
776           fputs (status[((count ^ INLO) >> 6) & 1], stdout);
777           fputs (status[((count ^ INHI) >> 4) & 1], stdout);
778 
779           cyan_write_copier (CNLO | CNHI, parport);
780           cyan_delay (1, parport);
781           count = cyan_read_copier (parport);
782           fputs (status[((count ^ INLO) >> 6) & 1], stdout);
783           fputs (status[((count ^ INHI) >> 4) & 1], stdout);
784 
785           cyan_write_copier (0, parport);
786           cyan_delay (1, parport);
787           fflush (stdout);
788 
789           if (check_exit ())
790             {
791               cyan_reset (parport);
792               puts ("\nUser aborted test");
793               return 0;
794             }
795         }
796       break;
797     default: // cmc_test() should only pass a correct speed value
798       fputs ("INTERNAL ERROR: Invalid test number passed to cyan_test_copier()\n", stderr);
799       exit (1);
800     }
801 //  return 0;
802 }
803 
804 
805 static int
cyan_copy_rom(const char * filename,int speed,unsigned short parport)806 cyan_copy_rom (const char *filename, int speed, unsigned short parport)
807 /*
808   Copy a ROM file -- this assumes the filename is valid and the file does not
809   already exist, since it will blindly try to write (overwrite) the filename you
810   give it.
811   If the open failed due to an invalid filename or path, it prints an error.
812   Speed setting should be between 1-4, 3 is default, and this is verified.
813 */
814 {
815   unsigned long romsize;
816   unsigned char *buffer;
817   FILE *f;
818 
819   fputs (INTRO_TEXT, stdout);
820   parport_print_info ();
821 
822   if (!strlen (filename))
823     {
824       fputs ("ERROR: Filename not specified\n"
825              "       You must specify a filename on the commandline, as follows:\n"
826              "       ucon64 " OPTION_LONG_S "xcmc dump.bin\n", stderr);
827       exit (1);
828     }
829 
830   printf ("Speed %d selected\n", speed);
831   cyan_test_parport (parport);
832   printf ("Destination file: %s\n", filename);
833 
834   if ((f = fopen (filename, "wb")) == NULL)
835     {
836       fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
837       exit (1);
838     }
839   fclose (f);
840 
841   puts ("NOTE: Dumping copier's full address space (file will be automatically trimmed\n"
842         "      after dumping)\n"
843         "Press escape or q to abort\n");
844 
845   buffer = cyan_read_rom (speed, parport, NULL);
846   if (!buffer)
847     {
848       remove (filename);
849       exit(0);
850     }
851 
852   fputc ('\n', stdout);
853   romsize = cyan_calculate_rom_size (buffer, 0);
854 
855   fputs ("Writing ROM to disk... ", stdout);
856   fflush (stdout);
857   if ((f = fopen (filename, "wb")) == NULL)
858     {
859       puts ("FAILED");
860       fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
861       free (buffer);
862       exit (1);
863     }
864   if (fwrite (buffer, 1, romsize, f) != romsize)
865     {
866       puts ("FAILED");
867       fprintf (stderr, ucon64_msg[WRITE_ERROR], filename);
868       free (buffer);
869       fclose (f);
870       exit (1);
871     }
872   fclose (f);
873   printf ("%d kBytes OK\n"
874           "Verifying checksum... ", (int) (romsize / 1024));
875   fflush (stdout);
876 
877   if (cyan_checksum_rom (buffer))
878     {
879       puts ("FAILED\n"
880             "WARNING: Checksum of ROM does not appear to be correct.\n"
881             "         This may be normal for this ROM, or it may indicate a bad copy.\n"
882             "         Please verify the ROM, and consider running a copier test");
883     }
884   else
885     puts ("OK");
886 
887   puts ("Copy complete!\n"
888         "Don't forget to turn the ROM copier off, and never insert or remove a\n"
889         "cartridge with the power on");
890 
891   free (buffer);
892   return 0;
893 
894 }
895 
896 #endif // USE_PARALLEL
897 
898 
899 /*******************
900  * uCON64 wrapping *
901  *******************/
902 
903 #ifdef  USE_PARALLEL
904 static st_ucon64_obj_t cmc_obj[] =
905   {
906     {UCON64_GEN, WF_STOP | WF_NO_ROM},
907     {UCON64_GEN, WF_SWITCH}
908   };
909 #endif
910 
911 const st_getopt2_t cmc_usage[] =
912   {
913     {
914       NULL, 0, 0, 0,
915       NULL, "Cyan's Megadrive ROM copier"/*"1999-2004 Cyan Helkaraxe"*/,
916       NULL
917     },
918 #ifdef  USE_PARALLEL
919     {
920       "xcmc", 0, 0, UCON64_XCMC,
921       NULL, "receive ROM from Cyan's Megadrive ROM copier; " OPTION_LONG_S "port" OPTARG_S "PORT",
922       &cmc_obj[0]
923     },
924     {
925       "xcmct", 1, 0, UCON64_XCMCT,
926       "TEST", "run test TEST\n"
927       "TEST" OPTARG_S "1 burn-in reliability test (specify speed)\n"
928       "TEST" OPTARG_S "2 testbench mode (experts only)",
929       &cmc_obj[0]
930     },
931     {
932       "xcmcm", 1, 0, UCON64_XCMCM,
933       "SPEED", "specify transfer speed\n"
934       "SPEED" OPTARG_S "1 slow (debug)\n"
935       "SPEED" OPTARG_S "2 medium\n"
936       "SPEED" OPTARG_S "3 fast (default)\n"     // verify with value of DEFAULT_SPEED
937       "SPEED" OPTARG_S "4 full speed (risky)",
938       &cmc_obj[1]
939     },
940 #endif // USE_PARALLEL
941     {NULL, 0, 0, 0, NULL, NULL, NULL}
942   };
943 
944 
945 #ifdef  USE_PARALLEL
946 
947 int
cmc_read_rom(const char * filename,unsigned short parport,int speed)948 cmc_read_rom (const char *filename, unsigned short parport, int speed)
949 {
950 #if     (defined __unix__ || defined __BEOS__) && !defined __MSDOS__
951   init_conio ();
952   if (register_func (deinit_conio) == -1)
953     {
954       fputs ("ERROR: Could not register function with register_func()\n", stderr);
955       exit (1);
956     }
957 #endif
958 
959   if (speed < 1 || speed > 4)
960     speed = DEFAULT_SPEED;
961   cyan_copy_rom (filename, speed, parport);
962 
963   return 0;
964 }
965 
966 
967 int
cmc_test(int test,unsigned short parport,int speed)968 cmc_test (int test, unsigned short parport, int speed)
969 {
970 #if     (defined __unix__ || defined __BEOS__) && !defined __MSDOS__
971   init_conio ();
972   if (register_func (deinit_conio) == -1)
973     {
974       fputs ("ERROR: Could not register function with register_func()\n", stderr);
975       exit (1);
976     }
977 #endif
978 
979   if (test < 1 || test > 2)
980     {
981       fputs ("ERROR: Choose a test between 1 and 2 (inclusive)\n", stderr);
982       exit (1);
983     }
984   if (speed < 1 || speed > 4)
985     speed = DEFAULT_SPEED;
986   cyan_test_copier (test, speed, parport);
987 
988   return 0;
989 }
990 
991 #endif // USE_PARALLEL
992