]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/nes/ppu.c
merge
[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;
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                 for(i = m - 1; i >= 0; i--){
216                         dx = x - t[i].x;
217                         if(dx < 0 || dx > 7)
218                                 continue;
219                         c = (t[i].r1 & 1) | (t[i].r2 & 1) << 1;
220                         if(c != 0){
221                                 if((t[i].a & (1<<5)) == 0 || !iscolor(x, ppuy))
222                                         pixel(x, ppuy, pal(c, t[i].a & 3, 1), 0);
223                         }
224                         t[i].r1 >>= 1;
225                         t[i].r2 >>= 1;
226                 }
227         }
228         if(ppux == 257){
229                 for(i = 0; i < n; i++){
230                         r1 = ppuread(s[i].t);
231                         r2 = ppuread(s[i].t | 8);
232                         if((s[i].a & (1<<6)) == 0){
233                                 r1 = ((r1 * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32;
234                                 r2 = ((r2 * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32;
235                         }
236                         t[i].x = s[i].x;
237                         t[i].a = s[i].a;
238                         t[i].r1 = r1;
239                         t[i].r2 = r2;
240                 }
241                 m = n;
242                 nz = t[0].r1 | t[0].r2;
243                 t0 = s0;
244         }
245 }
246
247 static void
248 flush(void)
249 {
250         extern Rectangle picr;
251         extern Image *tmp, *bg;
252         extern Mousectl *mc;
253         Mouse m;
254         Point p;
255
256         while(nbrecv(mc->c, &m) > 0)
257                 ;
258         if(nbrecvul(mc->resizec) > 0){
259                 if(getwindow(display, Refnone) < 0)
260                         sysfatal("resize failed: %r");
261                 p = divpt(addpt(screen->r.min, screen->r.max), 2);
262                 picr = (Rectangle){subpt(p, Pt(scale * 128, scale * 120)), addpt(p, Pt(scale * 128, scale * 120))};
263                 bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
264                 draw(screen, screen->r, bg, nil, ZP);
265         }
266         if(tmp){
267                 loadimage(tmp, tmp->r, pic, 256*240*4*scale*scale);
268                 draw(screen, picr, tmp, nil, ZP);
269         }else
270                 loadimage(screen, picr, pic, 256*240*4*scale*scale);
271         flushimage(display, 1);
272         memset(pic, sizeof pic, 0);
273 }
274
275 void
276 ppustep(void)
277 {
278         extern int nmi;
279         int bg;
280
281         if(ppuy < 240 || ppuy == 261){
282                 bg = (mem[PPUMASK] & BGDISP) != 0;
283                 if(bg)
284                         drawbg();
285                 if((mem[PPUMASK] & SPRITEDISP) != 0 && ppuy != 261)
286                         drawsprites();
287                 if(ppuy == 261){
288                         if(ppux == 1)
289                                 mem[PPUSTATUS] &= ~(PPUVBLANK|SPRITE0HIT);
290                         else if(ppux >= 280 && ppux <= 304 && bg)
291                                 ppuv = (pput & 0x7BE0) | (ppuv & 0x041F);
292                 }
293         }else if(ppuy == 241){
294                 if(ppux == 1){
295                         mem[PPUSTATUS] |= PPUVBLANK;
296                         if((mem[PPUCTRL] & PPUNMI) != 0)
297                                 nmi = 1;
298                         flush();
299                 }
300         }
301         ppux++;
302         if(ppux > 340){
303                 ppux = 0;
304                 ppuy++;
305                 if(ppuy > 261){
306                         ppuy = 0;
307                         if(odd && (mem[PPUCTRL] & (BGDISP | SPRITEDISP)) != 0)
308                                 ppux++;
309                         odd ^= 1;
310                 }
311         }
312 }