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