megaed-sv: input latency test
[megadrive.git] / megaed-sv / main.c
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"
10 #include "asmtools.h"
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
27 #define write16(a, d) \
28     *((volatile u16 *) (a)) = (d)
29 #define write32(a, d) \
30     *((volatile u32 *) (a)) = (d)
31
32 #define GFX_WRITE_VRAM_ADDR(adr) \
33     (((0x4000 | ((adr) & 0x3FFF)) << 16) | ((adr) >> 14) | 0x00)
34 #define GFX_WRITE_VSRAM_ADDR(adr) \
35     (((0x4000 | ((adr) & 0x3FFF)) << 16) | ((adr) >> 14) | 0x10)
36
37 enum {
38     VDP_MODE1 = 0x00,
39     VDP_MODE2 = 0x01,
40     VDP_BACKDROP = 0x07,
41     VDP_MODE3 = 0x0b,
42     VDP_MODE4 = 0x0c,
43     VDP_AUTOINC = 0x0f,
44     VDP_SCROLLSZ = 0x10,
45 };
46
47 /* cell counts */
48 #define LEFT_BORDER 1   /* lame TV */
49 #define PLANE_W 64
50 #define PLANE_H 32
51 #define CSCREEN_H 28
52
53 static noinline void VDP_drawTextML(const char *str, u16 plane_base,
54     u16 x, u16 y)
55 {
56     const u8 *src = (const u8 *)str;
57     u16 basetile = 0;
58     int max_len = 40 - LEFT_BORDER;
59     int len;
60     u32 addr;
61
62     x += LEFT_BORDER;
63
64     for (len = 0; str[len] && len < max_len; len++)
65         ;
66     if (len > (PLANE_W - x))
67         len = PLANE_W - x;
68
69     addr = plane_base + ((x + (PLANE_W * y)) << 1);
70     write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(addr));
71
72     while (len-- > 0) {
73         write16(GFX_DATA_PORT,
74             basetile | ((*src++) - 32 + TILE_FONT_BASE));
75     }
76 }
77
78 static int printf_ypos;
79
80 static void printf_line(int x, const char *buf)
81 {
82     u32 addr;
83     int i;
84
85     VDP_drawTextML(buf, APLANE, x, printf_ypos++ & (PLANE_H - 1));
86
87     if (printf_ypos >= CSCREEN_H) {
88         /* clear next line */
89         addr = APLANE;
90         addr += (PLANE_W * (printf_ypos & (PLANE_H - 1))) << 1;
91         write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(addr));
92         for (i = 0; i < 40 / 2; i++)
93             write32(GFX_DATA_PORT, 0);
94
95         /* scroll plane */
96         write32(GFX_CTRL_PORT, GFX_WRITE_VSRAM_ADDR(0));
97         write16(GFX_DATA_PORT, (printf_ypos - 27) * 8);
98     }
99 }
100
101 #define PRINTF_LEN 40
102
103 static noinline int printf(const char *fmt, ...)
104 {
105     static const char hexchars[] = "0123456789abcdef";
106     static int printf_xpos;
107     char c, buf[PRINTF_LEN + 11 + 1];
108     const char *s;
109     va_list ap;
110     int ival;
111     u32 uval;
112     int d = 0;
113     int i, j;
114
115     va_start(ap, fmt);
116     for (d = 0; *fmt; ) {
117         int prefix0 = 0;
118         int fwidth = 0;
119
120         c = *fmt++;
121         if (d < PRINTF_LEN)
122             buf[d] = c;
123
124         if (c != '%') {
125             if (c == '\n') {
126                 buf[d] = 0;
127                 printf_line(printf_xpos, buf);
128                 d = 0;
129                 printf_xpos = 0;
130                 continue;
131             }
132             d++;
133             continue;
134         }
135         if (d >= PRINTF_LEN)
136             continue;
137
138         if (*fmt == '0') {
139             prefix0 = 1;
140             fmt++;
141         }
142
143         while ('1' <= *fmt && *fmt <= '9') {
144             fwidth = fwidth * 10 + *fmt - '0';
145             fmt++;
146         }
147
148         switch (*fmt++) {
149         case '%':
150             d++;
151             break;
152         case 'd':
153         case 'i':
154             ival = va_arg(ap, int);
155             if (ival < 0) {
156                 buf[d++] = '-';
157                 ival = -ival;
158             }
159             for (i = 1000000000; i >= 10; i /= 10)
160                 if (ival >= i)
161                     break;
162             for (; i >= 10; i /= 10) {
163                 buf[d++] = '0' + ival / i;
164                 ival %= i;
165             }
166             buf[d++] = '0' + ival;
167             break;
168         case 'x':
169             uval = va_arg(ap, int);
170             while (fwidth > 0 && uval < (1 << (fwidth - 1) * 4)) {
171                 buf[d++] = prefix0 ? '0' : ' ';
172                 fwidth--;
173             }
174             for (j = 1; j < 8 && uval >= (1 << j * 4); j++)
175                 ;
176             for (j--; j >= 0; j--)
177                 buf[d++] = hexchars[(uval >> j * 4) & 0x0f];
178             break;
179         case 's':
180             s = va_arg(ap, char *);
181             while (*s && d < PRINTF_LEN)
182                 buf[d++] = *s++;
183             break;
184         default:
185             // don't handle, for now
186             d++;
187             va_arg(ap, void *);
188             break;
189         }
190     }
191     buf[d] = 0;
192     va_end(ap);
193
194     if (d != 0) {
195         // line without \n
196         VDP_drawTextML(buf, APLANE, printf_xpos,
197             printf_ypos & (PLANE_H - 1));
198         printf_xpos += d;
199     }
200
201     return d; // wrong..
202 }
203
204 void exception(void)
205 {
206     VDP_drawTextML("============", APLANE, 0, 0);
207     VDP_drawTextML(" exception! ", APLANE, 0, 1);
208     VDP_drawTextML("============", APLANE, 0, 2);
209     while (1)
210         ;
211 }
212
213 void vbl(void)
214 {
215 }
216
217 static int usb_read_while_ready(OsRoutine *ed,
218     void *buf_, int maxlen)
219 {
220     u8 *buf = buf_;
221     int r = 0;
222
223     while (ed->usbRdReady() && r < maxlen)
224         buf[r++] = ed->usbReadByte();
225
226     return r;
227 }
228
229 /*
230  * TH = 1 : ?1CBRLDU    3-button pad return value (not read)
231  * TH = 0 : ?0SA00DU    3-button pad return value
232  * TH = 1 : ?1CBRLDU    3-button pad return value
233  * TH = 0 : ?0SA0000    D3-0 are forced to '0'
234  * TH = 1 : ?1CBMXYZ    Extra buttons returned in D3-0
235  * TH = 0 : ?0SA1111    D3-0 are forced to '1'
236  */
237 static void test_joy_latency(int *min_out, int *max_out)
238 {
239     u8 rbuf[8 * 6];
240     int min = 8;
241     int max = 0;
242     int i, v, b, e;
243
244     for (i = 0; i < 64; i++) {
245         read_joy_responses(rbuf);
246
247         for (b = 0; b < 8 * 4; b++) {
248             v = b & 7;
249             e = (b & 0x08) ? 0x0c : 0;
250             if ((rbuf[b] & 0x0c) == e) {
251                 if (v < min)
252                     min = v;
253             }
254             else if (v > max)
255                 max = v;
256         }
257     }
258
259     /* print out the last test */
260     for (b = 0; b < 8 * 5; b++) {
261         printf(" %02x", rbuf[b]);
262         if ((b & 7) == 7)
263             printf("\n");
264     }
265     printf("\n");
266
267     *min_out = min;
268     *max_out = max;
269 }
270
271 static int do_test(OsRoutine *ed, u8 b3)
272 {
273     int min = 0, max = 0;
274
275     switch (b3)
276     {
277     case 'j':
278         test_joy_latency(&min, &max);
279         printf("latency: %d - %d\n\n", min, max);
280         return 0;
281     default:
282         break;
283     }
284
285     return -1;
286 }
287
288 int main()
289 {
290     OsRoutine *ed;
291     u8 buf[16];
292     int i, d, ret;
293
294     ed = (OsRoutine *) *(u32 *)0x1A0;
295     ed->memInitDmaCode(); 
296
297     ed->VDP_setReg(VDP_MODE1, 0x04); 
298     ed->VDP_setReg(VDP_MODE2, 0x64); 
299     ed->VDP_setReg(VDP_AUTOINC, 2); 
300     ed->VDP_setReg(VDP_SCROLLSZ, 0x01); 
301
302     /* clear name tables */
303     write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(APLANE));
304     for (i = 0; i < PLANE_W * PLANE_H / 2; i++)
305         write32(GFX_DATA_PORT, 0);
306
307     write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(BPLANE));
308     for (i = 0; i < PLANE_W * PLANE_H / 2; i++)
309         write32(GFX_DATA_PORT, 0);
310
311     /* note: relying on ED menu's font setup here.. */
312
313     for (;;) {
314         if (!ed->usbRdReady()) {
315             asm volatile("stop #0x2000");
316             continue;
317         }
318
319         buf[0] = ed->usbReadByte();
320         if (buf[0] == ' ')
321             continue;
322         if (buf[0] != '*') {
323             d = 1;
324             goto bad_input;
325         }
326
327         /* note: OS uses Twofgsr */
328         buf[1] = ed->usbReadByte();
329         switch (buf[1]) {
330         case 'T':
331             ed->usbWriteByte('k');
332             break;
333         /* custom */
334         case 't':
335             buf[2] = ed->usbReadByte();
336             ret = do_test(ed, buf[2]);
337             if (ret != 0) {
338                 d = 3;
339                 goto bad_input;
340             }
341             ed->usbWriteByte('k');
342             break;
343         default:
344             d = 2;
345             goto bad_input;
346         }
347
348         continue;
349
350 bad_input:
351         ret = usb_read_while_ready(ed, buf + d, sizeof(buf) - d);
352         buf[d + ret] = 0;
353         printf("bad cmd: %s\n", buf);
354         /* consume all remaining data */
355         while (ed->usbRdReady())
356             usb_read_while_ready(ed, buf, sizeof(buf));
357
358         ed->usbWriteByte('b');
359     }
360
361     return 0;
362 }
363
364 // vim:ts=4:sw=4:expandtab