]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/nes/nes.c
aa9c4fa2ae4c15887cbcf593cb421ae5cf413c7e
[plan9front.git] / sys / src / games / nes / nes.c
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <thread.h>
5 #include <mouse.h>
6 #include <keyboard.h>
7 #include "dat.h"
8 #include "fns.h"
9
10 extern uchar ppuram[16384];
11 int nprg, nchr, map, chrram;
12 uchar *prg, *chr;
13 int scale;
14 Rectangle picr;
15 Image *tmp, *bg;
16 int clock, ppuclock, apuclock, dmcclock, dmcfreq, sampclock, msgclock, saveclock;
17 Mousectl *mc;
18 int keys, keys2, paused, savereq, loadreq, oflag, savefd = -1;
19 int mirr;
20 QLock pauselock;
21
22 void
23 message(char *fmt, ...)
24 {
25         va_list va;
26         static char buf[512];
27         
28         va_start(va, fmt);
29         vsnprint(buf, sizeof buf, fmt, va);
30         string(screen, Pt(10, 10), display->black, ZP, display->defaultfont, buf);
31         msgclock = FREQ;
32         va_end(va);
33 }
34
35 void
36 flushram(void)
37 {
38         if(savefd >= 0)
39                 pwrite(savefd, mem + 0x6000, 0x2000, 0);
40         saveclock = 0;
41 }
42
43 void
44 loadrom(char *file, int sflag)
45 {
46         int fd;
47         int nes20;
48         char *s;
49         static uchar header[16];
50         static u32int flags;
51         static char buf[512];
52         
53         fd = open(file, OREAD);
54         if(fd < 0)
55                 sysfatal("open: %r");
56         if(readn(fd, header, sizeof(header)) < sizeof(header))
57                 sysfatal("read: %r");
58         if(memcmp(header, "NES\x1a", 4) != 0)
59                 sysfatal("not a ROM");
60         if(header[15] != 0)
61                 memset(header + 7, 0, 9);
62         flags = header[6] | header[7] << 8;
63         nes20 = (flags & FLNES20M) == FLNES20V;
64         if(flags & (FLVS | FLPC10))
65                 sysfatal("ROM not supported");
66         nprg = header[HPRG];
67         if(nes20)
68                 nprg |= (header[HROMH] & 0xf) << 8;
69         if(nprg == 0)
70                 sysfatal("invalid ROM");
71         nchr = header[HCHR];
72         if(nes20)
73                 nchr |= (header[HROMH] & 0xf0) << 4;
74         map = (flags >> FLMAPPERL) & 0x0f | (((flags >> FLMAPPERH) & 0x0f) << 4);
75         if(nes20)
76                 map |= (header[8] & 0x0f) << 8;
77         if(map >= 256 || mapper[map] == nil)
78                 sysfatal("unimplemented mapper %d", map);
79
80         memset(mem, 0, sizeof(mem));
81         if((flags & FLTRAINER) != 0 && readn(fd, mem + 0x7000, 512) < 512)
82                         sysfatal("read: %r");
83         prg = malloc(nprg * PRGSZ);
84         if(prg == nil)
85                 sysfatal("malloc: %r");
86         if(readn(fd, prg, nprg * PRGSZ) < nprg * PRGSZ)
87                 sysfatal("read: %r");
88         chrram = nchr == 0;
89         if(nchr != 0){
90                 chr = malloc(nchr * CHRSZ);
91                 if(chr == nil)
92                         sysfatal("malloc: %r");
93                 if(readn(fd, chr, nchr * CHRSZ) < nchr * CHRSZ)
94                         sysfatal("read: %r");
95         }else{
96                 nchr = 1;
97                 chr = malloc(nchr * CHRSZ);
98                 if(chr == nil)
99                         sysfatal("malloc: %r");
100         }
101         if((flags & FLFOUR) != 0)
102                 mirr = MFOUR;
103         else if((flags & FLMIRROR) != 0)
104                 mirr = MVERT;
105         else
106                 mirr = MHORZ;
107         if(sflag){
108                 strncpy(buf, file, sizeof buf - 5);
109                 s = buf + strlen(buf) - 4;
110                 if(s < buf || strcmp(s, ".nes") != 0)
111                         s += 4;
112                 strcpy(s, ".sav");
113                 savefd = create(buf, ORDWR | OEXCL, 0666);
114                 if(savefd < 0)
115                         savefd = open(buf, ORDWR);
116                 if(savefd < 0)
117                         message("open: %r");
118                 else
119                         readn(savefd, mem + 0x6000, 0x2000);
120                 atexit(flushram);
121         }
122         mapper[map](INIT, 0);
123 }
124
125 extern int trace;
126
127 void
128 joyproc(void *)
129 {
130         char *s, *down[9];
131         static char buf[64];
132         int n, k, j;
133
134         j = 1;
135         for(;;){
136                 n = read(0, buf, sizeof(buf) - 1);
137                 if(n <= 0)
138                         sysfatal("read: %r");
139                 buf[n] = 0;
140                 n = getfields(buf, down, nelem(down), 1, " ");
141                 k = 0;
142                 for(n--; n >= 0; n--){
143                         s = down[n];
144                         if(strcmp(s, "joy1") == 0)
145                                 j = 1;
146                         else if(strcmp(s, "joy2") == 0)
147                                 j = 2;
148                         else if(strcmp(s, "a") == 0)
149                                 k |= 1<<0;
150                         else if(strcmp(s, "b") == 0)
151                                 k |= 1<<1;
152                         else if(strcmp(s, "control") == 0)
153                                 k |= 1<<2;
154                         else if(strcmp(s, "start") == 0)
155                                 k |= 1<<3;
156                         else if(strcmp(s, "up") == 0)
157                                 k |= 1<<4;
158                         else if(strcmp(s, "down") == 0)
159                                 k |= 1<<5;
160                         else if(strcmp(s, "left") == 0)
161                                 k |= 1<<6;
162                         else if(strcmp(s, "right") == 0)
163                                 k |= 1<<7;
164                 }
165                 if(j == 2)
166                         keys2 = k;
167                 else
168                         keys = k;
169         }
170 }
171
172 void
173 keyproc(void *)
174 {
175         int fd, n, k;
176         static char buf[256];
177         char *s;
178         Rune r;
179
180         fd = open("/dev/kbd", OREAD);
181         if(fd < 0)
182                 sysfatal("open: %r");
183         for(;;){
184                 if(buf[0] != 0){
185                         n = strlen(buf)+1;
186                         memmove(buf, buf+n, sizeof(buf)-n);
187                 }
188                 if(buf[0] == 0){
189                         n = read(fd, buf, sizeof(buf)-1);
190                         if(n <= 0)
191                                 sysfatal("read /dev/kbd: %r");
192                         buf[n-1] = 0;
193                         buf[n] = 0;
194                 }
195                 if(buf[0] == 'c'){
196                         if(utfrune(buf, Kdel)){
197                                 close(fd);
198                                 threadexitsall(nil);
199                         }
200                         if(utfrune(buf, KF|5))
201                                 savereq = 1;
202                         if(utfrune(buf, KF|6))
203                                 loadreq = 1;
204                         if(utfrune(buf, 't'))
205                                 trace ^= 1;
206                 }
207                 if(buf[0] != 'k' && buf[0] != 'K')
208                         continue;
209                 s = buf + 1;
210                 k = 0;
211                 while(*s != 0){
212                         s += chartorune(&r, s);
213                         switch(r){
214                         case Kdel: close(fd); threadexitsall(nil);
215                         case 'x': k |= 1<<0; break;
216                         case 'z': k |= 1<<1; break;
217                         case Kshift: k |= 1<<2; break;
218                         case 10: k |= 1<<3; break;
219                         case Kup: k |= 1<<4; break;
220                         case Kdown: k |= 1<<5; break;
221                         case Kleft: k |= 1<<6; break;
222                         case Kright: k |= 1<<7; break;
223                         case Kesc:
224                                 if(paused)
225                                         qunlock(&pauselock);
226                                 else
227                                         qlock(&pauselock);
228                                 paused = !paused;
229                                 break;
230                         }
231                 }
232                 keys = k;
233         }
234 }
235
236 void
237 threadmain(int argc, char **argv)
238 {
239         int t, h, sflag;
240         Point p;
241
242         scale = 1;
243         h = 240;
244         sflag = 0;
245         ARGBEGIN {
246         case 'a':
247                 initaudio();
248                 break;
249         case '2':
250                 scale = 2;
251                 break;
252         case '3':
253                 scale = 3;
254                 break;
255         case 'o':
256                 oflag = 1;
257                 h -= 16;
258                 break;
259         case 's':
260                 sflag = 1;
261                 break;
262         default:
263                 goto usage;
264         } ARGEND;
265
266         if(argc != 1){
267         usage:
268                 fprint(2, "usage: %s [-23aos] rom\n", argv0);
269                 threadexitsall("usage");
270         }
271         loadrom(argv[0], sflag);
272         if(initdraw(nil, nil, nil) < 0)
273                 sysfatal("initdraw: %r");
274         mc = initmouse(nil, screen);
275         if(mc == nil)
276                 sysfatal("initmouse: %r");
277         proccreate(joyproc, nil, 8192);
278         proccreate(keyproc, nil, 8192);
279         originwindow(screen, Pt(0, 0), screen->r.min);
280         p = divpt(addpt(screen->r.min, screen->r.max), 2);
281         picr = (Rectangle){subpt(p, Pt(scale * 128, scale * h/2)), addpt(p, Pt(scale * 128, scale * h/2))};
282         tmp = allocimage(display, Rect(0, 0, scale * 256, scale * h), XRGB32, 0, 0);
283         bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
284         draw(screen, screen->r, bg, nil, ZP);
285         
286         pc = memread(0xFFFC) | memread(0xFFFD) << 8;
287         rP = FLAGI;
288         dmcfreq = 12 * 428;
289         for(;;){
290                 if(savereq){
291                         savestate("nes.save");
292                         savereq = 0;
293                 }
294                 if(loadreq){
295                         loadstate("nes.save");
296                         loadreq = 0;
297                 }
298                 if(paused){
299                         qlock(&pauselock);
300                         qunlock(&pauselock);
301                 }
302                 t = step() * 12;
303                 clock += t;
304                 ppuclock += t;
305                 apuclock += t;
306                 sampclock += t;
307                 dmcclock += t;
308                 while(ppuclock >= 4){
309                         ppustep();
310                         ppuclock -= 4;
311                 }
312                 if(apuclock >= APUDIV){
313                         apustep();
314                         apuclock -= APUDIV;
315                 }
316                 if(sampclock >= SAMPDIV){
317                         audiosample();
318                         sampclock -= SAMPDIV;
319                 }
320                 if(dmcclock >= dmcfreq){
321                         dmcstep();
322                         dmcclock -= dmcfreq;
323                 }
324                 if(msgclock > 0){
325                         msgclock -= t;
326                         if(msgclock <= 0){
327                                 draw(screen, screen->r, bg, nil, ZP);
328                                 msgclock = 0;
329                         }
330                 }
331                 if(saveclock > 0){
332                         saveclock -= t;
333                         if(saveclock <= 0)
334                                 flushram();
335                 }
336         }
337 }