sramtest: new test
[megadrive.git] / sramtest / main.c
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