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