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