megaed-sv: implement ROM load/run
[megadrive.git] / megaed-sv / main.c
CommitLineData
ab6ed3c4 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"
51f3a685 10#include "asmtools.h"
ab6ed3c4 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
df43aeea 27#define read8(a) \
28 *((volatile u8 *) (a))
29#define read16(a) \
30 *((volatile u16 *) (a))
31#define read32(a) \
32 *((volatile u32 *) (a))
ab6ed3c4 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
43enum {
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
59static 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
84static int printf_ypos;
85
86static void printf_line(int x, const char *buf)
87{
88 u32 addr;
89 int i;
90
ab6ed3c4 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
51f3a685 107#define PRINTF_LEN 40
108
ab6ed3c4 109static noinline int printf(const char *fmt, ...)
110{
51f3a685 111 static const char hexchars[] = "0123456789abcdef";
ab6ed3c4 112 static int printf_xpos;
51f3a685 113 char c, buf[PRINTF_LEN + 11 + 1];
114 const char *s;
ab6ed3c4 115 va_list ap;
116 int ival;
51f3a685 117 u32 uval;
ab6ed3c4 118 int d = 0;
51f3a685 119 int i, j;
ab6ed3c4 120
121 va_start(ap, fmt);
122 for (d = 0; *fmt; ) {
51f3a685 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') {
ab6ed3c4 132 buf[d] = 0;
51f3a685 133 printf_line(printf_xpos, buf);
ab6ed3c4 134 d = 0;
135 printf_xpos = 0;
136 continue;
137 }
138 d++;
139 continue;
140 }
51f3a685 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 }
ab6ed3c4 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;
51f3a685 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;
ab6ed3c4 185 case 's':
51f3a685 186 s = va_arg(ap, char *);
187 while (*s && d < PRINTF_LEN)
188 buf[d++] = *s++;
189 break;
ab6ed3c4 190 default:
191 // don't handle, for now
192 d++;
ab6ed3c4 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
210void 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
219void vbl(void)
220{
221}
222
51f3a685 223static 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 */
243static 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
277static 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
df43aeea 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
302static 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
ab6ed3c4 343int main()
344{
345 OsRoutine *ed;
51f3a685 346 u8 buf[16];
df43aeea 347 int len;
51f3a685 348 int i, d, ret;
ab6ed3c4 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
df43aeea 369 printf("version: %02x\n", read8(0xa10001));
370 printf("ED os/fw: %x/%x\n\n", ed->osGetOsVersion(),
371 ed->osGetFirmVersion());
372
ab6ed3c4 373 for (;;) {
51f3a685 374 if (!ed->usbRdReady()) {
ab6ed3c4 375 asm volatile("stop #0x2000");
51f3a685 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;
df43aeea 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
51f3a685 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
428bad_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));
ab6ed3c4 435
51f3a685 436 ed->usbWriteByte('b');
ab6ed3c4 437 }
438
439 return 0;
440}
441
442// vim:ts=4:sw=4:expandtab