3ee911dc |
1 | /* |
2 | * This software is released into the public domain. |
3 | * See UNLICENSE file in top level directory. |
4 | */ |
5 | #include <stdlib.h> |
6 | #include <stdarg.h> |
7 | |
8 | #define u8 unsigned char |
9 | #define u16 unsigned short |
10 | #define u32 unsigned int |
11 | |
12 | #define noinline __attribute__((noinline)) |
13 | #define unused __attribute__((unused)) |
14 | #define _packed __attribute__((packed)) |
15 | |
16 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) |
17 | |
18 | #include "asmtools.h" |
19 | |
20 | #define GFX_DATA_PORT 0xC00000 |
21 | #define GFX_CTRL_PORT 0xC00004 |
22 | |
23 | #define TILE_MEM_END 0xB000 |
24 | |
25 | #define FONT_LEN 128 |
26 | #define TILE_FONT_BASE (TILE_MEM_END / 32 - FONT_LEN) |
27 | |
28 | /* note: using ED menu's layout here.. */ |
29 | #define WPLANE (TILE_MEM_END + 0x0000) |
30 | #define HSCRL (TILE_MEM_END + 0x0800) |
31 | #define SLIST (TILE_MEM_END + 0x0C00) |
32 | #define APLANE (TILE_MEM_END + 0x1000) |
33 | #define BPLANE (TILE_MEM_END + 0x3000) |
34 | |
35 | #define read8(a) \ |
36 | *((volatile u8 *) (a)) |
37 | #define read16(a) \ |
38 | *((volatile u16 *) (a)) |
39 | #define read32(a) \ |
40 | *((volatile u32 *) (a)) |
41 | #define write8(a, d) \ |
42 | *((volatile u8 *) (a)) = (d) |
43 | #define write16(a, d) \ |
44 | *((volatile u16 *) (a)) = (d) |
45 | #define write32(a, d) \ |
46 | *((volatile u32 *) (a)) = (d) |
47 | |
48 | #define GFX_WRITE_VRAM_ADDR(adr) \ |
49 | (((0x4000 | ((adr) & 0x3FFF)) << 16) | ((adr) >> 14) | 0x00) |
50 | #define GFX_WRITE_VSRAM_ADDR(adr) \ |
51 | (((0x4000 | ((adr) & 0x3FFF)) << 16) | ((adr) >> 14) | 0x10) |
52 | #define GFX_WRITE_CRAM_ADDR(adr) \ |
53 | (((0xC000 | ((adr) & 0x3FFF)) << 16) | ((adr) >> 14) | 0x00) |
54 | |
55 | #define VDP_setReg(r, v) \ |
56 | write16(GFX_CTRL_PORT, 0x8000 | ((r) << 8) | (v)) |
57 | |
58 | enum { |
59 | VDP_MODE1 = 0x00, |
60 | VDP_MODE2 = 0x01, |
61 | VDP_NT_SCROLLA = 0x02, |
62 | VDP_NT_WIN = 0x03, |
63 | VDP_NT_SCROLLB = 0x04, |
64 | VDP_SAT_BASE = 0x05, |
65 | VDP_BACKDROP = 0x07, |
66 | VDP_MODE3 = 0x0b, |
67 | VDP_MODE4 = 0x0c, |
68 | VDP_HSCROLL = 0x0d, |
69 | VDP_AUTOINC = 0x0f, |
70 | VDP_SCROLLSZ = 0x10, |
71 | }; |
72 | |
73 | /* cell counts */ |
74 | #define LEFT_BORDER 1 /* lame TV */ |
75 | #define PLANE_W 64 |
76 | #define PLANE_H 32 |
77 | #define CSCREEN_H 28 |
78 | |
79 | static int text_pal; |
80 | |
81 | static noinline void VDP_drawTextML(const char *str, u16 plane_base, |
82 | u16 x, u16 y) |
83 | { |
84 | const u8 *src = (const u8 *)str; |
85 | u16 basetile = text_pal << 13; |
86 | int max_len = 40 - LEFT_BORDER; |
87 | int len; |
88 | u32 addr; |
89 | |
90 | x += LEFT_BORDER; |
91 | |
92 | for (len = 0; str[len] && len < max_len; len++) |
93 | ; |
94 | if (len > (PLANE_W - x)) |
95 | len = PLANE_W - x; |
96 | |
97 | addr = plane_base + ((x + (PLANE_W * y)) << 1); |
98 | write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(addr)); |
99 | |
100 | while (len-- > 0) { |
101 | write16(GFX_DATA_PORT, |
102 | basetile | ((*src++) - 32 + TILE_FONT_BASE / 32)); |
103 | } |
104 | } |
105 | |
106 | static int printf_ypos; |
107 | |
108 | static void printf_line(int x, const char *buf) |
109 | { |
110 | u32 addr; |
111 | int i; |
112 | |
113 | VDP_drawTextML(buf, APLANE, x, printf_ypos++ & (PLANE_H - 1)); |
114 | |
115 | if (printf_ypos >= CSCREEN_H) { |
116 | /* clear next line */ |
117 | addr = APLANE; |
118 | addr += (PLANE_W * (printf_ypos & (PLANE_H - 1))) << 1; |
119 | write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(addr)); |
120 | for (i = 0; i < 40 / 2; i++) |
121 | write32(GFX_DATA_PORT, 0); |
122 | |
123 | /* scroll plane */ |
124 | write32(GFX_CTRL_PORT, GFX_WRITE_VSRAM_ADDR(0)); |
125 | write16(GFX_DATA_PORT, (printf_ypos - CSCREEN_H + 1) * 8); |
126 | } |
127 | } |
128 | |
129 | #define PRINTF_LEN 40 |
130 | |
131 | static int printf_xpos; |
132 | |
133 | static noinline int printf(const char *fmt, ...) |
134 | { |
135 | static const char hexchars[] = "0123456789abcdef"; |
136 | char c, buf[PRINTF_LEN + 11 + 1]; |
137 | const char *s; |
138 | va_list ap; |
139 | int ival; |
140 | u32 uval; |
141 | int d = 0; |
142 | int i, j; |
143 | |
144 | va_start(ap, fmt); |
145 | for (d = 0; *fmt; ) { |
146 | int prefix0 = 0; |
147 | int fwidth = 0; |
148 | |
149 | c = *fmt++; |
150 | if (d < PRINTF_LEN) |
151 | buf[d] = c; |
152 | |
153 | if (c != '%') { |
154 | if (c == '\n') { |
155 | buf[d] = 0; |
156 | printf_line(printf_xpos, buf); |
157 | d = 0; |
158 | printf_xpos = 0; |
159 | continue; |
160 | } |
161 | d++; |
162 | continue; |
163 | } |
164 | if (d >= PRINTF_LEN) |
165 | continue; |
166 | |
167 | if (*fmt == '0') { |
168 | prefix0 = 1; |
169 | fmt++; |
170 | } |
171 | |
172 | while ('1' <= *fmt && *fmt <= '9') { |
173 | fwidth = fwidth * 10 + *fmt - '0'; |
174 | fmt++; |
175 | } |
176 | |
177 | switch (*fmt++) { |
178 | case '%': |
179 | d++; |
180 | break; |
181 | case 'd': |
182 | case 'i': |
183 | ival = va_arg(ap, int); |
184 | if (ival < 0) { |
185 | buf[d++] = '-'; |
186 | ival = -ival; |
187 | } |
188 | for (i = 1000000000; i >= 10; i /= 10) |
189 | if (ival >= i) |
190 | break; |
191 | for (; i >= 10; i /= 10) { |
192 | buf[d++] = '0' + ival / i; |
193 | ival %= i; |
194 | } |
195 | buf[d++] = '0' + ival; |
196 | break; |
197 | case 'x': |
198 | uval = va_arg(ap, int); |
199 | while (fwidth > 1 && uval < (1 << (fwidth - 1) * 4)) { |
200 | buf[d++] = prefix0 ? '0' : ' '; |
201 | fwidth--; |
202 | } |
203 | for (j = 1; j < 8 && uval >= (1 << j * 4); j++) |
204 | ; |
205 | for (j--; j >= 0; j--) |
206 | buf[d++] = hexchars[(uval >> j * 4) & 0x0f]; |
207 | break; |
208 | case 's': |
209 | s = va_arg(ap, char *); |
210 | while (*s && d < PRINTF_LEN) |
211 | buf[d++] = *s++; |
212 | break; |
213 | default: |
214 | // don't handle, for now |
215 | d++; |
216 | va_arg(ap, void *); |
217 | break; |
218 | } |
219 | } |
220 | buf[d] = 0; |
221 | va_end(ap); |
222 | |
223 | if (d != 0) { |
224 | // line without \n |
225 | VDP_drawTextML(buf, APLANE, printf_xpos, |
226 | printf_ypos & (PLANE_H - 1)); |
227 | printf_xpos += d; |
228 | } |
229 | |
230 | return d; // wrong.. |
231 | } |
232 | |
233 | static const char *exc_names[] = { |
234 | NULL, |
235 | NULL, |
236 | "Bus Error", |
237 | "Address Error", |
238 | "Illegal Instruction", |
239 | "Zero Divide", |
240 | "CHK Instruction", |
241 | "TRAPV Instruction", |
242 | "Privilege Violation", /* 8 8 */ |
243 | "Trace", |
244 | "Line 1010 Emulator", |
245 | "Line 1111 Emulator", |
246 | NULL, |
247 | NULL, |
248 | NULL, |
249 | "Uninitialized Interrupt", |
250 | NULL, /* 10 16 */ |
251 | NULL, |
252 | NULL, |
253 | NULL, |
254 | NULL, |
255 | NULL, |
256 | NULL, |
257 | NULL, |
258 | "Spurious Interrupt", /* 18 24 */ |
259 | "l1 irq", |
260 | "l2 irq", |
261 | "l3 irq", |
262 | "l4 irq", |
263 | "l5 irq", |
264 | "l6 irq", |
265 | "l7 irq", |
266 | }; |
267 | |
268 | struct exc_frame { |
269 | u32 dr[8]; |
270 | u32 ar[8]; |
271 | u16 ecxnum; // from handler |
272 | union { |
273 | struct { |
274 | u16 sr; |
275 | u32 pc; |
276 | } g _packed; |
277 | struct { |
278 | u16 fc; |
279 | u32 addr; |
280 | u16 ir; |
281 | u16 sr; |
282 | u32 pc; |
283 | } bae _packed; // bus/address error frame |
284 | }; |
285 | } _packed; |
286 | |
287 | int xtttt(void) { return sizeof(struct exc_frame); } |
288 | |
289 | void exception(const struct exc_frame *f) |
290 | { |
291 | int i; |
292 | |
293 | while (read16(GFX_CTRL_PORT) & 2) |
294 | ; |
295 | write16(GFX_CTRL_PORT, 0x8000 | (VDP_MODE1 << 8) | 0x04); |
296 | write16(GFX_CTRL_PORT, 0x8000 | (VDP_MODE2 << 8) | 0x44); |
297 | /* adjust scroll */ |
298 | write32(GFX_CTRL_PORT, GFX_WRITE_VSRAM_ADDR(0)); |
299 | write16(GFX_DATA_PORT, |
300 | printf_ypos >= CSCREEN_H ? |
301 | (printf_ypos - CSCREEN_H + 1) * 8 : 0); |
302 | |
303 | printf("exception %i ", f->ecxnum); |
304 | if (f->ecxnum < ARRAY_SIZE(exc_names) && exc_names[f->ecxnum] != NULL) |
305 | printf("(%s)", exc_names[f->ecxnum]); |
306 | if (f->ecxnum < 4) |
307 | printf(" (%s)", (f->bae.fc & 0x10) ? "r" : "w"); |
308 | printf(" \n"); |
309 | |
310 | if (f->ecxnum < 4) { |
311 | printf(" PC: %08x SR: %04x \n", f->bae.pc, f->bae.sr); |
312 | printf("addr: %08x IR: %04x FC: %02x \n", |
313 | f->bae.addr, f->bae.ir, f->bae.fc); |
314 | } |
315 | else { |
316 | printf(" PC: %08x SR: %04x \n", f->g.pc, f->g.sr); |
317 | } |
318 | for (i = 0; i < 8; i++) |
319 | printf(" D%d: %08x A%d: %08x \n", i, f->dr[i], i, f->ar[i]); |
320 | printf(" \n"); |
321 | } |
322 | |
323 | extern u32 font_base[]; |
324 | extern u8 test_data[]; |
325 | extern u8 test_data_end[]; |
326 | |
327 | static void simple_test(int *odd, int *even, int *rom) |
328 | { |
329 | u32 old, new, v0, v1; |
330 | |
331 | *odd = *even = *rom = 0; |
332 | old = read16(0x200000); |
333 | new = old ^ 0xa55a; |
334 | write16(0x200000, new); |
335 | v0 = read16(0x200000); |
336 | write8(0x200000, ~new >> 8); |
337 | write8(0x200001, ~new); |
338 | //write16(0x200000, ~new); |
339 | v1 = read16(0x200000); |
340 | if (((v0 ^ new) & 0xff00) == 0 && ((v1 ^ ~new) & 0xff00) == 0) { |
341 | printf(" even"); |
342 | *even = 1; |
343 | } |
344 | if (((v0 ^ new) & 0x00ff) == 0 && ((v1 ^ ~new) & 0x00ff) == 0) { |
345 | printf(" odd"); |
346 | *odd = 1; |
347 | } |
348 | if (v0 == old && v1 == old) { |
349 | printf(" ROM"); |
350 | *rom = 1; |
351 | } |
352 | else if (!(*odd | *even)) { |
353 | text_pal = 2; |
354 | printf(" bad value"); |
355 | text_pal = 0; |
356 | } |
357 | } |
358 | |
359 | static int detect_size(u8 *a) |
360 | { |
361 | int i, v; |
362 | |
363 | write8(a, 0); |
364 | for (i = 2, v = 1; i < 0x200000; i <<= 1, v++) { |
365 | write8(a + i, v); |
366 | if (read8(a) || read8(a + i) != v) |
367 | break; |
368 | } |
369 | return i > 2 ? i / 2 : 0; |
370 | } |
371 | |
372 | static unused void fill(u8 *d, unsigned int size, u8 val) |
373 | { |
374 | unsigned int i; |
375 | |
376 | for (i = 0; i < size * 2; i += 2) |
377 | d[i] = val; |
378 | } |
379 | |
380 | static int check(u8 *d, unsigned int size, u8 val) |
381 | { |
382 | unsigned int i; |
383 | |
384 | for (i = 0; i < size * 2; i += 2) |
385 | if (d[i] != val) |
386 | break; |
387 | |
388 | if (i == size * 2) |
389 | return 1; |
390 | |
391 | text_pal = 2; |
392 | printf("\nfailed at byte %x, val %02x vs %02x\n", |
393 | i / 2, d[i], val); |
394 | text_pal = 0; |
395 | return 0; |
396 | } |
397 | |
398 | static void do_test(u8 *d, unsigned int size) |
399 | { |
400 | int spos = printf_xpos; |
401 | int i; |
402 | |
403 | for (i = 0; i < 0x100; i++) { |
404 | printf_xpos = spos; |
405 | printf("%02x", i); |
406 | //fill(d, size, i); |
407 | fillpx16(d, size / 16, i); |
408 | if (!checkpx4(d, size / 4, i)) { |
409 | check(d, size, i); // for log |
410 | break; |
411 | } |
412 | } |
413 | } |
414 | |
415 | int main() |
416 | { |
417 | volatile u32 *vptr32; |
418 | int odd, even, rom; |
419 | u32 old; |
420 | int i; |
421 | |
422 | /* z80 */ |
423 | write16(0xa11100, 0x100); |
424 | write16(0xa11200, 0); |
425 | |
426 | /* setup VDP */ |
427 | while (read16(GFX_CTRL_PORT) & 2) |
428 | ; |
429 | |
430 | VDP_setReg(VDP_MODE1, 0x04); |
431 | VDP_setReg(VDP_MODE2, 0x44); |
432 | VDP_setReg(VDP_MODE3, 0x00); |
433 | VDP_setReg(VDP_MODE4, 0x81); |
434 | VDP_setReg(VDP_NT_SCROLLA, APLANE >> 10); |
435 | VDP_setReg(VDP_NT_SCROLLB, BPLANE >> 13); |
436 | VDP_setReg(VDP_SAT_BASE, SLIST >> 9); |
437 | VDP_setReg(VDP_HSCROLL, HSCRL >> 10); |
438 | VDP_setReg(VDP_AUTOINC, 2); |
439 | VDP_setReg(VDP_SCROLLSZ, 0x01); |
440 | VDP_setReg(VDP_BACKDROP, 0); |
441 | |
442 | /* clear name tables */ |
443 | write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(APLANE)); |
444 | for (i = 0; i < PLANE_W * PLANE_H / 2; i++) |
445 | write32(GFX_DATA_PORT, 0); |
446 | |
447 | write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(BPLANE)); |
448 | for (i = 0; i < PLANE_W * PLANE_H / 2; i++) |
449 | write32(GFX_DATA_PORT, 0); |
450 | |
451 | /* SAT, h. scroll */ |
452 | write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(SLIST)); |
453 | write32(GFX_DATA_PORT, 0); |
454 | |
455 | write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(HSCRL)); |
456 | write32(GFX_DATA_PORT, 0); |
457 | |
458 | /* scroll plane vscroll */ |
459 | write32(GFX_CTRL_PORT, GFX_WRITE_VSRAM_ADDR(0)); |
460 | write32(GFX_DATA_PORT, 0); |
461 | printf_xpos = printf_ypos = 0; |
462 | |
463 | /* load font */ |
464 | write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(TILE_FONT_BASE)); |
465 | for (i = 0; i < FONT_LEN * 32 / 4; i++) |
466 | write32(GFX_DATA_PORT, font_base[i]); |
467 | |
468 | /* set colors */ |
469 | write32(GFX_CTRL_PORT, GFX_WRITE_CRAM_ADDR(0)); |
470 | write32(GFX_DATA_PORT, 0); |
471 | write32(GFX_CTRL_PORT, GFX_WRITE_CRAM_ADDR(15 * 2)); // font normal |
472 | write16(GFX_DATA_PORT, 0xeee); |
473 | write32(GFX_CTRL_PORT, GFX_WRITE_CRAM_ADDR(31 * 2)); // green |
474 | write16(GFX_DATA_PORT, 0x0e0); |
475 | write32(GFX_CTRL_PORT, GFX_WRITE_CRAM_ADDR(47 * 2)); // red |
476 | write16(GFX_DATA_PORT, 0x00e); |
477 | text_pal = 0; |
478 | |
479 | printf("\n"); |
480 | printf("MD version: %02x\n", read8(0xa10001)); |
481 | printf("ROM writable? "); |
482 | |
483 | vptr32 = (void *)0x120; |
484 | old = *vptr32; |
485 | *vptr32 ^= ~0; |
486 | printf("%s\n", *vptr32 == old ? "no" : "yes"); |
487 | |
488 | printf("200000 initial state:"); |
489 | simple_test(&odd, &even, &rom); |
490 | |
491 | printf("\nenable with i0: "); |
492 | write_rreg_i0(1); |
493 | simple_test(&odd, &even, &rom); |
494 | |
495 | printf("\ndisable with i0:"); |
496 | write_rreg_i0(0); |
497 | simple_test(&odd, &even, &rom); |
498 | |
499 | printf("\nenable with i1: "); |
500 | write_rreg_i1(1); |
501 | simple_test(&odd, &even, &rom); |
502 | |
503 | printf("\ndisable with i1:"); |
504 | write_rreg_i1(0); |
505 | simple_test(&odd, &even, &rom); |
506 | |
507 | printf("\nenable with 16: "); |
508 | write16(0xa130f0, 1); |
509 | simple_test(&odd, &even, &rom); |
510 | printf("\n"); |
511 | |
512 | if (even) { |
513 | even = detect_size((void *)0x200000); |
514 | printf("detected even size: %d\n", even); |
515 | } |
516 | if (odd) { |
517 | odd = detect_size((void *)0x200001); |
518 | printf("detected odd size: %d\n", odd); |
519 | } |
520 | if (even) { |
521 | printf("testing even: ", even); |
522 | do_test((void *)0x200000, even); |
523 | printf("\n"); |
524 | } |
525 | if (odd) { |
526 | printf("testing odd: ", odd); |
527 | do_test((void *)0x200001, odd); |
528 | printf("\n"); |
529 | } |
530 | |
531 | if (!odd && !even) { |
532 | text_pal = 2; |
533 | printf("no RAM\n"); |
534 | text_pal = 0; |
535 | } |
536 | |
537 | printf("done.\n"); |
538 | |
539 | for (;;) |
540 | ; |
541 | |
542 | return 0; |
543 | } |
544 | |
545 | // vim:ts=4:sw=4:expandtab |