]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/gb/ppu.c
games/gb: fix mbc5 register addressing (fixes warioland3 gamebreaking bug)
[plan9front.git] / sys / src / games / gb / ppu.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include "../eui.h"
5 #include "dat.h"
6 #include "fns.h"
7
8 u8int ppustate, ppuy;
9 ulong hblclock, rendclock;
10 jmp_buf mainjmp, renderjmp;
11 static int cyc, done, ppux, ppux0;
12 extern int prish;
13 extern u32int white;
14
15 Var ppuvars[] = {VAR(ppustate), VAR(ppuy), VAR(hblclock), VAR(rendclock), {nil, 0, 0}};
16
17 #define ryield() {if(setjmp(renderjmp) == 0) longjmp(mainjmp, 1);}
18 #define myield() {if(setjmp(mainjmp) == 0) longjmp(renderjmp, 1);}
19
20 typedef struct sprite sprite;
21 struct sprite {
22         u8int dy, x, t;
23         u8int fetched, pal;
24         u16int chr;
25 };
26 enum {
27         SPRPRI = 0x80,
28         SPRYFL = 0x40,
29         SPRXFL = 0x20,
30         SPRPAL = 0x10,
31         SPRBANK = 0x08,
32         
33         TILCOL0 = 0x01,
34         TILPRI = 0x02,
35         TILSPR = 0x04,
36 };
37 sprite spr[10], *sprm;
38
39 void
40 ppurender(void)
41 {
42         int x, y, i, n, m, win;
43         u16int ta, ca, chr;
44         u8int tile, attr, pali;
45         u32int sr[8], *picp;
46         #define eat(nc) if(cyc <= nc){for(i = 0; i < nc; i++) if(--cyc == 0) ryield();} else cyc -= nc;
47         
48         ryield();
49         
50         attr = 0;
51         for(;;){
52                 eat(6*2);
53                 m = 168 + (reg[SCX] & 7);
54                 win = 0;
55                 if((reg[LCDC] & WINEN) != 0 && ppuy >= reg[WY] && reg[WX] <= 166)
56                         if(reg[WX] == 0)
57                                 m = 7;
58                         else if(reg[WX] == 166)
59                                 m = 0;
60                         else{
61                                 m = reg[WX] + (reg[SCX] & 7) + 1;
62                                 win = -1;
63                         }
64                 ppux = 0;
65                 ppux0 = 0;
66                 picp = (u32int*)pic + ppuy * PICW * scale;
67                 y = ppuy + reg[SCY] << 1 & 14;
68                 ta = 0x1800 | reg[LCDC] << 7 & 0x400 | ppuy + reg[SCY] << 2 & 0x3e0 | reg[SCX] >> 3;
69                 x = -(reg[SCX] & 7);
70         restart:
71                 do{
72                         tile = vram[ta];
73                         if((mode & COL) != 0)
74                                 attr = vram[8192 + ta];
75                         if((reg[LCDC] & BGTILE) != 0)
76                                 ca = (tile << 4) + y;
77                         else
78                                 ca = 0x1000 + ((s32int)(tile << 24) >> 20) + y;
79                         if((attr & 0x40) != 0)
80                                 ca ^= 14;
81                         ca |= (attr & 8) << 10;
82                         chr = vram[ca] << 8 | vram[ca+1];
83                         pali = attr << 2 & 0x1c;
84                         if((attr & 0x20) == 0)
85                                 for(i = 0; i < 8; i++){
86                                         sr[i] = pal[pali | chr >> 15 | chr >> 6 & 2] | ((chr & 0x8080) == 0) << prish;
87                                         chr <<= 1;
88                                 }
89                         else
90                                 for(i = 0; i < 8; i++){
91                                         sr[i] = pal[pali | chr << 1 & 2 | chr >> 8 & 1] | ((chr & 0x0101) == 0) << prish;
92                                         chr >>= 1;
93                                 }
94                         if((attr & 0x80) != 0)
95                                 for(i = 0; i < 8; i++)
96                                         sr[i] |= 2 << prish;
97                         if((reg[LCDC] & BGEN) == 0 && (mode & COL) == 0 && ((mode & CGB) != 0 || win == 0))
98                                 for(i = 0; i < 8; i++)
99                                         sr[i] = white;
100                         if(cyc <= 2*8){
101                                 for(i = 0; i < 2*8; i++)
102                                         if(--cyc == 0)
103                                                 ryield();
104                                 y = ppuy + reg[SCY] << 1 & 14;
105                                 ta = 0x1800 | reg[LCDC] << 7 & 0x400 | ppuy + reg[SCY] << 2 & 0x3e0 | ta & 0x1f;
106                         }else
107                                 cyc -= 2*8;
108                         m -= 8;
109                         n = m < 8 ? m : 8;
110                         if((ta & 0x1f) == 0x1f)
111                                 ta &= ~0x1f;
112                         else
113                                 ta++;
114                         for(i = 0; i < n; i++, x++)
115                                 if(x >= 0)
116                                         picp[x] = sr[i];
117                         ppux = x;
118                 }while(m > 8);
119                 if(win == -1){
120                         win = 1;
121                         ta = 0x1800 | reg[LCDC] << 4 & 0x400 | ppuy - reg[WY] << 2 & 0x3e0;
122                         y = ppuy - reg[WY] << 1 & 14;
123                         cyc += 2;
124                         m = 175 - reg[WX];
125                         goto restart;
126                 }
127                 done = 1;
128                 ryield();
129         }
130 }
131
132 void
133 oamsearch(void)
134 {
135         u8int *p;
136         sprite *q;
137         int y0, sy;
138         u8int t, tn;
139         u8int *chrp;
140         
141         y0 = ppuy + 16;
142         sy = (reg[LCDC] & SPR16) != 0 ? 16 : 8;
143         sprm = spr;
144         if((reg[LCDC] & SPREN) == 0)
145                 return;
146         for(p = oam; p < oam + 160; p += 4){
147                 if((u8int)(y0 - p[0]) >= sy)
148                         continue;
149                 if((mode & COL) == 0){
150                         for(q = spr; q < sprm; q++)
151                                 if(q->x > p[1]){
152                                         if(sprm != spr + 10){
153                                                 memmove(q + 1, q, (sprm - q) * sizeof(sprite));
154                                                 sprm++;
155                                         }else
156                                                 memmove(q + 1, q, (sprm - 1 - q) * sizeof(sprite));
157                                         goto found;
158                                 }
159                         if(q == spr + 10)
160                                 continue;
161                         sprm++;
162                 found:;
163                 }else
164                         q = sprm++;
165                 q->dy = y0 - p[0];
166                 q->x = p[1];
167                 q->t = t = p[3];
168                 if((t & SPRYFL) != 0)
169                         q->dy ^= sy - 1;
170                 tn = p[2];
171                 if(sy == 16)
172                         tn = tn & ~1 | q->dy >> 3;
173                 chrp = vram + (tn << 4 | q->dy << 1 & 14);
174                 if((mode & COL) != 0){
175                         chrp += t << 10 & 0x2000;
176                         q->pal = 0x20 | t << 2 & 0x1c;
177                 }else
178                         q->pal = 4 + (t >> 2 & 4);
179                 q->chr = chrp[0] << 8 | chrp[1];
180                 if(p[1] < 8)
181                         if((t & SPRXFL) != 0)
182                                 q->chr >>= 8 - p[1];
183                         else
184                                 q->chr <<= 8 - p[1];
185                 q->fetched = 0;
186                 if((mode & COL) != 0 && sprm == spr + 10)
187                         break;
188         }
189 }
190
191 void
192 sprites(void)
193 {
194         sprite *q;
195         u8int attr;
196         u32int *picp;
197         int x, x1;
198         u16int chr;
199         
200         picp = (u32int*)pic + ppuy * PICW * scale;
201         for(q = spr; q < sprm; q++){
202                 if(q->x <= ppux0 || q->x >= ppux + 8)
203                         continue;
204                 x = q->x - 8;
205                 if(x < ppux0) x = ppux0;
206                 x1 = q->x;
207                 if(x1 > ppux) x1 = ppux;
208                 for(; x < x1; x++){
209                         attr = picp[x] >> prish;
210                         chr = q->chr;
211                         if((chr & ((q->t & SPRXFL) != 0 ? 0x0101 : 0x8080)) != 0 && (attr & TILSPR) == 0 &&
212                                         ((mode & COL) != 0 && (reg[LCDC] & BGPRI) == 0 ||
213                                         (attr & TILPRI) == 0 && ((q->t & SPRPRI) == 0 || (attr & TILCOL0) != 0)))
214                                 if((q->t & SPRXFL) == 0)
215                                         picp[x] = pal[q->pal | chr >> 15 | chr >> 6 & 2] | TILSPR << prish;
216                                 else
217                                         picp[x] = pal[q->pal | chr << 1 & 2 | chr >> 8 & 1] | TILSPR << prish;
218                         if((q->t & SPRXFL) != 0)
219                                 q->chr >>= 1;
220                         else
221                                 q->chr <<= 1;
222                 }
223         }
224         ppux0 = ppux;
225 }
226
227 void
228 ppusync(void)
229 {
230         if(ppustate != 3)
231                 return;
232         cyc = clock - rendclock;
233         if(cyc != 0)
234                 myield();
235         sprites();
236         rendclock = clock;
237 }
238
239 int
240 linelen(void)
241 {
242         int t;
243         
244         t = 174 + (reg[SCX] & 7);
245         if((reg[LCDC] & WINEN) != 0 && ppuy >= reg[WY] && reg[WX] < 166)
246                 if(reg[WX] == 0)
247                         t += 7;
248                 else
249                         t += 6;
250         return t*2;
251 }
252
253 static void
254 lineexpand(void)
255 {
256         u32int *picp, *p, *q, l;
257         int i;
258
259         picp = (u32int*)pic + ppuy * PICW * scale;
260         p = picp + PICW;
261         q = picp + PICW * scale;
262         for(i = PICW; --i >= 0; ){
263                 l = *--p;
264                 switch(scale){
265                 case 16: *--q = l;
266                 case 15: *--q = l;
267                 case 14: *--q = l;
268                 case 13: *--q = l;
269                 case 12: *--q = l;
270                 case 11: *--q = l;
271                 case 10: *--q = l;
272                 case 9: *--q = l;
273                 case 8: *--q = l;
274                 case 7: *--q = l;
275                 case 6: *--q = l;
276                 case 5: *--q = l;
277                 case 4: *--q = l;
278                 case 3: *--q = l;
279                 case 2: *--q = l;
280                 case 1: *--q = l;
281                 }
282         }
283 }
284
285 void
286 hblanktick(void *)
287 {
288         extern Event evhblank;
289         int t;
290         
291         switch(ppustate){
292         case 0:
293                 hblclock = clock + evhblank.time;
294                 if(++ppuy == 144){
295                         ppustate = 1;
296                         if((reg[STAT] & IRQM1) != 0)
297                                 reg[IF] |= IRQLCDS;
298                         addevent(&evhblank, 456*2);
299                         reg[IF] |= IRQVBL;
300                         flush();
301                 }else{
302                         ppustate = 2;
303                         if((reg[STAT] & IRQM2) != 0)
304                                 reg[IF] |= IRQLCDS;
305                         addevent(&evhblank, 80*2);
306                 }
307                 if((reg[STAT] & IRQLYC) != 0 && ppuy == reg[LYC])
308                         reg[IF] |= IRQLCDS;
309                 break;
310         case 1:
311                 hblclock = clock + evhblank.time;
312                 if(++ppuy == 154){
313                         ppuy = 0;
314                         ppustate = 2;
315                         if((reg[STAT] & IRQM2) != 0)
316                                 reg[IF] |= IRQLCDS;
317                         addevent(&evhblank, 80*2);
318                 }else
319                         addevent(&evhblank, 456*2);
320                 if((reg[STAT] & IRQLYC) != 0 && ppuy == reg[LYC])
321                         reg[IF] |= IRQLCDS;
322                 break;
323         case 2:
324                 oamsearch();
325                 rendclock = clock + evhblank.time;
326                 ppustate = 3;
327                 addevent(&evhblank, linelen());
328                 break;
329         case 3:
330                 ppusync();
331                 if(!done) print("not done?!\n");
332                 done = 0;
333                 if(scale > 1)
334                         lineexpand();
335                 ppustate = 0;
336                 if((reg[STAT] & IRQM0) != 0)
337                         reg[IF] |= IRQLCDS;
338                 t = hblclock + 456 * 2 - clock;
339                 addevent(&evhblank, t < 0 ? 456 * 2 : t);
340                 if(dma < 0)
341                         dma = 1;
342                 break;
343         }
344 }
345
346 void
347 ppuinit(void)
348 {
349         static char ppustack[4096];
350         
351         renderjmp[JMPBUFPC] = (uintptr)ppurender;
352         renderjmp[JMPBUFSP] = (uintptr)(ppustack + sizeof(ppustack) - 64);
353         myield();
354 }