]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/nes/nes.c
added games/nes
[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;
12 uchar *prg, *chr;
13 int scale;
14 Rectangle picr;
15 Image *tmp, *bg;
16 int clock, ppuclock, syncclock, syncfreq, checkclock, sleeps;
17 Mousectl *mc;
18 int keys;
19 extern void (*mapper[])(int, u8int);
20 int mirr;
21
22 void
23 loadrom(char *file)
24 {
25         int fd;
26         int nes20;
27         static uchar header[16];
28         static u32int flags;
29         
30         fd = open(file, OREAD);
31         if(fd < 0)
32                 sysfatal("open: %r");
33         if(readn(fd, header, sizeof(header)) < sizeof(header))
34                 sysfatal("read: %r");
35         if(memcmp(header, "NES\x1a", 4) != 0)
36                 sysfatal("not a ROM");
37         if(header[15] != 0)
38                 memset(header + 7, 0, 9);
39         flags = header[6] | header[7] << 8;
40         nes20 = (flags & FLNES20M) == FLNES20V;
41         if(flags & (FLVS | FLPC10))
42                 sysfatal("ROM not supported");
43         nprg = header[HPRG];
44         if(nes20)
45                 nprg |= (header[HROMH] & 0xf) << 8;
46         if(nprg == 0)
47                 sysfatal("invalid ROM");
48         nchr = header[HCHR];
49         if(nes20)
50                 nchr |= (header[HROMH] & 0xf0) << 4;
51         map = (flags >> FLMAPPERL) & 0x0f | (((flags >> FLMAPPERH) & 0x0f) << 4);
52         if(nes20)
53                 map |= (header[8] & 0x0f) << 8;
54         if(map >= 256 || mapper[map] == nil)
55                 sysfatal("unimplemented mapper %d", map);
56
57         memset(mem, 0, sizeof(mem));
58         if((flags & FLTRAINER) != 0 && readn(fd, mem + 0x7000, 512) < 512)
59                         sysfatal("read: %r");
60         prg = malloc(nprg * PRGSZ);
61         if(prg == nil)
62                 sysfatal("malloc: %r");
63         if(readn(fd, prg, nprg * PRGSZ) < nprg * PRGSZ)
64                 sysfatal("read: %r");
65         if(nchr != 0){
66                 chr = malloc(nchr * CHRSZ);
67                 if(chr == nil)
68                         sysfatal("malloc: %r");
69                 if(readn(fd, chr, nchr * CHRSZ) < nchr * CHRSZ)
70                         sysfatal("read: %r");
71         }else{
72                 nchr = 16;
73                 chr = malloc(16 * CHRSZ);
74                 if(chr == nil)
75                         sysfatal("malloc: %r");
76         }
77         if((flags & FLFOUR) != 0)
78                 mirr = MFOUR;
79         else if((flags & FLMIRROR) != 0)
80                 mirr = MVERT;
81         else
82                 mirr = MHORZ;
83         mapper[map](-1, 0);
84 }
85
86 void
87 keyproc(void *)
88 {
89         int fd;
90         char buf[256], *s;
91         Rune r;
92
93         fd = open("/dev/kbd", OREAD);
94         if(fd < 0)
95                 sysfatal("open: %r");
96         for(;;){
97                 if(read(fd, buf, 256) <= 0)
98                         sysfatal("read /dev/kbd: %r");
99                 if(buf[0] == 'c'){
100                         if(utfrune(buf, Kdel))
101                                 threadexitsall(nil);
102                 }
103                 if(buf[0] != 'k' && buf[0] != 'K')
104                         continue;
105                 s = buf + 1;
106                 keys = 0;
107                 while(*s != 0){
108                         s += chartorune(&r, s);
109                         switch(r){
110                         case Kdel: threadexitsall(nil);
111                         case 'x': keys |= 1<<0; break;
112                         case 'z': keys |= 1<<1; break;
113                         case Kshift: keys |= 1<<2; break;
114                         case 10: keys |= 1<<3; break;
115                         case Kup: keys |= 1<<4; break;
116                         case Kdown: keys |= 1<<5; break;
117                         case Kleft: keys |= 1<<6; break;
118                         case Kright: keys |= 1<<7; break;
119                         }
120                 }       
121         }
122 }
123
124 void
125 threadmain(int argc, char **argv)
126 {
127         int t;
128         Point p;
129         uvlong old, new, diff;
130
131         scale = 1;
132         ARGBEGIN {
133         case '2':
134                 scale = 2;
135                 break;
136         case '3':
137                 scale = 3;
138                 break;
139         } ARGEND;
140
141         if(argc < 1)
142                 sysfatal("missing argument");
143         loadrom(argv[0]);
144         if(initdraw(nil, nil, nil) < 0)
145                 sysfatal("initdraw: %r");
146         mc = initmouse(nil, screen);
147         if(mc == nil)
148                 sysfatal("initmouse: %r");
149         proccreate(keyproc, nil, 8192);
150         originwindow(screen, Pt(0, 0), screen->r.min);
151         p = divpt(addpt(screen->r.min, screen->r.max), 2);
152         picr = (Rectangle){subpt(p, Pt(scale * 128, scale * 120)), addpt(p, Pt(scale * 128, scale * 120))};
153         if(screen->chan != XRGB32)
154                 tmp = allocimage(display, Rect(0, 0, scale * 256, scale * 240), XRGB32, 0, 0);
155         bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
156         draw(screen, screen->r, bg, nil, ZP);
157         
158         pc = memread(0xFFFC) | memread(0xFFFD) << 8;
159         rP = FLAGI;
160         syncfreq = FREQ / 30;
161         old = nsec();
162         for(;;){
163                 t = step() * 12;
164                 clock += t;
165                 ppuclock += t;
166                 syncclock += t;
167                 checkclock += t;
168                 while(ppuclock >= 4){
169                         ppustep();
170                         ppuclock -= 4;
171                 }
172                 if(syncclock >= syncfreq){
173                         sleep(10);
174                         sleeps++;
175                         syncclock = 0;
176                 }
177                 if(checkclock >= FREQ){
178                         new = nsec();
179                         diff = new - old - sleeps * 10 * MILLION;
180                         diff = BILLION - diff;
181                         if(diff <= 0)
182                                 syncfreq = FREQ;
183                         else
184                                 syncfreq = ((vlong)FREQ) * 10 * MILLION / diff;
185                         if(syncfreq < FREQ / 100)
186                                 syncfreq = FREQ / 100;
187                         old = new;
188                         checkclock = 0;
189                         sleeps = 0;
190                 }
191         }
192 }