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