megaed-sv: download memory command
[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 'j':
393         test_joy_latency(&min, &max);
394         printf("latency: %d - %d\n\n", min, max);
395         return 0;
396     default:
397         break;
398     }
399
400     return -1;
401 }
402
403 static int do_custom(OsRoutine *ed, u8 b3)
404 {
405     struct {
406         unsigned int addr;
407         unsigned int size;
408     } d;
409
410     switch (b3)
411     {
412     case 'd':
413         usb_read(ed, &d, sizeof(d));
414         ed->usbWriteByte('k');
415         printf("sending %i bytes from %06x..\n", d.size, d.addr);
416         usb_write(ed, (void *)d.addr, d.size);
417         printf("done.\n");
418         return 1;
419     default:
420         break;
421     }
422
423     return -1;
424 }
425
426 #define MTYPE_OS 0
427 #define MTYPE_MD 1
428 #define MTYPE_SSF 2
429 #define MTYPE_CD 3
430 #define MTYPE_SMS 4
431 #define MTYPE_10M 5
432 #define MTYPE_32X 6
433
434 static int do_run(OsRoutine *ed, u8 b3, int tas_sync)
435 {
436     u8 mapper = 0;
437
438     switch (b3)
439     {
440     case 's':
441         mapper = MTYPE_SMS | (7 << 4);
442         break;
443     case 'm':
444         mapper = MTYPE_MD;
445         break;
446     case 'o':
447         mapper = MTYPE_OS;
448         break;
449     case 'c':
450         mapper = MTYPE_CD;
451         break;
452     case '3':
453         mapper = MTYPE_32X;
454         break;
455     case 'M':
456         mapper = MTYPE_10M;
457         break;
458     default:
459         return -1;
460     }
461
462     printf("starting mapper %x..\n", mapper);
463
464     while (read16(GFX_CTRL_PORT) & 2)
465         ;
466     ed->VDP_setReg(VDP_MODE1, 0x04); 
467     ed->VDP_setReg(VDP_MODE2, 0x44); 
468
469     ed->usbWriteByte('k');
470
471     run_game(mapper, tas_sync);
472     /* should not get here.. */
473
474     return -1;
475 }
476
477 int main()
478 {
479     OsRoutine *ed;
480     u8 buf[16];
481     int len;
482     int i, d, ret;
483
484     ed = (OsRoutine *) *(u32 *)0x1A0;
485     ed->memInitDmaCode(); 
486
487     ed->VDP_setReg(VDP_MODE1, 0x04); 
488     ed->VDP_setReg(VDP_MODE2, 0x64); 
489     ed->VDP_setReg(VDP_AUTOINC, 2); 
490     ed->VDP_setReg(VDP_SCROLLSZ, 0x01); 
491
492     /* clear name tables */
493     write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(APLANE));
494     for (i = 0; i < PLANE_W * PLANE_H / 2; i++)
495         write32(GFX_DATA_PORT, 0);
496
497     write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(BPLANE));
498     for (i = 0; i < PLANE_W * PLANE_H / 2; i++)
499         write32(GFX_DATA_PORT, 0);
500
501     /* scroll planes */
502     write32(GFX_CTRL_PORT, GFX_WRITE_VSRAM_ADDR(0));
503     write32(GFX_DATA_PORT, 0);
504
505     /* note: relying on ED menu's font setup here.. */
506
507     printf("\n");
508     printf("version: %02x, start_hvc: %04x\n",
509            read8(0xa10001), start_hvc);
510     printf("ED os/fw: %x/%x\n\n", ed->osGetOsVersion(),
511            ed->osGetFirmVersion());
512
513     for (;;) {
514         if (!ed->usbRdReady()) {
515             asm volatile("stop #0x2000");
516             continue;
517         }
518
519         buf[0] = ed->usbReadByte();
520         if (buf[0] == ' ')
521             continue;
522         if (buf[0] != '*') {
523             d = 1;
524             goto bad_input;
525         }
526
527         /* note: OS uses Twofgsr */
528         buf[1] = ed->usbReadByte();
529         switch (buf[1]) {
530         case 'T':
531             ed->usbWriteByte('k');
532             break;
533         case 'g':
534             len = ed->usbReadByte() * 128;
535             printf("loading %d bytes.. ", len * 512);
536             ed->usbWriteByte('k');
537             ed->usbReadDma((void *)0x200000, len);
538             ed->usbWriteByte('d');
539             printf("done\n");
540             break;
541         case 'r':
542         case 'R':
543             buf[2] = ed->usbReadByte();
544             ret = do_run(ed, buf[2], buf[1] == 'R');
545             if (ret != 0) {
546                 d = 3;
547                 goto bad_input;
548             }
549             printf("run returned??\n");
550             break;
551
552         /* custom */
553         case 't':
554             buf[2] = ed->usbReadByte();
555             ret = do_test(ed, buf[2]);
556             if (ret != 0) {
557                 d = 3;
558                 goto bad_input;
559             }
560             ed->usbWriteByte('k');
561             break;
562         case 'x':
563             buf[2] = ed->usbReadByte();
564             ret = do_custom(ed, buf[2]);
565             if (ret == 1)
566                 break;
567             if (ret != 0) {
568                 d = 3;
569                 goto bad_input;
570             }
571             ed->usbWriteByte('k');
572             break;
573         default:
574             d = 2;
575             goto bad_input;
576         }
577
578         continue;
579
580 bad_input:
581         ret = usb_read_while_ready(ed, buf + d, sizeof(buf) - d);
582         buf[d + ret] = 0;
583         printf("bad cmd: %s\n", buf);
584         /* consume all remaining data */
585         while (ed->usbRdReady())
586             usb_read_while_ready(ed, buf, sizeof(buf));
587
588         ed->usbWriteByte('b');
589     }
590
591     return 0;
592 }
593
594 // vim:ts=4:sw=4:expandtab