]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/gb/gb.c
games/nes: cleanup and resize handling
[plan9front.git] / sys / src / games / gb / gb.c
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <thread.h>
5 #include <mouse.h>
6 #include <cursor.h>
7 #include <keyboard.h>
8 #include "dat.h"
9 #include "fns.h"
10
11 uchar *cart, *ram;
12 int mbc, rombanks, rambanks, clock, ppuclock, divclock, timerclock, syncclock, syncfreq, sleeps, checkclock, msgclock, timerfreq, timer, keys, savefd, savereq, loadreq, scale, paused;
13 Rectangle picr;
14 Image *bg, *tmp;
15 Mousectl *mc;
16 QLock pauselock;
17
18 void
19 message(char *fmt, ...)
20 {
21         va_list va;
22         char buf[512];
23         
24         va_start(va, fmt);
25         vsnprint(buf, sizeof buf, fmt, va);
26         string(screen, Pt(10, 10), display->black, ZP, display->defaultfont, buf);
27         msgclock = CPUFREQ;
28         va_end(va);
29 }
30
31 void
32 loadrom(char *file)
33 {
34         int fd, i;
35         vlong len;
36         u8int ck;
37         char buf[512];
38         char title[17];
39         Point p;
40         char *s;
41         extern int battery, ramen;
42         
43         fd = open(file, OREAD);
44         if(fd < 0)
45                 sysfatal("open: %r");
46         len = seek(fd, 0, 2);
47         if(len < 0)
48                 sysfatal("seek: %r");
49         if(len == 0 || len > 16*1048576)
50                 sysfatal("are you sure this is a ROM?");
51         cart = malloc(len);
52         if(cart == nil)
53                 sysfatal("malloc: %r");
54         seek(fd, 0, 0);
55         if(readn(fd, cart, len) < len)
56                 sysfatal("read: %r");
57         close(fd);
58
59         ck = 0;
60         for(i = 0x134; i <= 0x14C; i++)
61                 ck -= cart[i] + 1;
62         if(ck != cart[0x14D])
63                 sysfatal("checksum mismatch: %.2x != %.2x", ck, cart[0x14D]);
64         memcpy(mem, cart, 32768);
65         memset(title, 0, sizeof(title));
66         memcpy(title, cart+0x134, 16);
67         battery = 0;
68         switch(cart[0x147]){
69         case 0x09:
70                 battery = 1;
71         case 0x08:
72                 ramen = 1;
73         case 0x00:
74                 mbc = 0;
75                 break;
76         case 0x03:
77                 battery = 1;
78         case 0x01: case 0x02:
79                 mbc = 1;
80                 break;
81         case 0x06:
82                 battery = 1;
83         case 0x05:
84                 mbc = 2;
85                 break;
86         case 0x0F: case 0x10: case 0x13:
87                 battery = 1;
88         case 0x11: case 0x12:
89                 mbc = 3;
90                 break;
91         case 0x1B: case 0x1E:
92                 battery = 1;
93         case 0x19: case 0x1A: case 0x1C: case 0x1D:
94                 mbc = 5;
95                 break;
96         default:
97                 sysfatal("%s: unknown cartridge type %.2x", file, cart[0x147]);
98         }
99
100         switch(cart[0x148]){
101         case 0: case 1: case 2:
102         case 3: case 4: case 5:
103         case 6: case 7:
104                 rombanks = 2 << (uint)cart[0x148];
105                 break;
106         case 52:
107                 rombanks = 72;
108                 break;
109         case 53:
110                 rombanks = 80;
111                 break;
112         case 54:
113                 rombanks = 96;
114                 break;
115         default:
116                 sysfatal("header field 0x148 (%.2x) invalid", cart[0x148]);
117         }
118         switch(cart[0x149]){
119         case 0:
120                 if(mbc != 2){
121                         rambanks = 0;
122                         break;
123                 }
124                 /*fallthrough*/
125         case 1: case 2:
126                 rambanks = 1;
127                 break;
128         case 3:
129                 rambanks = 4;
130                 break;
131         default:
132                 sysfatal("header field 0x149 (%.2x) invalid", cart[0x149]);
133         }
134         if(rambanks > 0){
135                 ram = mallocz(rambanks * 8192, 1);
136                 if(ram == nil)
137                         sysfatal("malloc: %r");
138         }
139         if(len < rombanks * 0x4000)
140                 sysfatal("cartridge image is too small, %.4x < %.4x", (int)len, rombanks * 0x4000);
141         initdraw(nil, nil, title);
142         originwindow(screen, Pt(0, 0), screen->r.min);
143         p = divpt(addpt(screen->r.min, screen->r.max), 2);
144         picr = (Rectangle){subpt(p, Pt(scale * 80, scale * 72)), addpt(p, Pt(scale * 80, scale * 72))};
145         bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
146         if(screen->chan != XRGB32 || screen->chan != XBGR32)
147                 tmp = allocimage(display, Rect(0, 0, scale * 160, scale * 144), XRGB32, 0, 0);
148         draw(screen, screen->r, bg, nil, ZP);
149         
150         if(ram && battery){
151                 strncpy(buf, file, sizeof buf - 4);
152                 s = buf + strlen(buf) - 3;
153                 if(s < buf || strcmp(s, ".gb") != 0)
154                         s += 3;
155                 strcpy(s, ".gbs");
156                 savefd = create(buf, ORDWR|OEXCL, 0666);
157                 if(savefd < 0)
158                         savefd = open(buf, ORDWR);
159                 if(savefd < 0)
160                         message("open: %r");
161                 else
162                         readn(savefd, ram, rambanks * 8192);
163                 atexit(flushram);
164         }
165 }
166
167 void
168 keyproc(void *)
169 {
170         int fd;
171         char buf[256], *s;
172         Rune r;
173         
174         fd = open("/dev/kbd", OREAD);
175         if(fd < 0)
176                 sysfatal("open: %r");
177         for(;;){
178                 if(read(fd, buf, 256) <= 0)
179                         sysfatal("read /dev/kbd: %r");
180                 if(buf[0] == 'c'){
181                         if(utfrune(buf, Kdel))
182                                 threadexitsall(nil);
183                         if(utfrune(buf, KF|5))
184                                 savereq = 1;
185                         if(utfrune(buf, KF|6))
186                                 loadreq = 1;
187                 }
188                 if(buf[0] != 'k' && buf[0] != 'K')
189                         continue;
190                 s = buf + 1;
191                 keys = 0;
192                 while(*s != 0){
193                         s += chartorune(&r, s);
194                         switch(r){
195                         case Kesc:
196                                 if(paused)
197                                         qunlock(&pauselock);
198                                 else
199                                         qlock(&pauselock);
200                                 paused = !paused;
201                                 break;
202                         case Kdel:
203                                 threadexitsall(nil);
204                         case Kdown:
205                                 keys |= 1<<3;
206                                 break;
207                         case Kup:
208                                 keys |= 1<<2;
209                                 break;
210                         case Kleft:
211                                 keys |= 1<<1;
212                                 break;
213                         case Kright:
214                                 keys |= 1<<0;
215                                 break;
216                         case 'x':
217                                 keys |= 1<<4;
218                                 break;
219                         case 'z':
220                                 keys |= 1<<5;
221                                 break;
222                         case Kshift:
223                                 keys |= 1<<6;
224                                 break;
225                         case 10:
226                                 keys |= 1<<7;
227                                 break;
228                         }
229                 }
230         }
231 }
232
233 void
234 threadmain(int argc, char** argv)
235 {
236         int t;
237         vlong old, new, diff;
238         Mouse m;
239         Point p;
240
241         scale = 1;
242         ARGBEGIN{
243         case 'a':
244                 initaudio();
245                 break;
246         case '2':
247                 scale = 2;
248                 break;
249         case '3':
250                 scale = 3;
251                 break;
252         default:
253                 sysfatal("unknown flag -%c", ARGC());
254         }ARGEND;
255         if(argc == 0)
256                 sysfatal("argument missing");
257         pc = 0x100;
258         sp = 0xFFFE;
259         R[rA] = 0x01;
260         R[rC] = 0x13;
261         R[rE] = 0xD8;
262         R[rL] = 0x4D;
263         R[rH] = 0x01;
264         Fl = 0xB0;
265         loadrom(argv[0]);
266         mc = initmouse(nil, screen);
267         if(mc == nil)
268                 sysfatal("init mouse: %r");
269         proccreate(keyproc, nil, 8192);
270         syncfreq = CPUFREQ / 50;
271         old = nsec();
272         for(;;){
273                 if(savereq){
274                         savestate("gb.save");
275                         savereq = 0;
276                 }
277                 if(loadreq){
278                         loadstate("gb.save");
279                         loadreq = 0;
280                 }
281                 if(paused){
282                         qlock(&pauselock);
283                         qunlock(&pauselock);
284                 }
285                 t = step();
286                 clock += t;
287                 ppuclock += t;
288                 divclock += t;
289                 timerclock += t;
290                 syncclock += t;
291                 checkclock += t;
292                 if(ppuclock >= 456){
293                         ppustep();
294                         ppuclock -= 456;
295                         while(nbrecv(mc->c, &m) > 0)
296                                 ;
297                         if(nbrecvul(mc->resizec) > 0){
298                                 if(getwindow(display, Refnone) < 0)
299                                         sysfatal("resize failed: %r");
300                                 p = divpt(addpt(screen->r.min, screen->r.max), 2);
301                                 picr = (Rectangle){subpt(p, Pt(scale * 80, scale * 72)), addpt(p, Pt(scale * 80, scale * 72))};
302                                 bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
303                         }
304                 }
305                 if(divclock >= 256){
306                         mem[DIV]++;
307                         divclock = 0;
308                 }
309                 if(timer && timerclock >= timerfreq){
310                         mem[TIMA]++;
311                         if(mem[TIMA] == 0){
312                                 mem[TIMA] = mem[TMA];
313                                 interrupt(INTTIMER);
314                         }
315                         timerclock = 0;
316                 }
317                 if(syncclock >= syncfreq){
318                         sleep(10);
319                         sleeps++;
320                         syncclock = 0;
321                 }
322                 if(checkclock >= CPUFREQ){
323                         new = nsec();
324                         diff = new - old - sleeps * 10 * MILLION;
325                         diff = BILLION - diff;
326                         if(diff <= 0)
327                                 syncfreq = CPUFREQ;
328                         else
329                                 syncfreq = ((vlong)CPUFREQ) * 10 * MILLION / diff;
330                         old = new;
331                         checkclock = 0;
332                         sleeps = 0;
333                 }
334                 if(msgclock > 0){
335                         msgclock -= t;
336                         if(msgclock <= 0){
337                                 draw(screen, screen->r, bg, nil, ZP);
338                                 msgclock = 0;
339                         }
340                 }
341         }
342 }