]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/gba/gba.c
games/gba: new faster ppu code, audio support
[plan9front.git] / sys / src / games / gba / gba.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <draw.h>
5 #include <mouse.h>
6 #include <keyboard.h>
7 #include "dat.h"
8 #include "fns.h"
9
10 int cpuhalt;
11 int scale, profile;
12 Rectangle picr;
13 Image *bg, *tmp;
14 Mousectl *mc;
15 int keys, paused, framestep, backup;
16 QLock pauselock;
17 int savefd, saveframes;
18 int clock;
19
20 char *biosfile = "/sys/games/lib/gbabios.bin";
21
22 void *
23 emalloc(ulong sz)
24 {
25         void *v;
26         
27         v = malloc(sz);
28         if(v == nil)
29                 sysfatal("malloc: %r");
30         setmalloctag(v, getcallerpc(&sz));
31         return v;
32 }
33
34 void
35 writeback(void)
36 {
37         if(saveframes == 0)
38                 saveframes = 15;
39 }
40
41 void
42 flushback(void)
43 {
44         if(savefd >= 0)
45                 pwrite(savefd, back, nback, BACKTYPELEN);
46         saveframes = 0;
47 }
48
49 void
50 loadbios(void)
51 {
52         extern uchar bios[16384];
53
54         int fd;
55         
56         fd = open(biosfile, OREAD);
57         if(fd < 0)
58                 sysfatal("open: %r");
59         readn(fd, bios, 16384);
60         close(fd);
61 }
62
63 int
64 romtype(int *size)
65 {
66         u32int *p, n, v;
67         union {char a[4]; u32int u;} s1 = {"EEPR"}, s2 = {"SRAM"}, s3 = {"FLAS"};
68         
69         p = (u32int *) rom;
70         n = nrom / 4;
71         do{
72                 v = *p++;
73                 if(v == s1.u && memcmp(p - 1, "EEPROM_V", 8) == 0){
74                         print("backup type is either eeprom4 or eeprom64 -- can't detect which one\n");
75                         return NOBACK;
76                 }
77                 if(v == s2.u && memcmp(p - 1, "SRAM_V", 6) == 0){
78                         *size = 32*KB;
79                         return SRAM;
80                 }
81                 if(v == s3.u){
82                         if(memcmp(p - 1, "FLASH_V", 7) == 0 || memcmp(p - 1, "FLASH512_V", 10) == 0){
83                                 *size = 64*KB;
84                                 return FLASH;
85                         }
86                         if(memcmp(p - 1, "FLASH1M_V", 9) == 0){
87                                 *size = 128*KB;
88                                 return FLASH;
89                         }
90                 }
91         }while(--n);
92         return NOBACK;
93 }
94
95 int
96 parsetype(char *s, int *size)
97 {
98         if(strcmp(s, "eeprom4") == 0){
99                 *size = 512;
100                 return EEPROM;
101         }else if(strcmp(s, "eeprom64") == 0){
102                 *size = 8*KB;
103                 return EEPROM;
104         }else if(strcmp(s, "sram256") == 0){
105                 *size = 32*KB;
106                 return SRAM;
107         }else if(strcmp(s, "flash512") == 0){
108                 *size = 64*KB;
109                 return FLASH;
110         }else if(strcmp(s, "flash1024") == 0){
111                 *size = 128*KB;
112                 return FLASH;
113         }else
114                 return NOBACK;
115 }
116
117 void
118 typename(char *s, int type, int size)
119 {
120         char *st;
121         switch(type){
122         case EEPROM:
123                 st = "eeprom";
124                 break;
125         case FLASH:
126                 st = "flash";
127                 break;
128         case SRAM:
129                 st = "sram";
130                 break;
131         default:
132                 sysfatal("typestr: unknown type %d -- shouldn't happen", type);
133                 return;
134         }
135         snprint(s, BACKTYPELEN, "%s%d", st, size/128);
136 }
137
138 void
139 loadsave(char *file)
140 {
141         char *buf, *p;
142         char tstr[BACKTYPELEN];
143         int type, size;
144         
145         buf = emalloc(strlen(file) + 4);
146         strcpy(buf, file);
147         p = strchr(buf, '.');
148         if(p == nil)
149                 p = buf + strlen(buf);
150         strcpy(p, ".sav");
151         savefd = open(buf, ORDWR);
152         if(savefd < 0){
153                 if(backup == NOBACK){
154                         backup = romtype(&nback);
155                         if(backup == NOBACK){
156                                 fprint(2, "failed to autodetect save format\n");
157                                 free(buf);
158                                 return;
159                         }
160                 }
161                 savefd = create(buf, OWRITE, 0664);
162                 if(savefd < 0){
163                         fprint(2, "create: %r");
164                         free(buf);
165                         return;
166                 }
167                 memset(tstr, 0, sizeof(tstr));
168                 typename(tstr, backup, nback);
169                 write(savefd, tstr, sizeof(tstr));
170                 back = emalloc(nback);
171                 memset(back, 0, nback);
172                 write(savefd, back, nback);
173                 free(buf);
174                 atexit(flushback);
175                 return;
176         }
177         readn(savefd, tstr, sizeof(tstr));
178         tstr[31] = 0;
179         type = parsetype(tstr, &size);
180         if(type == NOBACK || backup != NOBACK && (type != backup || nback != size))
181                 sysfatal("%s: invalid format", buf);
182         backup = type;
183         nback = size;
184         back = emalloc(nback);
185         readn(savefd, back, nback);
186         atexit(flushback);
187         free(buf);
188 }
189
190 void
191 loadrom(char *file)
192 {
193         int fd;
194         vlong sz;
195         
196         fd = open(file, OREAD);
197         if(fd < 0)
198                 sysfatal("open: %r");
199         sz = seek(fd, 0, 2);
200         if(sz <= 0 || sz > 32*1024*1024)
201                 sysfatal("invalid file size");
202         seek(fd, 0, 0);
203         nrom = sz;
204         rom = emalloc(nrom);
205         if(readn(fd, rom, sz) < sz)
206                 sysfatal("read: %r");
207         close(fd);
208         loadsave(file);
209         if(nrom == 32*KB*KB && backup == EEPROM)
210                 nrom -= 256;
211 }
212
213 void
214 screeninit(void)
215 {
216         Point p;
217
218         p = divpt(addpt(screen->r.min, screen->r.max), 2);
219         picr = (Rectangle){subpt(p, Pt(scale * 120, scale * 80)), addpt(p, Pt(scale * 120, scale * 80))};
220         tmp = allocimage(display, Rect(0, 0, scale * 240, scale > 1 ? 1 : scale * 160), CHAN4(CIgnore, 1, CBlue, 5, CGreen, 5, CRed, 5), scale > 1, 0);
221         bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
222         draw(screen, screen->r, bg, nil, ZP);   
223 }
224
225 void
226 keyproc(void *)
227 {
228         int fd, k;
229         static char buf[256];
230         char *s;
231         Rune r;
232
233         fd = open("/dev/kbd", OREAD);
234         if(fd < 0)
235                 sysfatal("open: %r");
236         for(;;){
237                 if(read(fd, buf, sizeof(buf) - 1) <= 0)
238                         sysfatal("read /dev/kbd: %r");
239                 if(buf[0] == 'c'){
240                         /*if(utfrune(buf, KF|5))
241                                 savereq = 1;
242                         if(utfrune(buf, KF|6))
243                                 loadreq = 1;*/
244                         if(utfrune(buf, Kdel)){
245                                 close(fd);
246                                 threadexitsall(nil);
247                         }
248                         if(utfrune(buf, 't'))
249                                 trace = !trace;
250                 }
251                 if(buf[0] != 'k' && buf[0] != 'K')
252                         continue;
253                 s = buf + 1;
254                 k = 0;
255                 while(*s != 0){
256                         s += chartorune(&r, s);
257                         switch(r){
258                         case Kdel: close(fd); threadexitsall(nil);
259                         case 'z': k |= 1<<1; break;
260                         case 'x': k |= 1<<0; break;
261                         case 'a': k |= 1<<9; break;
262                         case 's': k |= 1<<8; break;
263                         case Kshift: k |= 1<<2; break;
264                         case 10: k |= 1<<3; break;
265                         case Kup: k |= 1<<6; break;
266                         case Kdown: k |= 1<<7; break;
267                         case Kleft: k |= 1<<5; break;
268                         case Kright: k |= 1<<4; break;
269                         case Kesc:
270                                 if(paused)
271                                         qunlock(&pauselock);
272                                 else
273                                         qlock(&pauselock);
274                                 paused = !paused;
275                                 break;
276                         case KF|1:      
277                                 if(paused){
278                                         qunlock(&pauselock);
279                                         paused=0;
280                                 }
281                                 framestep = !framestep;
282                                 break;
283                         }
284                 }
285                 k &= ~(k << 1 & 0xa0 | k >> 1 & 0x50);
286                 keys = k;
287         }
288
289 }
290
291 void
292 timing(void)
293 {
294         static int fcount;
295         static vlong old;
296         static char buf[32];
297         vlong new;
298         
299         if(++fcount == 60)
300                 fcount = 0;
301         else
302                 return;
303         new = nsec();
304         if(new != old)
305                 sprint(buf, "%6.2f%%", 1e11 / (new - old));
306         else
307                 buf[0] = 0;
308         draw(screen, rectaddpt(Rect(10, 10, 200, 30), screen->r.min), bg, nil, ZP);
309         string(screen, addpt(screen->r.min, Pt(10, 10)), display->black, ZP, display->defaultfont, buf);
310         old = nsec();
311 }
312
313 void
314 flush(void)
315 {
316         extern uchar pic[];
317         Mouse m;
318         int x;
319
320         if(nbrecvul(mc->resizec) > 0){
321                 if(getwindow(display, Refnone) < 0)
322                         sysfatal("resize failed: %r");
323                 screeninit();
324         }
325         while(nbrecv(mc->c, &m) > 0)
326                 ;
327         if(scale == 1){
328                 loadimage(tmp, tmp->r, pic, 240*160*2);
329                 draw(screen, picr, tmp, nil, ZP);
330         } else {
331                 Rectangle r;
332                 uchar *s;
333                 int w;
334
335                 s = pic;
336                 r = picr;
337                 w = 240*2*scale;
338                 while(r.min.y < picr.max.y){
339                         loadimage(tmp, tmp->r, s, w);
340                         s += w;
341                         r.max.y = r.min.y+scale;
342                         draw(screen, r, tmp, nil, ZP);
343                         r.min.y = r.max.y;
344                 }
345         }
346         flushimage(display, 1);
347         if(profile)
348                 timing();
349         audioout();
350         if(framestep){
351                 paused = 1;
352                 qlock(&pauselock);
353                 framestep = 0;
354         }
355         
356         if(saveframes > 0 && --saveframes == 0)
357                 flushback();
358         
359         if((reg[KEYCNT] & 1<<14) != 0){
360                 x = reg[KEYCNT] & keys;
361                 if((reg[KEYCNT] & 1<<15) != 0){
362                         if(x == (reg[KEYCNT] & 0x3ff))
363                                 setif(IRQKEY);
364                 }else
365                         if(x != 0)
366                                 setif(IRQKEY);
367         }
368 }
369
370 void
371 usage(void)
372 {
373         fprint(2, "usage: %s [-23aT] [-s savetype] [-b biosfile] rom\n", argv0);
374         exits("usage");
375 }
376
377 void
378 threadmain(int argc, char **argv)
379 {
380         char *s;
381         int t;
382
383         scale = 1;
384         ARGBEGIN {
385         case '2':
386                 scale = 2;
387                 break;
388         case '3':
389                 scale = 3;
390                 break;
391         case 'a':
392                 audioinit();
393                 break;
394         case 's':
395                 s = EARGF(usage());
396                 backup = parsetype(s, &nback);
397                 if(backup == NOBACK)
398                         sysfatal("unknown save type '%s'", s);
399                 break;
400         case 'b':
401                 biosfile = strdup(EARGF(usage()));
402                 break;
403         case 'T':
404                 profile++;
405                 break;
406         default:
407                 usage();
408         } ARGEND;
409         if(argc < 1)
410                 usage();
411
412         loadbios();
413         loadrom(argv[0]);
414         
415         if(initdraw(nil, nil, nil) < 0)
416                 sysfatal("initdraw: %r");
417         mc = initmouse(nil, screen);
418         if(mc == nil)
419                 sysfatal("initmouse: %r");
420         proccreate(keyproc, nil, mainstacksize);
421         screeninit();
422
423         eventinit();
424         memreset();
425         reset();
426         for(;;){
427                 if(paused){
428                         qlock(&pauselock);
429                         qunlock(&pauselock);
430                 }
431                 if(dmaact)
432                         t = dmastep();
433                 else if(cpuhalt)
434                         t = 8;
435                 else
436                         t = step();
437                 clock += t;
438                 if((elist->time -= t) <= 0)
439                         popevent();
440         }
441 }