]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/nes/ppu.c
games/nes: mmc3 and bugfixes
[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 u32int 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                 a = (a1 >> (15-ppusx)) & 1 | (a2 >> (14-ppusx)) & 2;
117                 if(ppuy < 240 && ppux <= 257)
118                         pixel(ppux-2, ppuy, pal(c, a, 0), c == 0);
119                 r1 <<= 1;
120                 r2 <<= 1;
121                 a1 <<= 1;
122                 a2 <<= 1;
123         }
124         if(ppux >= 256 && ppux <= 320){
125                 if(ppux == 256)
126                         incppuy();
127                 if(ppux == 257)
128                         ppuv = (ppuv & 0x7BE0) | (pput & 0x041F);
129                 return;
130         }
131         switch(ppux & 7){
132         case 0:
133                 if(ppux != 0){
134                         if((ppuv & 0x1f) == 0x1f){
135                                 ppuv &= ~0x1f;
136                                 ppuv ^= 0x400;
137                         }else
138                                 ppuv++;
139                 }
140                 break;
141         case 1:
142                 t = ppuread(0x2000 | ppuv & 0x0FFF);
143                 if(ppux != 1){
144                         r1 |= nr1;
145                         r2 |= nr2;
146                         if(na & 1)
147                                 a1 |= 0xff;
148                         if(na & 2)
149                                 a2 |= 0xff;
150                 }
151                 break;
152         case 3:
153                 na = ppuread(0x23C0 | ppuv & 0x0C00 | ((ppuv & 0x0380) >> 4) | ((ppuv & 0x001C) >> 2));
154                 if((ppuv & 0x0002) != 0) na >>= 2;
155                 if((ppuv & 0x0040) != 0) na >>= 4;
156                 break;
157         case 5:
158                 nr1 = ppuread(((mem[PPUCTRL] & BGTABLE) << 8) | t << 4 | ppuv >> 12);
159                 break;
160         case 7:
161                 nr2 = ppuread(((mem[PPUCTRL] & BGTABLE) << 8) | t << 4 | ppuv >> 12 | 8);
162                 break;
163         }
164 }
165
166 static void
167 drawsprites(void)
168 {
169         uchar *p;
170         int big, dx, dy, i, x, cc, pri;
171         u8int r1, r2, c;
172         static int n, m, nz, s0, t0;
173         static struct { u8int x, a; u16int t; } s[8], *sp;
174         static struct { u8int x, a, r1, r2; } t[8];
175
176         big = (mem[PPUCTRL] & BIGSPRITE) != 0;
177         if(ppux == 65){
178                 s0 = 0;
179                 for(p = oam, sp = s, n = 0; p < oam + sizeof(oam); p += 4){
180                         if((dy = p[0]) >= 0xEF)
181                                 continue;
182                         dy = ppuy - dy;
183                         if(dy < 0 || dy >= (big ? 16 : 8))
184                                 continue;
185                         if(p == oam)
186                                 s0 = 1;
187                         sp->t = p[1];
188                         sp->a = p[2];
189                         sp->x = p[3];
190                         if((sp->a & (1<<7)) != 0)
191                                 dy = (big ? 15 : 7) - dy;
192                         if(big){
193                                 sp->t |= (sp->t & 1) << 8;
194                                 if(dy >= 8){
195                                         sp->t |= 1;
196                                         dy -= 8;
197                                 }else
198                                         sp->t &= 0x1fe;
199                         }else
200                                 sp->t |= (mem[PPUCTRL] & SPRTABLE) << 5;
201                         sp->t = sp->t << 4 | dy;
202                         sp++;
203                         if(++n == 8)
204                                 break;
205                 }
206         }
207         if(ppux >= 2 && ppux <= 257 && m > 0){
208                 x = ppux - 2;
209                 dx = x - t[0].x;
210                 if(t0 && dx >= 0 && dx < 8 && ppux != 257){
211                         if((nz & 1) != 0 && iscolor(x, ppuy))
212                                 mem[PPUSTATUS] |= SPRITE0HIT;
213                         nz >>= 1;
214                 }
215                 cc = -1;
216                 pri = 0;
217                 for(i = m - 1; i >= 0; i--){
218                         dx = x - t[i].x;
219                         if(dx < 0 || dx > 7)
220                                 continue;
221                         c = (t[i].r1 & 1) | (t[i].r2 & 1) << 1;
222                         if(c != 0){
223                                 cc = pal(c, t[i].a & 3, 1);
224                                 pri = (t[i].a & (1<<5)) == 0;
225                         }
226                         t[i].r1 >>= 1;
227                         t[i].r2 >>= 1;
228                 }
229                 if(cc != -1 && (pri || !iscolor(x, ppuy)))
230                         pixel(x, ppuy, cc, 0);
231         }
232         if(ppux == 257){
233                 for(i = 0; i < n; i++){
234                         r1 = ppuread(s[i].t);
235                         r2 = ppuread(s[i].t | 8);
236                         if((s[i].a & (1<<6)) == 0){
237                                 r1 = ((r1 * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32;
238                                 r2 = ((r2 * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32;
239                         }
240                         t[i].x = s[i].x;
241                         t[i].a = s[i].a;
242                         t[i].r1 = r1;
243                         t[i].r2 = r2;
244                 }
245                 m = n;
246                 nz = t[0].r1 | t[0].r2;
247                 t0 = s0;
248         }
249 }
250
251 static void
252 flush(void)
253 {
254         extern Rectangle picr;
255         extern Image *tmp, *bg;
256         extern Mousectl *mc;
257         Mouse m;
258         Point p;
259
260         while(nbrecv(mc->c, &m) > 0)
261                 ;
262         if(nbrecvul(mc->resizec) > 0){
263                 if(getwindow(display, Refnone) < 0)
264                         sysfatal("resize failed: %r");
265                 p = divpt(addpt(screen->r.min, screen->r.max), 2);
266                 picr = (Rectangle){subpt(p, Pt(scale * 128, scale * 120)), addpt(p, Pt(scale * 128, scale * 120))};
267                 bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
268                 draw(screen, screen->r, bg, nil, ZP);
269         }
270         if(tmp){
271                 loadimage(tmp, tmp->r, pic, 256*240*4*scale*scale);
272                 draw(screen, picr, tmp, nil, ZP);
273         }else
274                 loadimage(screen, picr, pic, 256*240*4*scale*scale);
275         flushimage(display, 1);
276         memset(pic, sizeof pic, 0);
277 }
278
279 void
280 ppustep(void)
281 {
282         extern int nmi;
283         int mask;
284
285         if(ppuy < 240 || ppuy == 261){
286                 mask = mem[PPUMASK];
287                 if((mask & BGDISP) != 0)
288                         drawbg();
289                 if(((mask & BGDISP) == 0 && ppux <= 257 || ppux <= 10 && (mask & BG8DISP) == 0) && ppux >= 2)
290                         pixel(ppux - 2, ppuy, ppuread(0x3F00), 1);
291                 if((mask & SPRITEDISP) != 0 && ppuy != 261 && (ppux > 10 || (mask & SPRITE8DISP) != 0))
292                         drawsprites();
293                 if(ppux == 240 && (mask & SPRITEDISP) != 0)
294                         mapper[map](SCAN, 0);
295                 if(ppuy == 261){
296                         if(ppux == 1)
297                                 mem[PPUSTATUS] &= ~(PPUVBLANK|SPRITE0HIT);
298                         else if(ppux >= 280 && ppux <= 304 && (mask & BGDISP) != 0)
299                                 ppuv = (pput & 0x7BE0) | (ppuv & 0x041F);
300                 }
301         }else if(ppuy == 241){
302                 if(ppux == 1){
303                         mem[PPUSTATUS] |= PPUVBLANK;
304                         if((mem[PPUCTRL] & PPUNMI) != 0)
305                                 nmi = 1;
306                         flush();
307                 }
308         }
309         ppux++;
310         if(ppux > 340){
311                 ppux = 0;
312                 ppuy++;
313                 if(ppuy > 261){
314                         ppuy = 0;
315                         if(odd && (mem[PPUMASK] & (BGDISP | SPRITEDISP)) != 0)
316                                 ppux++;
317                         odd ^= 1;
318                 }
319         }
320 }