megaed-sv: allow to input raw mapper code
[megadrive.git] / megaed-sv / main.c
1 #include <stdlib.h>
2 #include <stdarg.h>
3
4 #define u8      unsigned char
5 #define u16     unsigned short
6 #define u32     unsigned int
7
8 #define noinline __attribute__((noinline))
9 #define _packed __attribute__((packed))
10
11 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
12
13 #include "edos.h"
14 #include "asmtools.h"
15
16 extern u16 start_hvc;
17
18 #define GFX_DATA_PORT    0xC00000
19 #define GFX_CTRL_PORT    0xC00004
20
21 #define TILE_MEM_END     0xB000
22
23 #define FONT_LEN         128
24 #define TILE_FONT_BASE   (TILE_MEM_END / 32  - FONT_LEN)
25
26 /* note: using ED menu's layout here.. */
27 #define WPLANE           (TILE_MEM_END + 0x0000)
28 #define HSCRL            (TILE_MEM_END + 0x0800)
29 #define SLIST            (TILE_MEM_END + 0x0C00)
30 #define APLANE           (TILE_MEM_END + 0x1000)
31 #define BPLANE           (TILE_MEM_END + 0x3000)
32
33 #define read8(a) \
34     *((volatile u8 *) (a))
35 #define read16(a) \
36     *((volatile u16 *) (a))
37 #define read32(a) \
38     *((volatile u32 *) (a))
39 #define write8(a, d) \
40     *((volatile u8 *) (a)) = (d)
41 #define write16(a, d) \
42     *((volatile u16 *) (a)) = (d)
43 #define write32(a, d) \
44     *((volatile u32 *) (a)) = (d)
45
46 #define GFX_WRITE_VRAM_ADDR(adr) \
47     (((0x4000 | ((adr) & 0x3FFF)) << 16) | ((adr) >> 14) | 0x00)
48 #define GFX_WRITE_VSRAM_ADDR(adr) \
49     (((0x4000 | ((adr) & 0x3FFF)) << 16) | ((adr) >> 14) | 0x10)
50
51 enum {
52     VDP_MODE1 = 0x00,
53     VDP_MODE2 = 0x01,
54     VDP_BACKDROP = 0x07,
55     VDP_MODE3 = 0x0b,
56     VDP_MODE4 = 0x0c,
57     VDP_AUTOINC = 0x0f,
58     VDP_SCROLLSZ = 0x10,
59 };
60
61 /* cell counts */
62 #define LEFT_BORDER 1   /* lame TV */
63 #define PLANE_W 64
64 #define PLANE_H 32
65 #define CSCREEN_H 28
66
67 static noinline void VDP_drawTextML(const char *str, u16 plane_base,
68     u16 x, u16 y)
69 {
70     const u8 *src = (const u8 *)str;
71     u16 basetile = 0;
72     int max_len = 40 - LEFT_BORDER;
73     int len;
74     u32 addr;
75
76     x += LEFT_BORDER;
77
78     for (len = 0; str[len] && len < max_len; len++)
79         ;
80     if (len > (PLANE_W - x))
81         len = PLANE_W - x;
82
83     addr = plane_base + ((x + (PLANE_W * y)) << 1);
84     write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(addr));
85
86     while (len-- > 0) {
87         write16(GFX_DATA_PORT,
88             basetile | ((*src++) - 32 + TILE_FONT_BASE));
89     }
90 }
91
92 static int printf_ypos;
93
94 static void printf_line(int x, const char *buf)
95 {
96     u32 addr;
97     int i;
98
99     VDP_drawTextML(buf, APLANE, x, printf_ypos++ & (PLANE_H - 1));
100
101     if (printf_ypos >= CSCREEN_H) {
102         /* clear next line */
103         addr = APLANE;
104         addr += (PLANE_W * (printf_ypos & (PLANE_H - 1))) << 1;
105         write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(addr));
106         for (i = 0; i < 40 / 2; i++)
107             write32(GFX_DATA_PORT, 0);
108
109         /* scroll plane */
110         write32(GFX_CTRL_PORT, GFX_WRITE_VSRAM_ADDR(0));
111         write16(GFX_DATA_PORT, (printf_ypos - CSCREEN_H + 1) * 8);
112     }
113 }
114
115 #define PRINTF_LEN 40
116
117 static noinline int printf(const char *fmt, ...)
118 {
119     static const char hexchars[] = "0123456789abcdef";
120     static int printf_xpos;
121     char c, buf[PRINTF_LEN + 11 + 1];
122     const char *s;
123     va_list ap;
124     int ival;
125     u32 uval;
126     int d = 0;
127     int i, j;
128
129     va_start(ap, fmt);
130     for (d = 0; *fmt; ) {
131         int prefix0 = 0;
132         int fwidth = 0;
133
134         c = *fmt++;
135         if (d < PRINTF_LEN)
136             buf[d] = c;
137
138         if (c != '%') {
139             if (c == '\n') {
140                 buf[d] = 0;
141                 printf_line(printf_xpos, buf);
142                 d = 0;
143                 printf_xpos = 0;
144                 continue;
145             }
146             d++;
147             continue;
148         }
149         if (d >= PRINTF_LEN)
150             continue;
151
152         if (*fmt == '0') {
153             prefix0 = 1;
154             fmt++;
155         }
156
157         while ('1' <= *fmt && *fmt <= '9') {
158             fwidth = fwidth * 10 + *fmt - '0';
159             fmt++;
160         }
161
162         switch (*fmt++) {
163         case '%':
164             d++;
165             break;
166         case 'd':
167         case 'i':
168             ival = va_arg(ap, int);
169             if (ival < 0) {
170                 buf[d++] = '-';
171                 ival = -ival;
172             }
173             for (i = 1000000000; i >= 10; i /= 10)
174                 if (ival >= i)
175                     break;
176             for (; i >= 10; i /= 10) {
177                 buf[d++] = '0' + ival / i;
178                 ival %= i;
179             }
180             buf[d++] = '0' + ival;
181             break;
182         case 'x':
183             uval = va_arg(ap, int);
184             while (fwidth > 1 && uval < (1 << (fwidth - 1) * 4)) {
185                 buf[d++] = prefix0 ? '0' : ' ';
186                 fwidth--;
187             }
188             for (j = 1; j < 8 && uval >= (1 << j * 4); j++)
189                 ;
190             for (j--; j >= 0; j--)
191                 buf[d++] = hexchars[(uval >> j * 4) & 0x0f];
192             break;
193         case 's':
194             s = va_arg(ap, char *);
195             while (*s && d < PRINTF_LEN)
196                 buf[d++] = *s++;
197             break;
198         default:
199             // don't handle, for now
200             d++;
201             va_arg(ap, void *);
202             break;
203         }
204     }
205     buf[d] = 0;
206     va_end(ap);
207
208     if (d != 0) {
209         // line without \n
210         VDP_drawTextML(buf, APLANE, printf_xpos,
211             printf_ypos & (PLANE_H - 1));
212         printf_xpos += d;
213     }
214
215     return d; // wrong..
216 }
217
218 static u8 gethex(char c)
219 {
220     if ('0' <= c && c <= '9')
221         return c - '0';
222     if ('a' <= c && c <= 'f')
223         return c - 'a' + 10;
224     if ('A' <= c && c <= 'F')
225         return c - 'A' + 10;
226     return 0;
227 }
228
229 static const char *exc_names[] = {
230     NULL,
231     NULL,
232     "Bus Error",
233     "Address Error",
234     "Illegal Instruction",
235     "Zero Divide",
236     "CHK Instruction",
237     "TRAPV Instruction",
238     "Privilege Violation",  /*  8  8 */
239     "Trace",
240     "Line 1010 Emulator",
241     "Line 1111 Emulator",
242     NULL,
243     NULL,
244     NULL,
245     "Uninitialized Interrupt",
246     NULL,                   /* 10 16 */
247     NULL,
248     NULL,
249     NULL,
250     NULL,
251     NULL,
252     NULL,
253     NULL,
254     "Spurious Interrupt",   /* 18 24 */
255     "l1 irq",
256     "l2 irq",
257     "l3 irq",
258     "l4 irq",
259     "l5 irq",
260     "l6 irq",
261     "l7 irq",
262 };
263
264 struct exc_frame {
265     u32 dr[8];
266     u32 ar[8];
267     u16 ecxnum; // from handler
268     union {
269         struct {
270             u16 sr;
271             u32 pc;
272         } g _packed;
273         struct {
274             u16 fc;
275             u32 addr;
276             u16 ir;
277             u16 sr;
278             u32 pc;
279         } bae _packed; // bus/address error frame
280     };
281 } _packed;
282
283 int xtttt(void) { return sizeof(struct exc_frame); }
284
285 void exception(const struct exc_frame *f)
286 {
287     int i;
288
289     while (read16(GFX_CTRL_PORT) & 2)
290         ;
291     write16(GFX_CTRL_PORT, 0x8000 | (VDP_MODE1 << 8) | 0x04);
292     write16(GFX_CTRL_PORT, 0x8000 | (VDP_MODE2 << 8) | 0x44);
293     /* adjust scroll */
294     write32(GFX_CTRL_PORT, GFX_WRITE_VSRAM_ADDR(0));
295     write16(GFX_DATA_PORT,
296       printf_ypos >= CSCREEN_H ?
297         (printf_ypos - CSCREEN_H + 1) * 8 : 0);
298
299     printf("exception %i ", f->ecxnum);
300     if (f->ecxnum < ARRAY_SIZE(exc_names) && exc_names[f->ecxnum] != NULL)
301         printf("(%s)", exc_names[f->ecxnum]);
302     if (f->ecxnum < 4)
303         printf(" (%s)", (f->bae.fc & 0x10) ? "r" : "w");
304     printf("    \n");
305
306     if (f->ecxnum < 4) {
307         printf("  PC: %08x SR: %04x    \n", f->bae.pc, f->bae.sr);
308         printf("addr: %08x IR: %04x FC: %02x   \n",
309                f->bae.addr, f->bae.ir, f->bae.fc);
310     }
311     else {
312         printf("  PC: %08x SR: %04x    \n", f->g.pc, f->g.sr);
313     }
314     for (i = 0; i < 8; i++)
315         printf("  D%d: %08x A%d: %08x    \n", i, f->dr[i], i, f->ar[i]);
316     printf("                       \n");
317 }
318
319 void vbl(void)
320 {
321 }
322
323 static int usb_read_while_ready(OsRoutine *ed,
324     void *buf_, unsigned int maxlen)
325 {
326     u8 *buf = buf_;
327     unsigned int r = 0;
328
329     while (ed->usbRdReady() && r < maxlen)
330         buf[r++] = ed->usbReadByte();
331
332     return r;
333 }
334
335 static int usb_read(OsRoutine *ed, void *buf_, unsigned int maxlen)
336 {
337     u8 *buf = buf_;
338     unsigned int r = 0;
339
340     while (r < maxlen)
341         buf[r++] = ed->usbReadByte();
342
343     return r;
344 }
345
346 static int usb_write(OsRoutine *ed, const void *buf_, unsigned int maxlen)
347 {
348     const u8 *buf = buf_;
349     unsigned int r = 0;
350
351     while (r < maxlen)
352         ed->usbWriteByte(buf[r++]);
353
354     return r;
355 }
356
357 /*
358  * TH = 1 : ?1CBRLDU    3-button pad return value (not read)
359  * TH = 0 : ?0SA00DU    3-button pad return value
360  * TH = 1 : ?1CBRLDU    3-button pad return value
361  * TH = 0 : ?0SA0000    D3-0 are forced to '0'
362  * TH = 1 : ?1CBMXYZ    Extra buttons returned in D3-0
363  * TH = 0 : ?0SA1111    D3-0 are forced to '1'
364  */
365 static void test_joy_latency(int *min_out, int *max_out)
366 {
367     u8 rbuf[8 * 6];
368     int min = 8;
369     int max = 0;
370     int i, v, b, e;
371
372     for (i = 0; i < 64; i++) {
373         read_joy_responses(rbuf);
374
375         for (b = 0; b < 8 * 4; b++) {
376             v = b & 7;
377             e = (b & 0x08) ? 0x0c : 0;
378             if ((rbuf[b] & 0x0c) == e) {
379                 if (v < min)
380                     min = v;
381             }
382             else if (v > max)
383                 max = v;
384         }
385     }
386
387     /* print out the last test */
388     for (b = 0; b < 8 * 5; b++) {
389         printf(" %02x", rbuf[b]);
390         if ((b & 7) == 7)
391             printf("\n");
392     }
393     printf("\n");
394
395     *min_out = min;
396     *max_out = max;
397 }
398
399 static int do_test(OsRoutine *ed, u8 b3)
400 {
401     int min = 0, max = 0;
402     int i, t, len, seed;
403     u8 *p, v;
404
405     switch (b3)
406     {
407     case '0':
408         printf("reading..\n");
409         test_joy_read_log((void *)0x200000, 0x20000, 1);
410         //test_joy_read_log((void *)0xff0200, 0x0f000, 1);
411         printf("done\n");
412         return 0;
413     case '1':
414         printf("reading w/vsync..\n");
415         test_joy_read_log_vsync((void *)0x200000, 3600 * 2);
416         printf("done\n");
417         return 0;
418     case '2':
419     case '3':
420         printf("3btn_idle test..\n");
421         p = (void *)0x200000;
422         len = 0x20000;
423         test_joy_read_log(p, len, b3 == '3');
424         for (i = 0; i < len; i++) {
425             static const u8 e[2] = { 0x33, 0x7f };
426             v = e[i & 1];
427             if (p[i] != v)
428                 printf("%06x: bad: %02x %02x\n", &p[i], p[i], v);
429         }
430         printf("done\n");
431         return 0;
432     case '4':
433         printf("3btn_idle data test..\n");
434         p = (void *)0x200000;
435         len = 0x20000;
436         for (i = 0; i < len; i++) {
437             static const u8 e[2] = { 0x33, 0x7f };
438             v = e[i & 1];
439             if (p[i] != v)
440                 printf("%06x: bad: %02x %02x\n", &p[i], p[i], v);
441         }
442         printf("done\n");
443         return 0;
444     case '5':
445         seed = read8(0xC00009);
446         printf("testing, seed=%02x\n", seed);
447         p = (void *)0x200000;
448         len = 0x100000;
449         test_byte_write(p, len, seed);
450         for (t = 0; t < 2; t++) {
451             for (i = 0; i < len; i++) {
452                 v = (u8)(i + seed);
453                 if (p[i] != v)
454                     printf("%06x: bad: %02x %02x\n", &p[i], p[i], v);
455             }
456             printf("done (%d)\n", t);
457         }
458         return 0;
459     case 'j':
460         test_joy_latency(&min, &max);
461         printf("latency: %d - %d\n\n", min, max);
462         return 0;
463     default:
464         break;
465     }
466
467     return -1;
468 }
469
470 static int do_custom(OsRoutine *ed, u8 b3)
471 {
472     struct {
473         unsigned int addr;
474         unsigned int size;
475     } d;
476
477     switch (b3)
478     {
479     case 'd':
480         usb_read(ed, &d, sizeof(d));
481         ed->usbWriteByte('k');
482         printf("sending %i bytes from %06x..\n", d.size, d.addr);
483         usb_write(ed, (void *)d.addr, d.size);
484         printf("done.\n");
485         return 1;
486     default:
487         break;
488     }
489
490     return -1;
491 }
492
493 #define MTYPE_OS 0
494 #define MTYPE_MD 1
495 #define MTYPE_SSF 2
496 #define MTYPE_CD 3
497 #define MTYPE_SMS 4
498 #define MTYPE_10M 5
499 #define MTYPE_32X 6
500
501 static int do_run(OsRoutine *ed, u8 b3, int tas_sync)
502 {
503     u8 mapper = 0;
504
505     switch (b3)
506     {
507     case 's':
508         mapper = MTYPE_SMS | (7 << 4);
509         break;
510     case 'm':
511         mapper = MTYPE_MD;
512         break;
513     case 'o':
514         mapper = MTYPE_OS;
515         break;
516     case 'c':
517         mapper = MTYPE_CD;
518         break;
519     case '3':
520         mapper = MTYPE_32X;
521         break;
522     case 'M':
523         mapper = MTYPE_10M;
524         break;
525     case 'n':
526         // raw numer: hex XX: mtype | x;
527         // x: bits [4-7]: SRAM_ON, SRAM_3M_ON, SNAP_SAVE_ON, MKEY
528         mapper  = gethex(ed->usbReadByte()) << 4;
529         mapper |= gethex(ed->usbReadByte());
530         break;
531     default:
532         return -1;
533     }
534
535     printf("syncing and starting mapper %x..\n", mapper);
536
537     while (read16(GFX_CTRL_PORT) & 2)
538         ;
539     ed->VDP_setReg(VDP_MODE1, 0x04); 
540     ed->VDP_setReg(VDP_MODE2, 0x44); 
541
542     ed->usbWriteByte('k');
543
544     run_game(mapper, tas_sync);
545     /* should not get here.. */
546
547     return -1;
548 }
549
550 void setup_z80(void)
551 {
552     u8 *mem = (u8 *)0xa00000;
553     int i;
554
555     write8(0xa11100, 1);
556     write8(0xa11200, 1);
557
558     while (read8(0xa11100) & 1)
559         ;
560
561     /* must use byte access */
562     for (i = 0x2000; i > 0; i--)
563         *mem++ = 0;
564
565     /* console starts with reset on, busreq off,
566      * gens starts with busreq on, keep that for gmv.. */
567 }
568
569 int main()
570 {
571     OsRoutine *ed;
572     u8 buf[16];
573     int len;
574     int i, d, ret;
575
576     ed = (OsRoutine *) *(u32 *)0x1A0;
577     ed->memInitDmaCode(); 
578
579     /* setup VDP */
580     while (read16(GFX_CTRL_PORT) & 2)
581         ;
582
583     ed->VDP_setReg(VDP_MODE1, 0x04); 
584     ed->VDP_setReg(VDP_MODE2, 0x64); 
585     ed->VDP_setReg(VDP_AUTOINC, 2); 
586     ed->VDP_setReg(VDP_SCROLLSZ, 0x01); 
587
588     /* clear name tables */
589     write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(APLANE));
590     for (i = 0; i < PLANE_W * PLANE_H / 2; i++)
591         write32(GFX_DATA_PORT, 0);
592
593     write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(BPLANE));
594     for (i = 0; i < PLANE_W * PLANE_H / 2; i++)
595         write32(GFX_DATA_PORT, 0);
596
597     /* scroll planes */
598     write32(GFX_CTRL_PORT, GFX_WRITE_VSRAM_ADDR(0));
599     write32(GFX_DATA_PORT, 0);
600
601     /* note: relying on ED menu's font setup here.. */
602
603     printf("\n");
604     printf("version: %02x, hvc: %04x %04x\n",
605            read8(0xa10001), start_hvc, read16(0xc00008));
606     printf("ED os/fw: %d/%d\n\n", ed->osGetOsVersion(),
607            ed->osGetFirmVersion());
608
609     setup_z80();
610
611     for (;;) {
612         if (!ed->usbRdReady()) {
613             /* note: stop corrupts SDRAM */
614             //asm volatile("stop #0x2000");
615             asm volatile(
616                 "move.l #1000/10, %0\n"
617                 "0: dbra %0, 0b\n" : "=r" (i) :: "cc");
618             continue;
619         }
620
621         buf[0] = ed->usbReadByte();
622         if (buf[0] == ' ')
623             continue;
624         if (buf[0] != '*') {
625             d = 1;
626             goto bad_input;
627         }
628
629         /* note: OS uses Twofgsr */
630         buf[1] = ed->usbReadByte();
631         switch (buf[1]) {
632         case 'T':
633             ed->usbWriteByte('k');
634             break;
635         case 'g':
636             len = ed->usbReadByte() * 128;
637             printf("loading %d bytes.. ", len * 512);
638             ed->usbWriteByte('k');
639             ed->usbReadDma((void *)0x200000, len);
640             ed->usbWriteByte('d');
641             printf("done\n");
642             break;
643         case 'r':
644         case 'R':
645             buf[2] = ed->usbReadByte();
646             ret = do_run(ed, buf[2], buf[1] == 'R');
647             if (ret != 0) {
648                 d = 3;
649                 goto bad_input;
650             }
651             printf("run returned??\n");
652             break;
653
654         /* custom */
655         case 't':
656             buf[2] = ed->usbReadByte();
657             ret = do_test(ed, buf[2]);
658             if (ret != 0) {
659                 d = 3;
660                 goto bad_input;
661             }
662             ed->usbWriteByte('k');
663             break;
664         case 'x':
665             buf[2] = ed->usbReadByte();
666             ret = do_custom(ed, buf[2]);
667             if (ret == 1)
668                 break;
669             if (ret != 0) {
670                 d = 3;
671                 goto bad_input;
672             }
673             ed->usbWriteByte('k');
674             break;
675         default:
676             d = 2;
677             goto bad_input;
678         }
679
680         continue;
681
682 bad_input:
683         ret = usb_read_while_ready(ed, buf + d, sizeof(buf) - d);
684         buf[d + ret] = 0;
685         printf("bad cmd: %s\n", buf);
686         /* consume all remaining data */
687         while (ed->usbRdReady())
688             usb_read_while_ready(ed, buf, sizeof(buf));
689
690         ed->usbWriteByte('b');
691     }
692
693     return 0;
694 }
695
696 // vim:ts=4:sw=4:expandtab