]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/nes/nes.c
games/nes: it's too late to write code
[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, 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 = 16;
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 keyproc(void *)
129 {
130         int fd, k;
131         static char buf[256];
132         char *s;
133         Rune r;
134
135         fd = open("/dev/kbd", OREAD);
136         if(fd < 0)
137                 sysfatal("open: %r");
138         for(;;){
139                 if(read(fd, buf, sizeof(buf) - 1) <= 0)
140                         sysfatal("read /dev/kbd: %r");
141                 if(buf[0] == 'c'){
142                         if(utfrune(buf, Kdel)){
143                                 close(fd);
144                                 threadexitsall(nil);
145                         }
146                         if(utfrune(buf, KF|5))
147                                 savereq = 1;
148                         if(utfrune(buf, KF|6))
149                                 loadreq = 1;
150                         if(utfrune(buf, 't'))
151                                 trace ^= 1;
152                 }
153                 if(buf[0] != 'k' && buf[0] != 'K')
154                         continue;
155                 s = buf + 1;
156                 k = 0;
157                 while(*s != 0){
158                         s += chartorune(&r, s);
159                         switch(r){
160                         case Kdel: close(fd); threadexitsall(nil);
161                         case 'x': k |= 1<<0; break;
162                         case 'z': k |= 1<<1; break;
163                         case Kshift: k |= 1<<2; break;
164                         case 10: k |= 1<<3; break;
165                         case Kup: k |= 1<<4; break;
166                         case Kdown: k |= 1<<5; break;
167                         case Kleft: k |= 1<<6; break;
168                         case Kright: k |= 1<<7; break;
169                         case Kesc:
170                                 if(paused)
171                                         qunlock(&pauselock);
172                                 else
173                                         qlock(&pauselock);
174                                 paused = !paused;
175                                 break;
176                         }
177                 }
178                 keys = k;
179         }
180 }
181
182 void
183 threadmain(int argc, char **argv)
184 {
185         int t, h, sflag;
186         Point p;
187
188         scale = 1;
189         h = 240;
190         sflag = 0;
191         ARGBEGIN {
192         case 'a':
193                 initaudio();
194                 break;
195         case '2':
196                 scale = 2;
197                 break;
198         case '3':
199                 scale = 3;
200                 break;
201         case 'o':
202                 oflag = 1;
203                 h -= 16;
204                 break;
205         case 's':
206                 sflag = 1;
207                 break;
208         default:
209                 goto usage;
210         } ARGEND;
211
212         if(argc != 1){
213         usage:
214                 fprint(2, "usage: %s [-23aos] rom\n", argv0);
215                 threadexitsall("usage");
216         }
217         loadrom(argv[0], sflag);
218         if(initdraw(nil, nil, nil) < 0)
219                 sysfatal("initdraw: %r");
220         mc = initmouse(nil, screen);
221         if(mc == nil)
222                 sysfatal("initmouse: %r");
223         proccreate(keyproc, nil, 8192);
224         originwindow(screen, Pt(0, 0), screen->r.min);
225         p = divpt(addpt(screen->r.min, screen->r.max), 2);
226         picr = (Rectangle){subpt(p, Pt(scale * 128, scale * h/2)), addpt(p, Pt(scale * 128, scale * h/2))};
227         tmp = allocimage(display, Rect(0, 0, scale * 256, scale * h), XRGB32, 0, 0);
228         bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
229         draw(screen, screen->r, bg, nil, ZP);
230         
231         pc = memread(0xFFFC) | memread(0xFFFD) << 8;
232         rP = FLAGI;
233         dmcfreq = 12 * 428;
234         for(;;){
235                 if(savereq){
236                         savestate("nes.save");
237                         savereq = 0;
238                 }
239                 if(loadreq){
240                         loadstate("nes.save");
241                         loadreq = 0;
242                 }
243                 if(paused){
244                         qlock(&pauselock);
245                         qunlock(&pauselock);
246                 }
247                 t = step() * 12;
248                 clock += t;
249                 ppuclock += t;
250                 apuclock += t;
251                 sampclock += t;
252                 dmcclock += t;
253                 while(ppuclock >= 4){
254                         ppustep();
255                         ppuclock -= 4;
256                 }
257                 if(apuclock >= APUDIV){
258                         apustep();
259                         apuclock -= APUDIV;
260                 }
261                 if(sampclock >= SAMPDIV){
262                         audiosample();
263                         sampclock -= SAMPDIV;
264                 }
265                 if(dmcclock >= dmcfreq){
266                         dmcstep();
267                         dmcclock -= dmcfreq;
268                 }
269                 if(msgclock > 0){
270                         msgclock -= t;
271                         if(msgclock <= 0){
272                                 draw(screen, screen->r, bg, nil, ZP);
273                                 msgclock = 0;
274                         }
275                 }
276                 if(saveclock > 0){
277                         saveclock -= t;
278                         if(saveclock <= 0)
279                                 flushram();
280                 }
281         }
282 }