]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/gba/gba.c
games/gba: small fixes
[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
19 char *biosfile = "/sys/games/lib/gbabios.bin";
20
21 int ppuclock;
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("nope.jpg");
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, 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(read(fd, buf, sizeof(buf) - 1) <= 0)
239                         sysfatal("read /dev/kbd: %r");
240                 if(buf[0] == 'c'){
241                         /*if(utfrune(buf, KF|5))
242                                 savereq = 1;
243                         if(utfrune(buf, KF|6))
244                                 loadreq = 1;*/
245                         if(utfrune(buf, Kdel)){
246                                 close(fd);
247                                 threadexitsall(nil);
248                         }
249                         if(utfrune(buf, 't'))
250                                 trace = !trace;
251                 }
252                 if(buf[0] != 'k' && buf[0] != 'K')
253                         continue;
254                 s = buf + 1;
255                 k = 0;
256                 while(*s != 0){
257                         s += chartorune(&r, s);
258                         switch(r){
259                         case Kdel: close(fd); threadexitsall(nil);
260                         case 'z': k |= 1<<1; break;
261                         case 'x': k |= 1<<0; break;
262                         case 'a': k |= 1<<9; break;
263                         case 's': k |= 1<<8; break;
264                         case Kshift: k |= 1<<2; break;
265                         case 10: k |= 1<<3; break;
266                         case Kup: k |= 1<<6; break;
267                         case Kdown: k |= 1<<7; break;
268                         case Kleft: k |= 1<<5; break;
269                         case Kright: k |= 1<<4; break;
270                         case Kesc:
271                                 if(paused)
272                                         qunlock(&pauselock);
273                                 else
274                                         qlock(&pauselock);
275                                 paused = !paused;
276                                 break;
277                         case KF|1:      
278                                 if(paused){
279                                         qunlock(&pauselock);
280                                         paused=0;
281                                 }
282                                 framestep = !framestep;
283                                 break;
284                         }
285                 }
286                 k &= ~(k << 1 & 0xa0 | k >> 1 & 0x50);
287                 keys = k;
288         }
289
290 }
291
292 void
293 timing(void)
294 {
295         static int fcount;
296         static vlong old;
297         static char buf[32];
298         vlong new;
299         
300         if(++fcount == 60)
301                 fcount = 0;
302         else
303                 return;
304         new = nsec();
305         if(new != old)
306                 sprint(buf, "%6.2f%%", 1e11 / (new - old));
307         else
308                 buf[0] = 0;
309         draw(screen, rectaddpt(Rect(10, 10, 200, 30), screen->r.min), bg, nil, ZP);
310         string(screen, addpt(screen->r.min, Pt(10, 10)), display->black, ZP, display->defaultfont, buf);
311         old = nsec();
312 }
313
314 void
315 flush(void)
316 {
317         extern uchar pic[];
318         Mouse m;
319         int x;
320
321         if(nbrecvul(mc->resizec) > 0){
322                 if(getwindow(display, Refnone) < 0)
323                         sysfatal("resize failed: %r");
324                 screeninit();
325         }
326         while(nbrecv(mc->c, &m) > 0)
327                 ;
328         if(scale == 1){
329                 loadimage(tmp, tmp->r, pic, 240*160*2);
330                 draw(screen, picr, tmp, nil, ZP);
331         } else {
332                 Rectangle r;
333                 uchar *s;
334                 int w;
335
336                 s = pic;
337                 r = picr;
338                 w = 240*2*scale;
339                 while(r.min.y < picr.max.y){
340                         loadimage(tmp, tmp->r, s, w);
341                         s += w;
342                         r.max.y = r.min.y+scale;
343                         draw(screen, r, tmp, nil, ZP);
344                         r.min.y = r.max.y;
345                 }
346         }
347         flushimage(display, 1);
348         if(profile)
349                 timing();
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 [-23T] [-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 's':
392                 s = EARGF(usage());
393                 backup = parsetype(s, &nback);
394                 if(backup == NOBACK)
395                         sysfatal("unknown save type '%s'", s);
396                 break;
397         case 'b':
398                 biosfile = strdup(EARGF(usage()));
399                 break;
400         case 'T':
401                 profile++;
402                 break;
403         default:
404                 usage();
405         } ARGEND;
406         if(argc < 1)
407                 usage();
408
409         loadbios();
410         loadrom(argv[0]);
411         
412         if(initdraw(nil, nil, nil) < 0)
413                 sysfatal("initdraw: %r");
414         mc = initmouse(nil, screen);
415         if(mc == nil)
416                 sysfatal("initmouse: %r");
417         proccreate(keyproc, nil, mainstacksize);
418         screeninit();
419         
420         memreset();
421         reset();
422         for(;;){
423                 if(paused){
424                         qlock(&pauselock);
425                         qunlock(&pauselock);
426                 }
427                 if(dmaact)
428                         t = dmastep();
429                 else if(cpuhalt)
430                         t = 8;
431                 else
432                         t = step();
433                 ppuclock += t;
434                 while(ppuclock >= 4){
435                         ppustep();
436                         ppuclock -= 4;
437                 }
438                 timerstep(t);
439         }
440 }