]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/gb/gb.c
games/gb: attempt at fixing sprite priority
[plan9front.git] / sys / src / games / gb / gb.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <draw.h>
5 #include <keyboard.h>
6 #include "../eui.h"
7 #include "dat.h"
8 #include "fns.h"
9
10 int cpuhalt;
11 int backup;
12 int savefd = -1, saveframes;
13 ulong clock;
14 u8int mbc, feat, mode;
15 extern MBC3Timer timer, timerl;
16
17 extern double TAU; 
18 void
19 tauup(void)
20 {
21         TAU += 5000;
22 }
23 void
24 taudn(void)
25 {
26         TAU -= 5000;
27 }
28
29 void
30 writeback(void)
31 {
32         if(saveframes == 0)
33                 saveframes = 15;
34 }
35
36 void
37 timerload(uchar *buf)
38 {
39         timer.ns = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24 | (uvlong)buf[4] << 32 | (uvlong)buf[5] << 40 | (uvlong)buf[6] << 48LL | (uvlong)buf[7] << 56LL;
40         timer.sec = buf[8];
41         timer.min = buf[9];
42         timer.hr = buf[10];
43         timer.dl = buf[11];
44         timer.dh = buf[12];
45         timerl.sec = buf[13];
46         timerl.min = buf[14];
47         timerl.hr = buf[15];
48         timerl.dl = buf[16];
49         timerl.dh = buf[17];
50 }
51
52 void
53 timersave(uchar *buf)
54 {
55         buf[0] = timer.ns;
56         buf[1] = timer.ns >> 8;
57         buf[2] = timer.ns >> 16;
58         buf[3] = timer.ns >> 24;
59         buf[4] = timer.ns >> 32;
60         buf[5] = timer.ns >> 40;
61         buf[6] = timer.ns >> 48;
62         buf[7] = timer.ns >> 56;
63         buf[8] = timer.sec;
64         buf[9] = timer.min;
65         buf[10] = timer.hr;
66         buf[11] = timer.dl;
67         buf[12] = timer.dh;
68         buf[13] = timerl.sec;
69         buf[14] = timerl.min;
70         buf[15] = timerl.hr;
71         buf[16] = timerl.dl;
72         buf[17] = timerl.dh;
73 }
74
75 void
76 flushback(void)
77 {
78         uchar buf[TIMERSIZ];
79
80         if(savefd >= 0){
81                 pwrite(savefd, back, nback, 0);
82                 timersave(buf);
83                 pwrite(savefd, buf, TIMERSIZ, nback);
84         }
85         saveframes = 0;
86 }
87
88 void
89 loadsave(char *file)
90 {
91         char *buf, *p;
92         uchar tim[TIMERSIZ];
93
94         buf = emalloc(strlen(file) + 4);
95         strcpy(buf, file);
96         p = strrchr(buf, '.');
97         if(p == nil)
98                 p = buf + strlen(buf);
99         strcpy(p, ".sav");
100         savefd = open(buf, ORDWR);
101         if(savefd < 0){
102                 savefd = create(buf, OWRITE, 0664);
103                 if(savefd < 0){
104                         fprint(2, "create: %r");
105                         free(buf);
106                         return;
107                 }
108                 back = emalloc(nback);
109                 memset(back, 0, nback);
110                 write(savefd, back, nback);
111                 free(buf);
112                 if((feat & FEATTIM) != 0){
113                         timer.ns = nsec();
114                         timersave(tim);
115                         write(savefd, tim, TIMERSIZ);
116                 }
117                 atexit(flushback);
118                 return;
119         }
120         back = emalloc(nback);
121         readn(savefd, back, nback);
122         if((feat & FEATTIM) != 0){
123                 readn(savefd, tim, TIMERSIZ);
124                 timerload(tim);
125         }
126         atexit(flushback);
127         free(buf);
128 }
129
130 void
131 loadrom(char *file)
132 {
133         int fd;
134         vlong sz;
135         static uchar nintendo[24] = {
136                 0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B, 0x03, 0x73, 0x00, 0x83,
137                 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x08, 0x11, 0x1F, 0x88, 0x89, 0x00, 0x0E
138         };
139         static u8int mbctab[31] = {
140                 0, 1, 1, 1, -1, 2, 2, -1,
141                 0, 0, -1, 6, 6, 6, -1, 3,
142                 3, 3, 3, 3, -1, 4, 4, 4,
143                 -1, 5, 5, 5, 5, 5, 5};
144         static u8int feattab[31] = {
145                 0, 0, FEATRAM, FEATRAM|FEATBAT, 0, FEATRAM, FEATRAM|FEATBAT, 0,
146                 FEATRAM, FEATRAM|FEATBAT, 0, 0, FEATRAM, FEATRAM|FEATBAT, 0, FEATTIM|FEATBAT,
147                 FEATTIM|FEATRAM|FEATBAT, 0, FEATRAM, FEATRAM|FEATBAT, 0, 0, FEATRAM, FEATRAM|FEATBAT,
148                 0, 0, FEATRAM, FEATRAM|FEATBAT, 0, FEATRAM, FEATRAM|FEATBAT
149         };
150         
151         fd = open(file, OREAD);
152         if(fd < 0)
153                 sysfatal("open: %r");
154         sz = seek(fd, 0, 2);
155         if(sz <= 0 || sz > 32*1024*1024)
156                 sysfatal("invalid file size");
157         seek(fd, 0, 0);
158         nrom = sz;
159         rom = emalloc(nrom);
160         if(readn(fd, rom, sz) < sz)
161                 sysfatal("read: %r");
162         close(fd);
163         if(memcmp(rom + 0x104, nintendo, 24) != 0)
164                 sysfatal("not a gameboy rom");
165         if(rom[0x147] > 0x1f)
166                 sysfatal("unsupported mapper ([0x147] = %.2ux)", rom[0x147]);
167         mbc = mbctab[rom[0x147]];
168         feat = feattab[rom[0x147]];
169         if((feat & FEATRAM) != 0){
170                 switch(rom[0x149]){
171                 case 0:
172                         if(mbc == 2)
173                                 nback = 512;
174                         else
175                                 feat &= ~FEATRAM;
176                         break;
177                 case 1: nback = 2048; break;
178                 case 2: nback = 8192; break;
179                 case 3: nback = 32768; break;
180                 default: sysfatal("invalid ram size");
181                 }
182         }
183         if(nback == 0)
184                 nbackbank = 1;
185         else
186                 nbackbank = nback + 8191 >> 13;
187         if((feat & (FEATRAM|FEATTIM)) == 0)
188                 feat &= ~FEATBAT;
189         if((rom[0x143] & 0x80) != 0 && (mode & FORCEDMG) == 0)
190                 mode = CGB|COL;
191         if((feat & FEATBAT) != 0)
192                 loadsave(file); 
193         switch(mbc){
194         case 0: case 1: case 2: case 3: case 5: break;
195         default: sysfatal("unsupported mbc %d", mbc);
196         }
197
198 }
199
200 void
201 flush(void)
202 {
203         extern uchar pic[];
204         static vlong old, delta;
205
206         flushmouse(1);
207         flushscreen();
208         flushaudio(audioout);
209         if(saveframes > 0 && --saveframes == 0)
210                 flushback();
211         if(savereq){
212                 savestate("gb.save");
213                 savereq = 0;
214         }
215         if(loadreq){
216                 loadstate("gb.save");
217                 loadreq = 0;
218         }
219 }
220
221 void
222 usage(void)
223 {
224         fprint(2, "usage: %s [-23aTcd] [-C col0,col1,col2,col3] rom\n", argv0);
225         exits("usage");
226 }
227
228 void
229 colinit(void)
230 {
231         int i;
232         union { u8int c[4]; u32int l; } c;
233         
234         c.c[3] = 0;
235         for(i = 0; i < 4; i++){
236                 c.c[0] = c.c[1] = c.c[2] = i * 0x55;
237                 moncols[i] = c.l;
238         }
239 }
240
241 void
242 colparse(char *p)
243 {
244         int i;
245         union { u8int c[4]; u32int l; } c;
246         u32int l;
247         
248         c.c[3] = 0;
249         for(i = 0; i < 4; i++){
250                 l = strtol(p, &p, 16);
251                 if(*p != (i == 3 ? 0 : ',') || l > 0xffffff)
252                         usage();
253                 p++;
254                 c.c[0] = l;
255                 c.c[1] = l >> 8;
256                 c.c[2] = l >> 16;
257                 moncols[i] = c.l;
258         }
259 }
260
261 void
262 threadmain(int argc, char **argv)
263 {
264         int t;
265
266         colinit();
267         ARGBEGIN {
268         case 'a':
269                 audioinit();
270                 break;
271         case 'c':
272                 mode |= CGB;
273                 break;
274         case 'd':
275                 mode |= FORCEDMG;
276                 break;
277         case 'C':
278                 colparse(EARGF(usage()));
279                 break;
280         default:
281                 usage();
282         } ARGEND;
283         if(argc < 1)
284                 usage();
285
286         loadrom(argv[0]);
287         initemu(PICW, PICH, 4, XRGB32, 1, nil);
288         regkey("b", 'z', 1<<5);
289         regkey("a", 'x', 1<<4);
290         regkey("control", Kshift, 1<<6);
291         regkey("start", '\n', 1<<7);
292         regkey("up", Kup, 1<<2);
293         regkey("down", Kdown, 1<<3);
294         regkey("left", Kleft, 1<<1);
295         regkey("right", Kright, 1<<0);
296         regkeyfn(KF|9, tauup);
297         regkeyfn(KF|10, taudn);
298
299         eventinit();
300         meminit();
301         ppuinit();
302         reset();
303         for(;;){
304                 if(paused){
305                         qlock(&pauselock);
306                         qunlock(&pauselock);
307                 }
308                 if(dma > 0)
309                         t = dmastep();
310                 else
311                         t = step();
312                 if((mode & TURBO) == 0)
313                         t += t;
314                 clock += t;
315                 if((elist->time -= t) <= 0)
316                         popevent();
317         }
318 }