megaed-sv: first somewhat working sync code
[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
65d9165c 16extern u16 start_hvc;
17
ab6ed3c4 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.. */
65d9165c 27#define WPLANE (TILE_MEM_END + 0x0000)
ab6ed3c4 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
df43aeea 33#define read8(a) \
34 *((volatile u8 *) (a))
35#define read16(a) \
36 *((volatile u16 *) (a))
37#define read32(a) \
38 *((volatile u32 *) (a))
6b0c90a9 39#define write8(a, d) \
40 *((volatile u8 *) (a)) = (d)
ab6ed3c4 41#define write16(a, d) \
42 *((volatile u16 *) (a)) = (d)
43#define write32(a, d) \
44 *((volatile u32 *) (a)) = (d)
45
46#define GFX_WRITE_VRAM_ADDR(adr) \
47 (((0x4000 | ((adr) & 0x3FFF)) << 16) | ((adr) >> 14) | 0x00)
48#define GFX_WRITE_VSRAM_ADDR(adr) \
49 (((0x4000 | ((adr) & 0x3FFF)) << 16) | ((adr) >> 14) | 0x10)
50
51enum {
52 VDP_MODE1 = 0x00,
53 VDP_MODE2 = 0x01,
54 VDP_BACKDROP = 0x07,
55 VDP_MODE3 = 0x0b,
56 VDP_MODE4 = 0x0c,
57 VDP_AUTOINC = 0x0f,
58 VDP_SCROLLSZ = 0x10,
59};
60
61/* cell counts */
62#define LEFT_BORDER 1 /* lame TV */
63#define PLANE_W 64
64#define PLANE_H 32
65#define CSCREEN_H 28
66
67static noinline void VDP_drawTextML(const char *str, u16 plane_base,
68 u16 x, u16 y)
69{
70 const u8 *src = (const u8 *)str;
71 u16 basetile = 0;
72 int max_len = 40 - LEFT_BORDER;
73 int len;
74 u32 addr;
75
76 x += LEFT_BORDER;
77
78 for (len = 0; str[len] && len < max_len; len++)
79 ;
80 if (len > (PLANE_W - x))
81 len = PLANE_W - x;
82
83 addr = plane_base + ((x + (PLANE_W * y)) << 1);
84 write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(addr));
85
86 while (len-- > 0) {
87 write16(GFX_DATA_PORT,
88 basetile | ((*src++) - 32 + TILE_FONT_BASE));
89 }
90}
91
92static int printf_ypos;
93
94static void printf_line(int x, const char *buf)
95{
96 u32 addr;
97 int i;
98
ab6ed3c4 99 VDP_drawTextML(buf, APLANE, x, printf_ypos++ & (PLANE_H - 1));
100
101 if (printf_ypos >= CSCREEN_H) {
102 /* clear next line */
103 addr = APLANE;
104 addr += (PLANE_W * (printf_ypos & (PLANE_H - 1))) << 1;
105 write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(addr));
106 for (i = 0; i < 40 / 2; i++)
107 write32(GFX_DATA_PORT, 0);
108
109 /* scroll plane */
110 write32(GFX_CTRL_PORT, GFX_WRITE_VSRAM_ADDR(0));
39ac9835 111 write16(GFX_DATA_PORT, (printf_ypos - CSCREEN_H + 1) * 8);
ab6ed3c4 112 }
113}
114
51f3a685 115#define PRINTF_LEN 40
116
ab6ed3c4 117static noinline int printf(const char *fmt, ...)
118{
51f3a685 119 static const char hexchars[] = "0123456789abcdef";
ab6ed3c4 120 static int printf_xpos;
51f3a685 121 char c, buf[PRINTF_LEN + 11 + 1];
122 const char *s;
ab6ed3c4 123 va_list ap;
124 int ival;
51f3a685 125 u32 uval;
ab6ed3c4 126 int d = 0;
51f3a685 127 int i, j;
ab6ed3c4 128
129 va_start(ap, fmt);
130 for (d = 0; *fmt; ) {
51f3a685 131 int prefix0 = 0;
132 int fwidth = 0;
133
134 c = *fmt++;
135 if (d < PRINTF_LEN)
136 buf[d] = c;
137
138 if (c != '%') {
139 if (c == '\n') {
ab6ed3c4 140 buf[d] = 0;
51f3a685 141 printf_line(printf_xpos, buf);
ab6ed3c4 142 d = 0;
143 printf_xpos = 0;
144 continue;
145 }
146 d++;
147 continue;
148 }
51f3a685 149 if (d >= PRINTF_LEN)
150 continue;
151
152 if (*fmt == '0') {
153 prefix0 = 1;
154 fmt++;
155 }
156
157 while ('1' <= *fmt && *fmt <= '9') {
158 fwidth = fwidth * 10 + *fmt - '0';
159 fmt++;
160 }
ab6ed3c4 161
162 switch (*fmt++) {
163 case '%':
164 d++;
165 break;
166 case 'd':
167 case 'i':
168 ival = va_arg(ap, int);
169 if (ival < 0) {
170 buf[d++] = '-';
171 ival = -ival;
172 }
173 for (i = 1000000000; i >= 10; i /= 10)
174 if (ival >= i)
175 break;
176 for (; i >= 10; i /= 10) {
177 buf[d++] = '0' + ival / i;
178 ival %= i;
179 }
180 buf[d++] = '0' + ival;
181 break;
51f3a685 182 case 'x':
183 uval = va_arg(ap, int);
39ac9835 184 while (fwidth > 1 && uval < (1 << (fwidth - 1) * 4)) {
51f3a685 185 buf[d++] = prefix0 ? '0' : ' ';
186 fwidth--;
187 }
188 for (j = 1; j < 8 && uval >= (1 << j * 4); j++)
189 ;
190 for (j--; j >= 0; j--)
191 buf[d++] = hexchars[(uval >> j * 4) & 0x0f];
192 break;
ab6ed3c4 193 case 's':
51f3a685 194 s = va_arg(ap, char *);
195 while (*s && d < PRINTF_LEN)
196 buf[d++] = *s++;
197 break;
ab6ed3c4 198 default:
199 // don't handle, for now
200 d++;
ab6ed3c4 201 va_arg(ap, void *);
202 break;
203 }
204 }
205 buf[d] = 0;
206 va_end(ap);
207
208 if (d != 0) {
209 // line without \n
210 VDP_drawTextML(buf, APLANE, printf_xpos,
211 printf_ypos & (PLANE_H - 1));
212 printf_xpos += d;
213 }
214
215 return d; // wrong..
216}
217
39ac9835 218static const char *exc_names[] = {
219 NULL,
220 NULL,
221 "Bus Error",
222 "Address Error",
223 "Illegal Instruction",
224 "Zero Divide",
225 "CHK Instruction",
226 "TRAPV Instruction",
227 "Privilege Violation", /* 8 8 */
228 "Trace",
229 "Line 1010 Emulator",
230 "Line 1111 Emulator",
231 NULL,
232 NULL,
233 NULL,
234 "Uninitialized Interrupt",
235 NULL, /* 10 16 */
236 NULL,
237 NULL,
238 NULL,
239 NULL,
240 NULL,
241 NULL,
242 NULL,
243 "Spurious Interrupt", /* 18 24 */
244 "l1 irq",
245 "l2 irq",
246 "l3 irq",
247 "l4 irq",
248 "l5 irq",
249 "l6 irq",
250 "l7 irq",
251};
252
253struct exc_frame {
254 u32 dr[8];
255 u32 ar[8];
256 u16 ecxnum; // from handler
257 union {
258 struct {
259 u16 sr;
260 u32 pc;
261 } g _packed;
262 struct {
263 u16 fc;
264 u32 addr;
265 u16 ir;
266 u16 sr;
267 u32 pc;
268 } bae _packed; // bus/address error frame
269 };
270} _packed;
271
272int xtttt(void) { return sizeof(struct exc_frame); }
273
274void exception(const struct exc_frame *f)
ab6ed3c4 275{
39ac9835 276 int i;
277
278 while (read16(GFX_CTRL_PORT) & 2)
ab6ed3c4 279 ;
39ac9835 280 write16(GFX_CTRL_PORT, 0x8000 | (VDP_MODE1 << 8) | 0x04);
281 write16(GFX_CTRL_PORT, 0x8000 | (VDP_MODE2 << 8) | 0x44);
282 /* adjust scroll */
283 write32(GFX_CTRL_PORT, GFX_WRITE_VSRAM_ADDR(0));
284 write16(GFX_DATA_PORT,
285 printf_ypos >= CSCREEN_H ?
286 (printf_ypos - CSCREEN_H + 1) * 8 : 0);
287
288 printf("exception %i ", f->ecxnum);
289 if (f->ecxnum < ARRAY_SIZE(exc_names) && exc_names[f->ecxnum] != NULL)
290 printf("(%s)", exc_names[f->ecxnum]);
291 if (f->ecxnum < 4)
292 printf(" (%s)", (f->bae.fc & 0x10) ? "r" : "w");
293 printf(" \n");
294
295 if (f->ecxnum < 4) {
296 printf(" PC: %08x SR: %04x \n", f->bae.pc, f->bae.sr);
297 printf("addr: %08x IR: %04x FC: %02x \n",
298 f->bae.addr, f->bae.ir, f->bae.fc);
299 }
300 else {
301 printf(" PC: %08x SR: %04x \n", f->g.pc, f->g.sr);
302 }
303 for (i = 0; i < 8; i++)
304 printf(" D%d: %08x A%d: %08x \n", i, f->dr[i], i, f->ar[i]);
305 printf(" \n");
ab6ed3c4 306}
307
308void vbl(void)
309{
310}
311
51f3a685 312static int usb_read_while_ready(OsRoutine *ed,
fb63f62c 313 void *buf_, unsigned int maxlen)
51f3a685 314{
315 u8 *buf = buf_;
fb63f62c 316 unsigned int r = 0;
51f3a685 317
318 while (ed->usbRdReady() && r < maxlen)
319 buf[r++] = ed->usbReadByte();
320
321 return r;
322}
323
fb63f62c 324static int usb_read(OsRoutine *ed, void *buf_, unsigned int maxlen)
325{
326 u8 *buf = buf_;
327 unsigned int r = 0;
328
329 while (r < maxlen)
330 buf[r++] = ed->usbReadByte();
331
332 return r;
333}
334
335static int usb_write(OsRoutine *ed, const void *buf_, unsigned int maxlen)
336{
337 const u8 *buf = buf_;
338 unsigned int r = 0;
339
340 while (r < maxlen)
341 ed->usbWriteByte(buf[r++]);
342
343 return r;
344}
345
51f3a685 346/*
347 * TH = 1 : ?1CBRLDU 3-button pad return value (not read)
348 * TH = 0 : ?0SA00DU 3-button pad return value
349 * TH = 1 : ?1CBRLDU 3-button pad return value
350 * TH = 0 : ?0SA0000 D3-0 are forced to '0'
351 * TH = 1 : ?1CBMXYZ Extra buttons returned in D3-0
352 * TH = 0 : ?0SA1111 D3-0 are forced to '1'
353 */
354static void test_joy_latency(int *min_out, int *max_out)
355{
356 u8 rbuf[8 * 6];
357 int min = 8;
358 int max = 0;
359 int i, v, b, e;
360
361 for (i = 0; i < 64; i++) {
362 read_joy_responses(rbuf);
363
364 for (b = 0; b < 8 * 4; b++) {
365 v = b & 7;
366 e = (b & 0x08) ? 0x0c : 0;
367 if ((rbuf[b] & 0x0c) == e) {
368 if (v < min)
369 min = v;
370 }
371 else if (v > max)
372 max = v;
373 }
374 }
375
376 /* print out the last test */
377 for (b = 0; b < 8 * 5; b++) {
378 printf(" %02x", rbuf[b]);
379 if ((b & 7) == 7)
380 printf("\n");
381 }
382 printf("\n");
383
384 *min_out = min;
385 *max_out = max;
386}
387
388static int do_test(OsRoutine *ed, u8 b3)
389{
390 int min = 0, max = 0;
eea25dd1 391 int i, t, len, seed;
392 u8 *p, v;
51f3a685 393
394 switch (b3)
395 {
ad997e79 396 case '0':
397 printf("reading..\n");
eea25dd1 398 test_joy_read_log((void *)0x200000, 0x20000, 1);
399 //test_joy_read_log((void *)0xff0200, 0x0f000, 1);
ad997e79 400 printf("done\n");
401 return 0;
402 case '1':
403 printf("reading w/vsync..\n");
404 test_joy_read_log_vsync((void *)0x200000, 3600 * 2);
405 printf("done\n");
406 return 0;
eea25dd1 407 case '2':
408 case '3':
409 printf("3btn_idle test..\n");
410 p = (void *)0x200000;
411 len = 0x20000;
412 test_joy_read_log(p, len, b3 == '3');
413 for (i = 0; i < len; i++) {
414 static const u8 e[2] = { 0x33, 0x7f };
415 v = e[i & 1];
416 if (p[i] != v)
417 printf("%06x: bad: %02x %02x\n", &p[i], p[i], v);
418 }
419 printf("done\n");
420 return 0;
421 case '4':
422 printf("3btn_idle data test..\n");
423 p = (void *)0x200000;
424 len = 0x20000;
425 for (i = 0; i < len; i++) {
426 static const u8 e[2] = { 0x33, 0x7f };
427 v = e[i & 1];
428 if (p[i] != v)
429 printf("%06x: bad: %02x %02x\n", &p[i], p[i], v);
430 }
431 printf("done\n");
432 return 0;
433 case '5':
434 seed = read8(0xC00009);
435 printf("testing, seed=%02x\n", seed);
436 p = (void *)0x200000;
437 len = 0x100000;
438 test_byte_write(p, len, seed);
439 for (t = 0; t < 2; t++) {
440 for (i = 0; i < len; i++) {
441 v = (u8)(i + seed);
442 if (p[i] != v)
443 printf("%06x: bad: %02x %02x\n", &p[i], p[i], v);
444 }
445 printf("done (%d)\n", t);
446 }
447 return 0;
51f3a685 448 case 'j':
449 test_joy_latency(&min, &max);
450 printf("latency: %d - %d\n\n", min, max);
451 return 0;
452 default:
453 break;
454 }
455
456 return -1;
457}
458
fb63f62c 459static int do_custom(OsRoutine *ed, u8 b3)
460{
461 struct {
462 unsigned int addr;
463 unsigned int size;
464 } d;
465
466 switch (b3)
467 {
468 case 'd':
469 usb_read(ed, &d, sizeof(d));
470 ed->usbWriteByte('k');
471 printf("sending %i bytes from %06x..\n", d.size, d.addr);
472 usb_write(ed, (void *)d.addr, d.size);
473 printf("done.\n");
474 return 1;
475 default:
476 break;
477 }
478
479 return -1;
480}
481
df43aeea 482#define MTYPE_OS 0
483#define MTYPE_MD 1
484#define MTYPE_SSF 2
485#define MTYPE_CD 3
486#define MTYPE_SMS 4
487#define MTYPE_10M 5
488#define MTYPE_32X 6
489
65d9165c 490static int do_run(OsRoutine *ed, u8 b3, int tas_sync)
df43aeea 491{
492 u8 mapper = 0;
493
494 switch (b3)
495 {
496 case 's':
497 mapper = MTYPE_SMS | (7 << 4);
498 break;
499 case 'm':
500 mapper = MTYPE_MD;
501 break;
502 case 'o':
503 mapper = MTYPE_OS;
504 break;
505 case 'c':
506 mapper = MTYPE_CD;
507 break;
508 case '3':
509 mapper = MTYPE_32X;
510 break;
511 case 'M':
512 mapper = MTYPE_10M;
513 break;
514 default:
515 return -1;
516 }
517
47896a18 518 printf("syncing and starting mapper %x..\n", mapper);
65d9165c 519
520 while (read16(GFX_CTRL_PORT) & 2)
df43aeea 521 ;
522 ed->VDP_setReg(VDP_MODE1, 0x04);
523 ed->VDP_setReg(VDP_MODE2, 0x44);
524
525 ed->usbWriteByte('k');
526
65d9165c 527 run_game(mapper, tas_sync);
df43aeea 528 /* should not get here.. */
529
530 return -1;
531}
532
6b0c90a9 533void setup_z80(void)
534{
535 u8 *mem = (u8 *)0xa00000;
536 int i;
537
538 write8(0xa11100, 1);
539 write8(0xa11200, 1);
540
541 while (read8(0xa11100) & 1)
542 ;
543
544 /* must use byte access */
545 for (i = 0x2000; i > 0; i--)
546 *mem++ = 0;
547
548 /* console starts with reset on, busreq off,
549 * gens starts with busreq on, keep that for gmv.. */
550}
551
ab6ed3c4 552int main()
553{
554 OsRoutine *ed;
51f3a685 555 u8 buf[16];
df43aeea 556 int len;
51f3a685 557 int i, d, ret;
ab6ed3c4 558
559 ed = (OsRoutine *) *(u32 *)0x1A0;
560 ed->memInitDmaCode();
561
6b0c90a9 562 /* setup VDP */
563 while (read16(GFX_CTRL_PORT) & 2)
564 ;
565
ab6ed3c4 566 ed->VDP_setReg(VDP_MODE1, 0x04);
567 ed->VDP_setReg(VDP_MODE2, 0x64);
568 ed->VDP_setReg(VDP_AUTOINC, 2);
569 ed->VDP_setReg(VDP_SCROLLSZ, 0x01);
570
571 /* clear name tables */
572 write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(APLANE));
573 for (i = 0; i < PLANE_W * PLANE_H / 2; i++)
574 write32(GFX_DATA_PORT, 0);
575
576 write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(BPLANE));
577 for (i = 0; i < PLANE_W * PLANE_H / 2; i++)
578 write32(GFX_DATA_PORT, 0);
579
fb63f62c 580 /* scroll planes */
581 write32(GFX_CTRL_PORT, GFX_WRITE_VSRAM_ADDR(0));
582 write32(GFX_DATA_PORT, 0);
583
ab6ed3c4 584 /* note: relying on ED menu's font setup here.. */
585
fb63f62c 586 printf("\n");
47896a18 587 printf("version: %02x, hvc: %04x %04x\n",
588 read8(0xa10001), start_hvc, read16(0xc00008));
c6d1079a 589 printf("ED os/fw: %d/%d\n\n", ed->osGetOsVersion(),
df43aeea 590 ed->osGetFirmVersion());
591
6b0c90a9 592 setup_z80();
593
ab6ed3c4 594 for (;;) {
51f3a685 595 if (!ed->usbRdReady()) {
c6d1079a 596 /* note: stop corrupts SDRAM */
597 //asm volatile("stop #0x2000");
598 asm volatile(
599 "move.l #1000/10, %0\n"
600 "0: dbra %0, 0b\n" : "=r" (i) :: "cc");
51f3a685 601 continue;
602 }
603
604 buf[0] = ed->usbReadByte();
605 if (buf[0] == ' ')
606 continue;
607 if (buf[0] != '*') {
608 d = 1;
609 goto bad_input;
610 }
611
612 /* note: OS uses Twofgsr */
613 buf[1] = ed->usbReadByte();
614 switch (buf[1]) {
615 case 'T':
616 ed->usbWriteByte('k');
617 break;
df43aeea 618 case 'g':
619 len = ed->usbReadByte() * 128;
620 printf("loading %d bytes.. ", len * 512);
621 ed->usbWriteByte('k');
622 ed->usbReadDma((void *)0x200000, len);
623 ed->usbWriteByte('d');
624 printf("done\n");
625 break;
626 case 'r':
65d9165c 627 case 'R':
df43aeea 628 buf[2] = ed->usbReadByte();
65d9165c 629 ret = do_run(ed, buf[2], buf[1] == 'R');
df43aeea 630 if (ret != 0) {
631 d = 3;
632 goto bad_input;
633 }
634 printf("run returned??\n");
635 break;
636
51f3a685 637 /* custom */
638 case 't':
639 buf[2] = ed->usbReadByte();
640 ret = do_test(ed, buf[2]);
641 if (ret != 0) {
642 d = 3;
643 goto bad_input;
644 }
645 ed->usbWriteByte('k');
646 break;
fb63f62c 647 case 'x':
648 buf[2] = ed->usbReadByte();
649 ret = do_custom(ed, buf[2]);
650 if (ret == 1)
651 break;
652 if (ret != 0) {
653 d = 3;
654 goto bad_input;
655 }
656 ed->usbWriteByte('k');
657 break;
51f3a685 658 default:
659 d = 2;
660 goto bad_input;
661 }
662
663 continue;
664
665bad_input:
666 ret = usb_read_while_ready(ed, buf + d, sizeof(buf) - d);
667 buf[d + ret] = 0;
668 printf("bad cmd: %s\n", buf);
669 /* consume all remaining data */
670 while (ed->usbRdReady())
671 usb_read_while_ready(ed, buf, sizeof(buf));
ab6ed3c4 672
51f3a685 673 ed->usbWriteByte('b');
ab6ed3c4 674 }
675
676 return 0;
677}
678
679// vim:ts=4:sw=4:expandtab