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