]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/nes/ppu.c
games/nes: SUROM support, subtle NMI timing bug fixed
[plan9front.git] / sys / src / games / nes / ppu.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <draw.h>
5 #include <mouse.h>
6 #include "dat.h"
7 #include "fns.h"
8
9 int ppuy, ppux, odd;
10 uchar pic[256*240*4*9];
11
12 static void
13 pixel(int x, int y, int val, int back)
14 {
15         int Y;
16         union { u8int c[4]; u32int l; } u;
17         u32int *p, l;
18         static u8int palred[64] = {
19                 0x7C, 0x00, 0x00, 0x44, 0x94, 0xA8, 0xA8, 0x88, 
20                 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
21                 0xBC, 0x00, 0x00, 0x68, 0xD8, 0xE4, 0xF8, 0xE4, 
22                 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
23                 0xF8, 0x3C, 0x68, 0x98, 0xF8, 0xF8, 0xF8, 0xFC, 
24                 0xF8, 0xB8, 0x58, 0x58, 0x00, 0x78, 0x00, 0x00, 
25                 0xFC, 0xA4, 0xB8, 0xD8, 0xF8, 0xF8, 0xF0, 0xFC, 
26                 0xF8, 0xD8, 0xB8, 0xB8, 0x00, 0xF8, 0x00, 0x00, 
27         };
28         static u8int palgreen[64] = {
29                 0x7C, 0x00, 0x00, 0x28, 0x00, 0x00, 0x10, 0x14, 
30                 0x30, 0x78, 0x68, 0x58, 0x40, 0x00, 0x00, 0x00, 
31                 0xBC, 0x78, 0x58, 0x44, 0x00, 0x00, 0x38, 0x5C, 
32                 0x7C, 0xB8, 0xA8, 0xA8, 0x88, 0x00, 0x00, 0x00, 
33                 0xF8, 0xBC, 0x88, 0x78, 0x78, 0x58, 0x78, 0xA0, 
34                 0xB8, 0xF8, 0xD8, 0xF8, 0xE8, 0x78, 0x00, 0x00, 
35                 0xFC, 0xE4, 0xB8, 0xB8, 0xB8, 0xA4, 0xD0, 0xE0, 
36                 0xD8, 0xF8, 0xF8, 0xF8, 0xFC, 0xD8, 0x00, 0x00, 
37         };
38         static u8int palblue[64] = {
39                 0x7C, 0xFC, 0xBC, 0xBC, 0x84, 0x20, 0x00, 0x00, 
40                 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 
41                 0xBC, 0xF8, 0xF8, 0xFC, 0xCC, 0x58, 0x00, 0x10, 
42                 0x00, 0x00, 0x00, 0x44, 0x88, 0x00, 0x00, 0x00, 
43                 0xF8, 0xFC, 0xFC, 0xF8, 0xF8, 0x98, 0x58, 0x44, 
44                 0x00, 0x18, 0x54, 0x98, 0xD8, 0x78, 0x00, 0x00, 
45                 0xFC, 0xFC, 0xF8, 0xF8, 0xF8, 0xC0, 0xB0, 0xA8, 
46                 0x78, 0x78, 0xB8, 0xD8, 0xFC, 0xF8, 0x00, 0x00, 
47         };
48
49         u.c[0] = palblue[val];
50         u.c[1] = palgreen[val];
51         u.c[2] = palred[val];
52         u.c[3] = back ? 0 : 0xFF;
53         l = u.l;
54         if(scale == 3){
55                 p = ((u32int*)pic) + y * 3 * 3 * 256 + 3 * x;
56                 for(Y = 0; Y < 3; Y++){
57                         *p++ = l;
58                         *p++ = l;
59                         *p = l;
60                         p += 3 * 256 - 2;
61                 }
62         }else if(scale == 2){
63                 p = ((u32int*)pic) + y * 2 * 2 * 256 + 2 * x;
64                 *p++ = l;
65                 *p = l;
66                 p += 2 * 256 - 1;
67                 *p++ = l;
68                 *p = l;
69         }else{
70                 p = ((u32int*)pic) + y * 256 + x;
71                 *p = l;
72         }
73 }
74
75 static int
76 iscolor(int x, int y)
77 {
78         return pic[y * scale * scale * 256 * 4 + x * scale * 4 + 3] != 0;
79 }
80
81 static int
82 pal(int c, int a, int spr)
83 {
84         if(c == 0)
85                 return ppuread(0x3F00);
86         return ppuread(0x3F00 | ((a&3)<<2) | (c & 3) | (spr << 4));
87 }
88
89 static void
90 incppuy(void)
91 {
92         int y;
93
94         if((ppuv & 0x7000) != 0x7000){
95                 ppuv += 0x1000;
96                 return;
97         }
98         y = (ppuv >> 5) & 31;
99         if(y++ == 29){
100                 y = 0;
101                 ppuv ^= 0x800;
102         }
103         ppuv = (ppuv & 0x0C1F) | ((y & 31) << 5);
104 }
105
106 static void
107 drawbg(void)
108 {
109         static int t;
110         u8int c, a;
111         static u8int nr1, nr2, na;
112         static u16int r1, r2, a1, a2;
113         
114         if(ppux >= 2 && ppux <= 257 || ppux >= 322 && ppux <= 337){
115                 c = (r1 >> (15-ppusx)) & 1 | (r2 >> (14-ppusx)) & 2;
116                 if(ppuy < 240 && ppux <= 257){
117                         a = (a1 >> (15-ppusx)) & 1 | (a2 >> (14-ppusx)) & 2;
118                         pixel(ppux-2, ppuy, pal(c, a, 0), c == 0);
119                 }
120                 r1 <<= 1;
121                 r2 <<= 1;
122                 a1 <<= 1;
123                 a2 <<= 1;
124         }
125         if(ppux >= 256 && ppux <= 320){
126                 if(ppux == 256)
127                         incppuy();
128                 if(ppux == 257)
129                         ppuv = (ppuv & 0x7BE0) | (pput & 0x041F);
130                 return;
131         }
132         switch(ppux & 7){
133         case 0:
134                 if(ppux != 0){
135                         if((ppuv & 0x1f) == 0x1f){
136                                 ppuv &= ~0x1f;
137                                 ppuv ^= 0x400;
138                         }else
139                                 ppuv++;
140                 }
141                 break;
142         case 1:
143                 t = ppuread(0x2000 | ppuv & 0x0FFF);
144                 if(ppux != 1){
145                         r1 |= nr1;
146                         r2 |= nr2;
147                         if(na & 1)
148                                 a1 |= 0xff;
149                         if(na & 2)
150                                 a2 |= 0xff;
151                 }
152                 break;
153         case 3:
154                 na = ppuread(0x23C0 | ppuv & 0x0C00 | ((ppuv & 0x0380) >> 4) | ((ppuv & 0x001C) >> 2));
155                 if((ppuv & 0x0002) != 0) na >>= 2;
156                 if((ppuv & 0x0040) != 0) na >>= 4;
157                 break;
158         case 5:
159                 nr1 = ppuread(((mem[PPUCTRL] & BGTABLE) << 8) | t << 4 | ppuv >> 12);
160                 break;
161         case 7:
162                 nr2 = ppuread(((mem[PPUCTRL] & BGTABLE) << 8) | t << 4 | ppuv >> 12 | 8);
163                 break;
164         }
165 }
166
167 static void
168 drawsprites(int show)
169 {
170         uchar *p;
171         int big, dx, dy, i, x, cc, pri;
172         u8int r1, r2, c;
173         static int n, m, nz, s0, t0;
174         static struct { u8int x, a; u16int t; } s[8], *sp;
175         static struct { u8int x, a, r1, r2; } t[8];
176
177         big = (mem[PPUCTRL] & BIGSPRITE) != 0;
178         if(ppux == 65){
179                 s0 = 0;
180                 for(p = oam, sp = s, n = 0; p < oam + sizeof(oam); p += 4){
181                         if((dy = p[0]) >= 0xEF)
182                                 continue;
183                         dy = ppuy - dy;
184                         if(dy < 0 || dy >= (big ? 16 : 8))
185                                 continue;
186                         if(p == oam)
187                                 s0 = 1;
188                         sp->t = p[1];
189                         sp->a = p[2];
190                         sp->x = p[3];
191                         if((sp->a & (1<<7)) != 0)
192                                 dy = (big ? 15 : 7) - dy;
193                         if(big){
194                                 sp->t |= (sp->t & 1) << 8;
195                                 if(dy >= 8){
196                                         sp->t |= 1;
197                                         dy -= 8;
198                                 }else
199                                         sp->t &= 0x1fe;
200                         }else
201                                 sp->t |= (mem[PPUCTRL] & SPRTABLE) << 5;
202                         sp->t = sp->t << 4 | dy;
203                         sp++;
204                         if(++n == 8)
205                                 break;
206                 }
207         }
208         if(ppux >= 2 && ppux <= 257 && m > 0){
209                 x = ppux - 2;
210                 dx = x - t[0].x;
211                 if(t0 && dx >= 0 && dx < 8 && ppux != 257){
212                         if((nz & 1) != 0 && iscolor(x, ppuy) && show)
213                                 mem[PPUSTATUS] |= SPRITE0HIT;
214                         nz >>= 1;
215                 }
216                 cc = -1;
217                 pri = 0;
218                 for(i = m - 1; i >= 0; i--){
219                         dx = x - t[i].x;
220                         if(dx < 0 || dx > 7)
221                                 continue;
222                         c = (t[i].r1 & 1) | (t[i].r2 & 1) << 1;
223                         if(c != 0){
224                                 cc = pal(c, t[i].a & 3, 1);
225                                 pri = (t[i].a & (1<<5)) == 0;
226                         }
227                         t[i].r1 >>= 1;
228                         t[i].r2 >>= 1;
229                 }
230                 if(cc != -1 && show && (pri || !iscolor(x, ppuy)))
231                         pixel(x, ppuy, cc, 0);
232         }
233         if(ppux == 257){
234                 for(i = 0; i < n; i++){
235                         r1 = ppuread(s[i].t);
236                         r2 = ppuread(s[i].t | 8);
237                         if((s[i].a & (1<<6)) == 0){
238                                 r1 = ((r1 * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32;
239                                 r2 = ((r2 * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32;
240                         }
241                         t[i].x = s[i].x;
242                         t[i].a = s[i].a;
243                         t[i].r1 = r1;
244                         t[i].r2 = r2;
245                 }
246                 m = n;
247                 nz = t[0].r1 | t[0].r2;
248                 t0 = s0;
249         }
250 }
251
252 static void
253 flush(void)
254 {
255         extern Rectangle picr;
256         extern Image *tmp, *bg;
257         extern Mousectl *mc;
258         static vlong old, delta;
259         vlong new, diff;
260         Mouse m;
261         Point p;
262         int h;
263
264         h = 240;
265         if(oflag)
266                 h -= 16;
267         while(nbrecv(mc->c, &m) > 0)
268                 ;
269         if(nbrecvul(mc->resizec) > 0){
270                 if(getwindow(display, Refnone) < 0)
271                         sysfatal("resize failed: %r");
272                 p = divpt(addpt(screen->r.min, screen->r.max), 2);
273                 picr = (Rectangle){subpt(p, Pt(scale * 128, scale * h/2)), addpt(p, Pt(scale * 128, scale * h/2))};
274                 if(bg->chan != screen->chan){
275                         freeimage(bg);
276                         bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
277                 }
278                 draw(screen, screen->r, bg, nil, ZP);
279         }
280         if(screen->chan != tmp->chan || !rectinrect(picr, screen->r)){
281                 loadimage(tmp, tmp->r, pic + oflag*8*256*4*scale*scale, 256*h*4*scale*scale);
282                 draw(screen, picr, tmp, nil, ZP);
283         }else
284                 loadimage(screen, picr, pic + oflag*8*256*4*scale*scale, 256*h*4*scale*scale);
285         flushimage(display, 1);
286         memset(pic, sizeof pic, 0);
287         if(audioout() < 0){
288                 new = nsec();
289                 diff = 0;
290                 if(old != 0){
291                         diff = BILLION/60 - (new - old) - delta;
292                         if(diff >= MILLION)
293                                 sleep(diff/MILLION);
294                 }
295                 old = nsec();
296                 if(diff != 0){
297                         diff = (old - new) - (diff / MILLION) * MILLION;
298                         delta += (diff - delta) / 100;
299                 }
300         }
301 }
302
303 void
304 ppustep(void)
305 {
306         extern int nmi;
307         int mask;
308
309         if(ppuy < 240 || ppuy == 261){
310                 mask = mem[PPUMASK];
311                 if((mask & BGDISP) != 0)
312                         drawbg();
313                 if(((mask & BGDISP) == 0 && ppux <= 257 || ppux < 10 && (mask & BG8DISP) == 0) && ppux >= 2)
314                         pixel(ppux - 2, ppuy, ppuread(0x3F00), 1);
315                 if((mask & SPRITEDISP) != 0 && ppuy != 261)
316                         drawsprites(ppux >= 10 || (mask & SPRITE8DISP) != 0);
317                 if(ppux == 240 && (mask & SPRITEDISP) != 0)
318                         mapper[map](SCAN, 0);
319                 if(ppuy == 261){
320                         if(ppux == 1)
321                                 mem[PPUSTATUS] &= ~(PPUVBLANK|SPRITE0HIT);
322                         else if(ppux >= 280 && ppux <= 304 && (mask & BGDISP) != 0)
323                                 ppuv = (pput & 0x7BE0) | (ppuv & 0x041F);
324                 }
325         }else if(ppuy == 241){
326                 if(ppux == 1){
327                         mem[PPUSTATUS] |= PPUVBLANK;
328                         if((mem[PPUCTRL] & PPUNMI) != 0)
329                                 nmi = 2;
330                         flush();
331                 }
332         }
333         ppux++;
334         if(ppux > 340){
335                 ppux = 0;
336                 ppuy++;
337                 if(ppuy > 261){
338                         ppuy = 0;
339                         if(odd && (mem[PPUMASK] & (BGDISP | SPRITEDISP)) != 0)
340                                 ppux++;
341                         odd ^= 1;
342                 }
343         }
344 }