megaed-sv: add exception handler
[megadrive.git] / megaed-sv / main.c
CommitLineData
39ac9835 1#include <stdlib.h>
ab6ed3c4 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))
39ac9835 9#define _packed __attribute__((packed))
10
11#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
ab6ed3c4 12
13#include "edos.h"
51f3a685 14#include "asmtools.h"
ab6ed3c4 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
df43aeea 31#define read8(a) \
32 *((volatile u8 *) (a))
33#define read16(a) \
34 *((volatile u16 *) (a))
35#define read32(a) \
36 *((volatile u32 *) (a))
ab6ed3c4 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
47enum {
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
63static 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
88static int printf_ypos;
89
90static void printf_line(int x, const char *buf)
91{
92 u32 addr;
93 int i;
94
ab6ed3c4 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));
39ac9835 107 write16(GFX_DATA_PORT, (printf_ypos - CSCREEN_H + 1) * 8);
ab6ed3c4 108 }
109}
110
51f3a685 111#define PRINTF_LEN 40
112
ab6ed3c4 113static noinline int printf(const char *fmt, ...)
114{
51f3a685 115 static const char hexchars[] = "0123456789abcdef";
ab6ed3c4 116 static int printf_xpos;
51f3a685 117 char c, buf[PRINTF_LEN + 11 + 1];
118 const char *s;
ab6ed3c4 119 va_list ap;
120 int ival;
51f3a685 121 u32 uval;
ab6ed3c4 122 int d = 0;
51f3a685 123 int i, j;
ab6ed3c4 124
125 va_start(ap, fmt);
126 for (d = 0; *fmt; ) {
51f3a685 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') {
ab6ed3c4 136 buf[d] = 0;
51f3a685 137 printf_line(printf_xpos, buf);
ab6ed3c4 138 d = 0;
139 printf_xpos = 0;
140 continue;
141 }
142 d++;
143 continue;
144 }
51f3a685 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 }
ab6ed3c4 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;
51f3a685 178 case 'x':
179 uval = va_arg(ap, int);
39ac9835 180 while (fwidth > 1 && uval < (1 << (fwidth - 1) * 4)) {
51f3a685 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;
ab6ed3c4 189 case 's':
51f3a685 190 s = va_arg(ap, char *);
191 while (*s && d < PRINTF_LEN)
192 buf[d++] = *s++;
193 break;
ab6ed3c4 194 default:
195 // don't handle, for now
196 d++;
ab6ed3c4 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
39ac9835 214static 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
249struct 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
268int xtttt(void) { return sizeof(struct exc_frame); }
269
270void exception(const struct exc_frame *f)
ab6ed3c4 271{
39ac9835 272 int i;
273
274 while (read16(GFX_CTRL_PORT) & 2)
ab6ed3c4 275 ;
39ac9835 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");
ab6ed3c4 302}
303
304void vbl(void)
305{
306}
307
51f3a685 308static 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 */
328static 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
362static 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
df43aeea 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
387static 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
ab6ed3c4 428int main()
429{
430 OsRoutine *ed;
51f3a685 431 u8 buf[16];
df43aeea 432 int len;
51f3a685 433 int i, d, ret;
ab6ed3c4 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
df43aeea 454 printf("version: %02x\n", read8(0xa10001));
455 printf("ED os/fw: %x/%x\n\n", ed->osGetOsVersion(),
456 ed->osGetFirmVersion());
457
ab6ed3c4 458 for (;;) {
51f3a685 459 if (!ed->usbRdReady()) {
ab6ed3c4 460 asm volatile("stop #0x2000");
51f3a685 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;
df43aeea 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
51f3a685 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
513bad_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));
ab6ed3c4 520
51f3a685 521 ed->usbWriteByte('b');
ab6ed3c4 522 }
523
524 return 0;
525}
526
527// vim:ts=4:sw=4:expandtab