d5a65a9ca0fe6b937185fb1dfd5da6da4b071cf7
[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_, int maxlen)
312 {
313     u8 *buf = buf_;
314     int r = 0;
315
316     while (ed->usbRdReady() && r < maxlen)
317         buf[r++] = ed->usbReadByte();
318
319     return r;
320 }
321
322 /*
323  * TH = 1 : ?1CBRLDU    3-button pad return value (not read)
324  * TH = 0 : ?0SA00DU    3-button pad return value
325  * TH = 1 : ?1CBRLDU    3-button pad return value
326  * TH = 0 : ?0SA0000    D3-0 are forced to '0'
327  * TH = 1 : ?1CBMXYZ    Extra buttons returned in D3-0
328  * TH = 0 : ?0SA1111    D3-0 are forced to '1'
329  */
330 static void test_joy_latency(int *min_out, int *max_out)
331 {
332     u8 rbuf[8 * 6];
333     int min = 8;
334     int max = 0;
335     int i, v, b, e;
336
337     for (i = 0; i < 64; i++) {
338         read_joy_responses(rbuf);
339
340         for (b = 0; b < 8 * 4; b++) {
341             v = b & 7;
342             e = (b & 0x08) ? 0x0c : 0;
343             if ((rbuf[b] & 0x0c) == e) {
344                 if (v < min)
345                     min = v;
346             }
347             else if (v > max)
348                 max = v;
349         }
350     }
351
352     /* print out the last test */
353     for (b = 0; b < 8 * 5; b++) {
354         printf(" %02x", rbuf[b]);
355         if ((b & 7) == 7)
356             printf("\n");
357     }
358     printf("\n");
359
360     *min_out = min;
361     *max_out = max;
362 }
363
364 static int do_test(OsRoutine *ed, u8 b3)
365 {
366     int min = 0, max = 0;
367
368     switch (b3)
369     {
370     case 'j':
371         test_joy_latency(&min, &max);
372         printf("latency: %d - %d\n\n", min, max);
373         return 0;
374     default:
375         break;
376     }
377
378     return -1;
379 }
380
381 #define MTYPE_OS 0
382 #define MTYPE_MD 1
383 #define MTYPE_SSF 2
384 #define MTYPE_CD 3
385 #define MTYPE_SMS 4
386 #define MTYPE_10M 5
387 #define MTYPE_32X 6
388
389 static int do_run(OsRoutine *ed, u8 b3, int tas_sync)
390 {
391     u8 mapper = 0;
392
393     switch (b3)
394     {
395     case 's':
396         mapper = MTYPE_SMS | (7 << 4);
397         break;
398     case 'm':
399         mapper = MTYPE_MD;
400         break;
401     case 'o':
402         mapper = MTYPE_OS;
403         break;
404     case 'c':
405         mapper = MTYPE_CD;
406         break;
407     case '3':
408         mapper = MTYPE_32X;
409         break;
410     case 'M':
411         mapper = MTYPE_10M;
412         break;
413     default:
414         return -1;
415     }
416
417     printf("starting mapper %x..\n", mapper);
418
419     while (read16(GFX_CTRL_PORT) & 2)
420         ;
421     ed->VDP_setReg(VDP_MODE1, 0x04); 
422     ed->VDP_setReg(VDP_MODE2, 0x44); 
423
424     ed->usbWriteByte('k');
425
426     run_game(mapper, tas_sync);
427     /* should not get here.. */
428
429     return -1;
430 }
431
432 int main()
433 {
434     OsRoutine *ed;
435     u8 buf[16];
436     int len;
437     int i, d, ret;
438
439     ed = (OsRoutine *) *(u32 *)0x1A0;
440     ed->memInitDmaCode(); 
441
442     ed->VDP_setReg(VDP_MODE1, 0x04); 
443     ed->VDP_setReg(VDP_MODE2, 0x64); 
444     ed->VDP_setReg(VDP_AUTOINC, 2); 
445     ed->VDP_setReg(VDP_SCROLLSZ, 0x01); 
446
447     /* clear name tables */
448     write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(APLANE));
449     for (i = 0; i < PLANE_W * PLANE_H / 2; i++)
450         write32(GFX_DATA_PORT, 0);
451
452     write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(BPLANE));
453     for (i = 0; i < PLANE_W * PLANE_H / 2; i++)
454         write32(GFX_DATA_PORT, 0);
455
456     /* note: relying on ED menu's font setup here.. */
457
458     printf("version: %02x, start_hvc: %04x\n",
459            read8(0xa10001), start_hvc);
460     printf("ED os/fw: %x/%x\n\n", ed->osGetOsVersion(),
461            ed->osGetFirmVersion());
462
463     for (;;) {
464         if (!ed->usbRdReady()) {
465             asm volatile("stop #0x2000");
466             continue;
467         }
468
469         buf[0] = ed->usbReadByte();
470         if (buf[0] == ' ')
471             continue;
472         if (buf[0] != '*') {
473             d = 1;
474             goto bad_input;
475         }
476
477         /* note: OS uses Twofgsr */
478         buf[1] = ed->usbReadByte();
479         switch (buf[1]) {
480         case 'T':
481             ed->usbWriteByte('k');
482             break;
483         case 'g':
484             len = ed->usbReadByte() * 128;
485             printf("loading %d bytes.. ", len * 512);
486             ed->usbWriteByte('k');
487             ed->usbReadDma((void *)0x200000, len);
488             ed->usbWriteByte('d');
489             printf("done\n");
490             break;
491         case 'r':
492         case 'R':
493             buf[2] = ed->usbReadByte();
494             ret = do_run(ed, buf[2], buf[1] == 'R');
495             if (ret != 0) {
496                 d = 3;
497                 goto bad_input;
498             }
499             printf("run returned??\n");
500             break;
501
502         /* custom */
503         case 't':
504             buf[2] = ed->usbReadByte();
505             ret = do_test(ed, buf[2]);
506             if (ret != 0) {
507                 d = 3;
508                 goto bad_input;
509             }
510             ed->usbWriteByte('k');
511             break;
512         default:
513             d = 2;
514             goto bad_input;
515         }
516
517         continue;
518
519 bad_input:
520         ret = usb_read_while_ready(ed, buf + d, sizeof(buf) - d);
521         buf[d + ret] = 0;
522         printf("bad cmd: %s\n", buf);
523         /* consume all remaining data */
524         while (ed->usbRdReady())
525             usb_read_while_ready(ed, buf, sizeof(buf));
526
527         ed->usbWriteByte('b');
528     }
529
530     return 0;
531 }
532
533 // vim:ts=4:sw=4:expandtab