megaed-sv: first somewhat working sync 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 const char *exc_names[] = {
219     NULL,
220     NULL,
221     "Bus Error",
222     "Address Error",
223     "Illegal Instruction",
224     "Zero Divide",
225     "CHK Instruction",
226     "TRAPV Instruction",
227     "Privilege Violation",  /*  8  8 */
228     "Trace",
229     "Line 1010 Emulator",
230     "Line 1111 Emulator",
231     NULL,
232     NULL,
233     NULL,
234     "Uninitialized Interrupt",
235     NULL,                   /* 10 16 */
236     NULL,
237     NULL,
238     NULL,
239     NULL,
240     NULL,
241     NULL,
242     NULL,
243     "Spurious Interrupt",   /* 18 24 */
244     "l1 irq",
245     "l2 irq",
246     "l3 irq",
247     "l4 irq",
248     "l5 irq",
249     "l6 irq",
250     "l7 irq",
251 };
252
253 struct exc_frame {
254     u32 dr[8];
255     u32 ar[8];
256     u16 ecxnum; // from handler
257     union {
258         struct {
259             u16 sr;
260             u32 pc;
261         } g _packed;
262         struct {
263             u16 fc;
264             u32 addr;
265             u16 ir;
266             u16 sr;
267             u32 pc;
268         } bae _packed; // bus/address error frame
269     };
270 } _packed;
271
272 int xtttt(void) { return sizeof(struct exc_frame); }
273
274 void exception(const struct exc_frame *f)
275 {
276     int i;
277
278     while (read16(GFX_CTRL_PORT) & 2)
279         ;
280     write16(GFX_CTRL_PORT, 0x8000 | (VDP_MODE1 << 8) | 0x04);
281     write16(GFX_CTRL_PORT, 0x8000 | (VDP_MODE2 << 8) | 0x44);
282     /* adjust scroll */
283     write32(GFX_CTRL_PORT, GFX_WRITE_VSRAM_ADDR(0));
284     write16(GFX_DATA_PORT,
285       printf_ypos >= CSCREEN_H ?
286         (printf_ypos - CSCREEN_H + 1) * 8 : 0);
287
288     printf("exception %i ", f->ecxnum);
289     if (f->ecxnum < ARRAY_SIZE(exc_names) && exc_names[f->ecxnum] != NULL)
290         printf("(%s)", exc_names[f->ecxnum]);
291     if (f->ecxnum < 4)
292         printf(" (%s)", (f->bae.fc & 0x10) ? "r" : "w");
293     printf("    \n");
294
295     if (f->ecxnum < 4) {
296         printf("  PC: %08x SR: %04x    \n", f->bae.pc, f->bae.sr);
297         printf("addr: %08x IR: %04x FC: %02x   \n",
298                f->bae.addr, f->bae.ir, f->bae.fc);
299     }
300     else {
301         printf("  PC: %08x SR: %04x    \n", f->g.pc, f->g.sr);
302     }
303     for (i = 0; i < 8; i++)
304         printf("  D%d: %08x A%d: %08x    \n", i, f->dr[i], i, f->ar[i]);
305     printf("                       \n");
306 }
307
308 void vbl(void)
309 {
310 }
311
312 static int usb_read_while_ready(OsRoutine *ed,
313     void *buf_, unsigned int maxlen)
314 {
315     u8 *buf = buf_;
316     unsigned int r = 0;
317
318     while (ed->usbRdReady() && r < maxlen)
319         buf[r++] = ed->usbReadByte();
320
321     return r;
322 }
323
324 static int usb_read(OsRoutine *ed, void *buf_, unsigned int maxlen)
325 {
326     u8 *buf = buf_;
327     unsigned int r = 0;
328
329     while (r < maxlen)
330         buf[r++] = ed->usbReadByte();
331
332     return r;
333 }
334
335 static int usb_write(OsRoutine *ed, const void *buf_, unsigned int maxlen)
336 {
337     const u8 *buf = buf_;
338     unsigned int r = 0;
339
340     while (r < maxlen)
341         ed->usbWriteByte(buf[r++]);
342
343     return r;
344 }
345
346 /*
347  * TH = 1 : ?1CBRLDU    3-button pad return value (not read)
348  * TH = 0 : ?0SA00DU    3-button pad return value
349  * TH = 1 : ?1CBRLDU    3-button pad return value
350  * TH = 0 : ?0SA0000    D3-0 are forced to '0'
351  * TH = 1 : ?1CBMXYZ    Extra buttons returned in D3-0
352  * TH = 0 : ?0SA1111    D3-0 are forced to '1'
353  */
354 static void test_joy_latency(int *min_out, int *max_out)
355 {
356     u8 rbuf[8 * 6];
357     int min = 8;
358     int max = 0;
359     int i, v, b, e;
360
361     for (i = 0; i < 64; i++) {
362         read_joy_responses(rbuf);
363
364         for (b = 0; b < 8 * 4; b++) {
365             v = b & 7;
366             e = (b & 0x08) ? 0x0c : 0;
367             if ((rbuf[b] & 0x0c) == e) {
368                 if (v < min)
369                     min = v;
370             }
371             else if (v > max)
372                 max = v;
373         }
374     }
375
376     /* print out the last test */
377     for (b = 0; b < 8 * 5; b++) {
378         printf(" %02x", rbuf[b]);
379         if ((b & 7) == 7)
380             printf("\n");
381     }
382     printf("\n");
383
384     *min_out = min;
385     *max_out = max;
386 }
387
388 static int do_test(OsRoutine *ed, u8 b3)
389 {
390     int min = 0, max = 0;
391     int i, t, len, seed;
392     u8 *p, v;
393
394     switch (b3)
395     {
396     case '0':
397         printf("reading..\n");
398         test_joy_read_log((void *)0x200000, 0x20000, 1);
399         //test_joy_read_log((void *)0xff0200, 0x0f000, 1);
400         printf("done\n");
401         return 0;
402     case '1':
403         printf("reading w/vsync..\n");
404         test_joy_read_log_vsync((void *)0x200000, 3600 * 2);
405         printf("done\n");
406         return 0;
407     case '2':
408     case '3':
409         printf("3btn_idle test..\n");
410         p = (void *)0x200000;
411         len = 0x20000;
412         test_joy_read_log(p, len, b3 == '3');
413         for (i = 0; i < len; i++) {
414             static const u8 e[2] = { 0x33, 0x7f };
415             v = e[i & 1];
416             if (p[i] != v)
417                 printf("%06x: bad: %02x %02x\n", &p[i], p[i], v);
418         }
419         printf("done\n");
420         return 0;
421     case '4':
422         printf("3btn_idle data test..\n");
423         p = (void *)0x200000;
424         len = 0x20000;
425         for (i = 0; i < len; i++) {
426             static const u8 e[2] = { 0x33, 0x7f };
427             v = e[i & 1];
428             if (p[i] != v)
429                 printf("%06x: bad: %02x %02x\n", &p[i], p[i], v);
430         }
431         printf("done\n");
432         return 0;
433     case '5':
434         seed = read8(0xC00009);
435         printf("testing, seed=%02x\n", seed);
436         p = (void *)0x200000;
437         len = 0x100000;
438         test_byte_write(p, len, seed);
439         for (t = 0; t < 2; t++) {
440             for (i = 0; i < len; i++) {
441                 v = (u8)(i + seed);
442                 if (p[i] != v)
443                     printf("%06x: bad: %02x %02x\n", &p[i], p[i], v);
444             }
445             printf("done (%d)\n", t);
446         }
447         return 0;
448     case 'j':
449         test_joy_latency(&min, &max);
450         printf("latency: %d - %d\n\n", min, max);
451         return 0;
452     default:
453         break;
454     }
455
456     return -1;
457 }
458
459 static int do_custom(OsRoutine *ed, u8 b3)
460 {
461     struct {
462         unsigned int addr;
463         unsigned int size;
464     } d;
465
466     switch (b3)
467     {
468     case 'd':
469         usb_read(ed, &d, sizeof(d));
470         ed->usbWriteByte('k');
471         printf("sending %i bytes from %06x..\n", d.size, d.addr);
472         usb_write(ed, (void *)d.addr, d.size);
473         printf("done.\n");
474         return 1;
475     default:
476         break;
477     }
478
479     return -1;
480 }
481
482 #define MTYPE_OS 0
483 #define MTYPE_MD 1
484 #define MTYPE_SSF 2
485 #define MTYPE_CD 3
486 #define MTYPE_SMS 4
487 #define MTYPE_10M 5
488 #define MTYPE_32X 6
489
490 static int do_run(OsRoutine *ed, u8 b3, int tas_sync)
491 {
492     u8 mapper = 0;
493
494     switch (b3)
495     {
496     case 's':
497         mapper = MTYPE_SMS | (7 << 4);
498         break;
499     case 'm':
500         mapper = MTYPE_MD;
501         break;
502     case 'o':
503         mapper = MTYPE_OS;
504         break;
505     case 'c':
506         mapper = MTYPE_CD;
507         break;
508     case '3':
509         mapper = MTYPE_32X;
510         break;
511     case 'M':
512         mapper = MTYPE_10M;
513         break;
514     default:
515         return -1;
516     }
517
518     printf("syncing and starting mapper %x..\n", mapper);
519
520     while (read16(GFX_CTRL_PORT) & 2)
521         ;
522     ed->VDP_setReg(VDP_MODE1, 0x04); 
523     ed->VDP_setReg(VDP_MODE2, 0x44); 
524
525     ed->usbWriteByte('k');
526
527     run_game(mapper, tas_sync);
528     /* should not get here.. */
529
530     return -1;
531 }
532
533 void setup_z80(void)
534 {
535     u8 *mem = (u8 *)0xa00000;
536     int i;
537
538     write8(0xa11100, 1);
539     write8(0xa11200, 1);
540
541     while (read8(0xa11100) & 1)
542         ;
543
544     /* must use byte access */
545     for (i = 0x2000; i > 0; i--)
546         *mem++ = 0;
547
548     /* console starts with reset on, busreq off,
549      * gens starts with busreq on, keep that for gmv.. */
550 }
551
552 int main()
553 {
554     OsRoutine *ed;
555     u8 buf[16];
556     int len;
557     int i, d, ret;
558
559     ed = (OsRoutine *) *(u32 *)0x1A0;
560     ed->memInitDmaCode(); 
561
562     /* setup VDP */
563     while (read16(GFX_CTRL_PORT) & 2)
564         ;
565
566     ed->VDP_setReg(VDP_MODE1, 0x04); 
567     ed->VDP_setReg(VDP_MODE2, 0x64); 
568     ed->VDP_setReg(VDP_AUTOINC, 2); 
569     ed->VDP_setReg(VDP_SCROLLSZ, 0x01); 
570
571     /* clear name tables */
572     write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(APLANE));
573     for (i = 0; i < PLANE_W * PLANE_H / 2; i++)
574         write32(GFX_DATA_PORT, 0);
575
576     write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(BPLANE));
577     for (i = 0; i < PLANE_W * PLANE_H / 2; i++)
578         write32(GFX_DATA_PORT, 0);
579
580     /* scroll planes */
581     write32(GFX_CTRL_PORT, GFX_WRITE_VSRAM_ADDR(0));
582     write32(GFX_DATA_PORT, 0);
583
584     /* note: relying on ED menu's font setup here.. */
585
586     printf("\n");
587     printf("version: %02x, hvc: %04x %04x\n",
588            read8(0xa10001), start_hvc, read16(0xc00008));
589     printf("ED os/fw: %d/%d\n\n", ed->osGetOsVersion(),
590            ed->osGetFirmVersion());
591
592     setup_z80();
593
594     for (;;) {
595         if (!ed->usbRdReady()) {
596             /* note: stop corrupts SDRAM */
597             //asm volatile("stop #0x2000");
598             asm volatile(
599                 "move.l #1000/10, %0\n"
600                 "0: dbra %0, 0b\n" : "=r" (i) :: "cc");
601             continue;
602         }
603
604         buf[0] = ed->usbReadByte();
605         if (buf[0] == ' ')
606             continue;
607         if (buf[0] != '*') {
608             d = 1;
609             goto bad_input;
610         }
611
612         /* note: OS uses Twofgsr */
613         buf[1] = ed->usbReadByte();
614         switch (buf[1]) {
615         case 'T':
616             ed->usbWriteByte('k');
617             break;
618         case 'g':
619             len = ed->usbReadByte() * 128;
620             printf("loading %d bytes.. ", len * 512);
621             ed->usbWriteByte('k');
622             ed->usbReadDma((void *)0x200000, len);
623             ed->usbWriteByte('d');
624             printf("done\n");
625             break;
626         case 'r':
627         case 'R':
628             buf[2] = ed->usbReadByte();
629             ret = do_run(ed, buf[2], buf[1] == 'R');
630             if (ret != 0) {
631                 d = 3;
632                 goto bad_input;
633             }
634             printf("run returned??\n");
635             break;
636
637         /* custom */
638         case 't':
639             buf[2] = ed->usbReadByte();
640             ret = do_test(ed, buf[2]);
641             if (ret != 0) {
642                 d = 3;
643                 goto bad_input;
644             }
645             ed->usbWriteByte('k');
646             break;
647         case 'x':
648             buf[2] = ed->usbReadByte();
649             ret = do_custom(ed, buf[2]);
650             if (ret == 1)
651                 break;
652             if (ret != 0) {
653                 d = 3;
654                 goto bad_input;
655             }
656             ed->usbWriteByte('k');
657             break;
658         default:
659             d = 2;
660             goto bad_input;
661         }
662
663         continue;
664
665 bad_input:
666         ret = usb_read_while_ready(ed, buf + d, sizeof(buf) - d);
667         buf[d + ret] = 0;
668         printf("bad cmd: %s\n", buf);
669         /* consume all remaining data */
670         while (ed->usbRdReady())
671             usb_read_while_ready(ed, buf, sizeof(buf));
672
673         ed->usbWriteByte('b');
674     }
675
676     return 0;
677 }
678
679 // vim:ts=4:sw=4:expandtab