]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/snes/ppu.c
merge
[plan9front.git] / sys / src / games / snes / ppu.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include "dat.h"
5 #include "fns.h"
6
7 int ppux, ppuy, rx;
8 static u8int mode, bright, pixelpri[2];
9 static u32int pixelcol[2];
10 u16int vtime = 0x1ff, htime = 0x1ff, subcolor, mosatop;
11 uchar pic[256*239*2*9];
12 u16int m7[6], hofs[4], vofs[4];
13
14 enum { OBJ = 4, COL = 5, OBJNC = 8 };
15
16 static u16int
17 darken(u16int v)
18 {
19         u8int r, g, b;
20         
21         r = (v >> 10) & 0x1f;
22         g = (v >> 5) & 0x1f;
23         b = v & 0x1f;
24         r = r * bright / 15;
25         g = g * bright / 15;
26         b = b * bright / 15;
27         return r << 10 | g << 5 | b;
28 }
29
30 static void
31 pixeldraw(int x, int y, u16int v)
32 {
33         uchar *p;
34         u16int *q;
35         union { u16int w; u8int b[2]; } u;
36         int i;
37
38         if(bright != 0xf)
39                 v = darken(v);
40         if(scale == 1){
41                 p = pic + (x + y * 256) * 2;
42                 *p++ = v;
43                 *p = v >> 8;
44                 return;
45         }
46         u.b[0] = v;
47         u.b[1] = v >> 8;
48         if(scale == 2){
49                 q = (u16int*)pic + (x + y * 256 * 2) * 2;
50                 *q++ = u.w;
51                 *q = u.w;
52                 q += 256 * 2 - 1;
53                 *q++ = u.w;
54                 *q = u.w;
55         }else{
56                 q = (u16int*)pic + (x + y * 256 * 3) * 3;
57                 for(i = 0; i < 3; i++){
58                         *q++ = u.w;
59                         *q++ = u.w;
60                         *q = u.w;
61                         q += 256 * 3 - 2;
62                 }
63         }
64 }
65
66 static int
67 window(int n)
68 {
69         int a, w1, w2;
70
71         a = reg[0x2123 + (n >> 1)];
72         if((n & 1) != 0)
73                 a >>= 4;
74         if((a & (WIN1|WIN2)) == 0)
75                 return 0;
76         w1 = rx >= reg[0x2126] && rx <= reg[0x2127];
77         w2 = rx >= reg[0x2128] && rx <= reg[0x2129];
78         if((a & INVW1) != 0)
79                 w1 = !w1;
80         if((a & INVW2) != 0)
81                 w2 = !w2;
82         if((a & (WIN1|WIN2)) != (WIN1|WIN2))
83                 return (a & WIN1) != 0 ? w1 : w2;
84         a = reg[0x212a + (n >> 2)] >> ((n & 3) << 1);
85         switch(a & 3){
86         case 1: return w1 & w2;
87         case 2: return w1 ^ w2;
88         case 3: return w1 ^ w2 ^ 1;
89         }
90         return w1 | w2;
91 }
92
93 static void
94 pixel(int n, int v, int pri)
95 {
96         int a;
97         
98         a = 1<<n;
99         if((reg[TM] & a) != 0 && pri > pixelpri[0] && ((reg[TMW] & a) == 0 || !window(n))){
100                 pixelcol[0] = v;
101                 pixelpri[0] = pri;
102         }
103         if((reg[TS] & a) != 0 && pri > pixelpri[1] && ((reg[TSW] & a) == 0 || !window(n))){
104                 pixelcol[1] = v;
105                 pixelpri[1] = pri;
106         }
107 }
108
109 static u16int
110 tile(int n, int tx, int ty)
111 {
112         int a;
113         u16int ta;
114         u16int t;
115
116         a = reg[0x2107 + n];
117         ta = ((a & ~3) << 9) + ((tx & 0x1f) << 1) + ((ty & 0x1f) << 6);
118         if((a & 1) != 0)
119                 ta += (tx & 0x20) << 6;
120         if((a & 2) != 0)
121                 ta += (ty & 0x20) << (6 + (a & 1));
122         t = vram[ta++];
123         return t | vram[ta] << 8;
124 }
125
126 static void
127 chr(int n, int nb, int sz, u16int t, int x, int y, u32int c[])
128 {
129         u16int a;
130
131         if(sz == 16){
132                 if(y >= 8){
133                         t += ((x >> 3 ^ t >> 14) & 1) + ((~t >> 11) & 16);
134                         y -= 8;
135                 }else
136                         t += ((x >> 3 ^ t >> 14) & 1) + ((t >> 11) & 16);
137         }
138         if((t & 0x8000) != 0)
139                 y = 7 - y;
140         a = reg[0x210b + (n >> 1)];
141         if((n & 1) != 0)
142                 a >>= 4;
143         else
144                 a &= 0xf;
145         a = (a << 13) + (t & 0x3ff) * 8 * nb + y * 2;
146         c[0] = vram[a++];
147         c[0] |= vram[a] << 8;
148         if(nb != 2){
149                 a += 15;
150                 c[0] |= vram[a++] << 16;
151                 c[0] |= vram[a] << 24;
152                 if(nb == 8){
153                         a += 15;
154                         c[1] = vram[a++];
155                         c[1] |= vram[a] << 8;
156                         a += 15;
157                         c[1] |= vram[a++] << 16;
158                         c[1] |= vram[a] << 24;
159                 }
160         }
161 }
162
163 static int
164 palette(int n, int p)
165 {
166         switch(mode){
167         case 0:
168                 return p << 2 | n << 5;
169         case 1:
170                 if(n >= 2)
171                         return p << 2;
172         case 2:
173         case 6:
174                 return p << 4;
175         case 5:
176                 if(n == 0)
177                         return p << 4;
178                 return p << 2;
179         case 3:
180                 if(n != 0)
181                         return p << 4;
182         case 4:
183                 if(n != 0)
184                         return p << 2;
185                 if((reg[CGWSEL] & DIRCOL) != 0)
186                         return 0x10000;
187         }
188         return 0;
189 }
190
191 static void
192 shift(u32int *c, int nb, int n, int d)
193 {
194         if(d){
195                 c[0] >>= n;
196                 if(nb == 8)
197                         c[1] >>= n;
198         }else{
199                 c[0] <<= n;
200                 if(nb == 8)
201                         c[1] <<= n;
202         }
203 }
204
205 static u8int
206 bgpixel(u32int *c, int nb, int d)
207 {
208         u8int v;
209         
210         if(d){
211                 v = c[0] & 1 | c[0] >> 7 & 2;
212                 if(nb != 2){
213                         v |= c[0] >> 14 & 4 | c[0] >> 21 & 8;
214                         if(nb == 8){
215                                 v |= c[1] << 4 & 16 | c[1] >> 3 & 32 | c[1] >> 10 & 64 | c[1] >> 17 & 128;
216                                 c[1] >>= 1;
217                         }
218                 }
219                 c[0] >>= 1;
220         }else{
221                 v = c[0] >> 7 & 1 | c[0] >> 14 & 2;
222                 if(nb != 2){
223                         v |= c[0] >> 21 & 4 | c[0] >> 28 & 8;
224                         if(nb == 8){
225                                 v |= c[1] >> 3 & 16 | c[1] >> 10 & 32 | c[1] >> 17 & 64 | c[1] >> 24 & 128;
226                                 c[1] <<= 1;
227                         }
228                 }
229                 c[0] <<= 1;
230         }
231         return v;
232 }
233
234 static struct bgctxt {
235         u8int sz, szsh, nb, pri[2];
236         u16int tx, ty, tnx, tny;
237         u16int t;
238         u32int c[2];
239         int pal;
240         u8int msz, mv, mx;
241 } bgctxts[4];
242
243 static void
244 bginit(int n, int nb, int prilo, int prihi)
245 {
246         struct bgctxt *p;
247         int sx, sy;
248
249         p = bgctxts + n;
250         p->szsh = (reg[BGMODE] & (1<<(4+n))) != 0 ? 4 : 3;
251         p->sz = 1<<p->szsh;
252         p->nb = nb;
253         p->pri[0] = prilo;
254         p->pri[1] = prihi;
255         sx = hofs[n];
256         sy = vofs[n] + ppuy;
257         if(reg[MOSAIC] != 0 && (reg[MOSAIC] & (1<<n)) != 0){
258                 p->msz = (reg[MOSAIC] >> 4) + 1;
259                 if(p->msz != 1){
260                         sx -= p->mx = sx % p->msz;
261                         sy -= sy % p->msz;
262                 }
263         }else
264                 p->msz = 1;
265 redo:
266         p->tx = sx >> p->szsh;
267         p->tnx = sx & (p->sz - 1);
268         p->ty = sy >> p->szsh;
269         p->tny = sy & (p->sz - 1);
270         p->t = tile(n, p->tx, p->ty);
271         chr(n, nb, p->sz, p->t, p->tnx, p->tny, p->c);
272         p->pal = palette(n, p->t >> 10 & 7);
273         if(p->tnx != 0)
274                 shift(p->c, nb, p->tnx, p->t & 0x4000);
275         if(p->msz != 1 && p->mx != 0 && sx % p->msz == 0){
276                 p->mv = bgpixel(p->c, nb, p->t & 0x4000);
277                 if(p->tnx + p->mx >= 8){
278                         sx += p->mx;
279                         goto redo;
280                 }else if(p->mx > 1)
281                         shift(p->c, nb, p->mx - 1, p->t & 0x4000);
282         }
283
284 }
285
286 static void
287 bg(int n)
288 {
289         struct bgctxt *p;
290         u8int v;
291
292         p = bgctxts + n;
293         v = bgpixel(p->c, p->nb, p->t & 0x4000);
294         if(p->msz != 1)
295                 if(p->mx++ == 0)
296                         p->mv = v;
297                 else{
298                         if(p->mx == p->msz)
299                                 p->mx = 0;
300                         v = p->mv;
301                 }
302         if(v != 0)
303                 pixel(n, p->pal + v, p->pri[(p->t & 0x2000) != 0]);
304         if(++p->tnx == p->sz){
305                 p->tx++;
306                 p->tnx = 0;
307                 p->t = tile(n, p->tx, p->ty);
308                 p->pal = palette(n, p->t >> 10 & 7);
309         }
310         if((p->tnx & 7) == 0)
311                 chr(n, p->nb, p->sz, p->t, p->tnx, p->tny, p->c);
312 }
313
314 static void
315 bgsinit(void)
316 {
317         static int bitch[8];
318
319         switch(mode){
320         case 0:
321                 bginit(0, 2, 0x80, 0xb0);
322                 bginit(1, 2, 0x71, 0xa1);
323                 bginit(2, 2, 0x22, 0x52);
324                 bginit(3, 2, 0x13, 0x43);
325                 break;
326         case 1:
327                 bginit(0, 4, 0x80, 0xb0);
328                 bginit(1, 4, 0x71, 0xa1);
329                 bginit(2, 2, 0x12, (reg[BGMODE] & 8) != 0 ? 0xd2 : 0x42);
330                 break;
331         case 2:
332                 bginit(0, 4, 0x40, 0xa0);
333                 bginit(1, 4, 0x11, 0x71);
334                 break;
335         case 3:
336                 bginit(0, 8, 0x40, 0xa0);
337                 bginit(1, 4, 0x11, 0x71);
338                 break;
339         default:
340                 bgctxts[0].sz = bgctxts[1].sz = 0;
341                 if(bitch[mode]++ == 0)
342                         print("bg mode %d not implemented\n", mode);
343         }
344 }
345
346 static void
347 bgs(void)
348 {
349         switch(mode){
350         case 0:
351                 bg(0);
352                 bg(1);
353                 bg(2);
354                 bg(3);
355                 break;
356         case 1:
357                 bg(0);
358                 bg(1);
359                 bg(2);
360                 break;
361         case 2:
362         case 3:
363                 bg(0);
364                 bg(1);
365                 break;
366         }
367 }
368
369 static void
370 sprites(void)
371 {
372         static struct {
373                 short x;
374                 u8int y, i, c, sx, sy;
375                 u16int t0, t1;
376         } s[32], *sp;
377         static struct {
378                 short x;
379                 u8int sx, i, c, pal, pri;
380                 u32int *ch;
381         } t[32], *tp;
382         static u32int ch[34];
383         static u8int *p, q, over;
384         static int n, m;
385         static int *sz;
386         static int szs[] = {
387                 8, 8, 16, 16, 8, 8, 32, 32,
388                 8, 8, 64, 64, 16, 16, 32, 32,
389                 16, 16, 64, 64, 32, 32, 64, 64,
390                 16, 32, 32, 64, 16, 32, 32, 32
391         };
392         static u16int base[2];
393         u8int dy, v, col, pri0, pri1, prio;
394         u16int a;
395         u32int w, *cp;
396         int i, nt, dx;
397
398         if(rx == 0){
399                 n = 0;
400                 over = 1;
401                 sp = s;
402                 sz = szs + ((reg[OBSEL] & 0xe0) >> 3);
403                 base[0] = (reg[OBSEL] & 0x07) << 14;
404                 base[1] = base[0] + (((reg[OBSEL] & 0x18) + 8) << 10);
405         }
406         if((rx & 1) == 0){
407                 p = oam + 2 * rx;
408                 if(p[1] == 0xf0)
409                         goto nope;
410                 q = (oam[512 + (rx >> 3)] >> (rx & 6)) & 3;
411                 dy = ppuy - p[1];
412                 sp->sx = sz[q & 2];
413                 sp->sy = sz[(q & 2) + 1];
414                 if(dy >= sp->sy)
415                         goto nope;
416                 sp->x = p[0];
417                 if((q & 1) != 0)
418                         sp->x |= 0xff00;
419                 if(sp->x < -(short)sp->sx && sp->x != -256)
420                         goto nope;
421                 if(n == 32){
422                         over |= 0x40;
423                         goto nope;
424                 }
425                 sp->i = rx >> 1;
426                 sp->y = p[1];
427                 sp->c = p[3];
428                 sp->t0 = p[2] << 5;
429                 sp->t1 = base[sp->c & 1];
430                 sp++;
431                 n++;
432         }
433 nope:
434         if(ppuy != 0){
435                 col = 0;
436                 pri0 = 0;
437                 pri1 = 128;
438                 if((reg[OAMADDH] & 0x80) != 0)
439                         prio = oamaddr >> 2;
440                 else
441                         prio = 0;
442                 for(i = 0, tp = t; i < m; i++, tp++){
443                         dx = rx - tp->x;
444                         if(dx < 0 || dx >= tp->sx)
445                                 continue;
446                         w = *tp->ch;
447                         if((tp->c & 0x40) != 0){
448                                 v = w & 1 | w >> 7 & 2 | w >> 14 & 4 | w >> 21 & 8;
449                                 *tp->ch = w >> 1;
450                         }else{
451                                 v = w >> 7 & 1 | w >> 14 & 2 | w >> 21 & 4 | w >> 28 & 8;
452                                 *tp->ch = w << 1;
453                         }
454                         if((dx & 7) == 7)
455                                 tp->ch++;
456                         nt = (tp->i - prio) & 0x7f;
457                         if(v != 0 && nt < pri1){
458                                 col = tp->pal + v;
459                                 pri0 = tp->pri;
460                                 pri1 = nt;
461                         }
462                 }
463                 if(col > 0)
464                         pixel(OBJ, col, pri0);
465         }
466         if(rx == 255){
467                 cp = ch;
468                 m = n;
469                 for(sp = s + n - 1, tp = t + n - 1; sp >= s; sp--, tp--){
470                         tp->x = sp->x;
471                         tp->sx = 0;
472                         tp->c = sp->c;
473                         tp->pal = 0x80 | sp->c << 3 & 0x70;
474                         tp->pri = 3 * (0x10 + (sp->c & 0x30));
475                         if((tp->c & 8) != 0)
476                                 tp->pri |= OBJ;
477                         else
478                                 tp->pri |= OBJNC;
479                         tp->ch = cp;
480                         tp->i = sp->i;
481                         nt = sp->sx >> 3;
482                         dy = ppuy - sp->y;
483                         if((sp->c & 0x80) != 0)
484                                 dy = sp->sy - 1 - dy;
485                         a = sp->t0 | (dy & 7) << 1;
486                         if(dy >= 8)
487                                 a += (dy & ~7) << 6;
488                         if((sp->c & 0x40) != 0){
489                                 a += sp->sx * 4;
490                                 for(i = 0; i < nt; i++){
491                                         if(cp < ch + nelem(ch)){
492                                                 w  = vram[sp->t1 | (a -= 16) & 0x1fff] << 16;
493                                                 w |= vram[sp->t1 | (a + 1) & 0x1fff] << 24;
494                                                 w |= vram[sp->t1 | (a -= 16) & 0x1fff] << 0;
495                                                 w |= vram[sp->t1 | (a + 1) & 0x1fff] << 8;
496                                                 *cp++ = w;
497                                                 tp->sx += 8;
498                                         }else
499                                                 over |= 0x80;
500                                 }
501                         }else
502                                 for(i = 0; i < nt; i++){
503                                         if(cp < ch + nelem(ch)){
504                                                 w  = vram[sp->t1 | a & 0x1fff];
505                                                 w |= vram[sp->t1 | ++a & 0x1fff] << 8;
506                                                 w |= vram[sp->t1 | (a += 15) & 0x1fff] << 16;
507                                                 w |= vram[sp->t1 | ++a & 0x1fff] << 24;
508                                                 *cp++ = w;
509                                                 tp->sx += 8;
510                                                 a += 15;
511                                         }else
512                                                 over |= 0x80;
513                                 }
514                 }
515                 reg[0x213e] = over;
516         }
517 }
518
519 static u16int
520 colormath(void)
521 {
522         u16int v, w, r, g, b;
523         u8int m, m2, div;
524         int cw;
525         
526         m = reg[CGWSEL];
527         m2 = reg[CGADSUB];
528         cw = -1;
529         switch(m >> 6){
530         default: v = 1; break;
531         case 1: v = cw = window(COL); break;
532         case 2: v = !(cw = window(COL)); break;
533         case 3: v = 0; break;
534         }
535         if(v){
536                 if((pixelcol[0] & 0x10000) != 0)
537                         v = pixelcol[0];
538                 else
539                         v = cgram[pixelcol[0] & 0xff];
540         }
541         if((m2 & (1 << (pixelpri[0] & 0xf))) == 0)
542                 return v;
543         switch((m >> 4) & 3){
544         case 0: break;
545         case 1: if(cw < 0) cw = window(COL); if(!cw) return v; break;
546         case 2: if(cw < 0) cw = window(COL); if(cw) return v; break;
547         default: return v;
548         }
549         div = (m2 & 0x40) != 0;
550         if((m & 2) != 0){
551                 if((pixelcol[1] & 0x10000) != 0)
552                         w = pixelcol[1];
553                 else
554                         w = cgram[pixelcol[1] & 0xff];
555                 div = div && (pixelpri[1] & 0xf) != COL;
556         }else
557                 w = subcolor;
558         if((m2 & 0x80) != 0){
559                 r = (v & 0x7c00) - (w & 0x7c00);
560                 g = (v & 0x03e0) - (w & 0x03e0);
561                 b = (v & 0x001f) - (w & 0x001f);
562                 if(r > 0x7c00) r = 0;
563                 if(g > 0x03e0) g = 0;
564                 if(b > 0x001f) b = 0;
565                 if(div){
566                         r = (r >> 1) & 0xfc00;
567                         g = (g >> 1) & 0xffe0;
568                         b >>= 1;
569                 }
570                 return r | g | b;
571         }else{
572                 r = (v & 0x7c00) + (w & 0x7c00);
573                 g = (v & 0x03e0) + (w & 0x03e0);
574                 b = (v & 0x001f) + (w & 0x001f);
575                 if(div){
576                         r = (r >> 1) & 0xfc00;
577                         g = (g >> 1) & 0xffe0;
578                         b >>= 1;
579                 }
580                 if(r > 0x7c00) r = 0x7c00;
581                 if(g > 0x03e0) g = 0x03e0;
582                 if(b > 0x001f) b = 0x001f;
583                 return r | g | b;
584         }
585 }
586
587 void
588 ppustep(void)
589 {
590         int yvbl;
591
592         mode = reg[BGMODE] & 7;
593         bright = reg[INIDISP] & 0xf;
594         yvbl = (reg[SETINI] & OVERSCAN) != 0 ? 0xf0 : 0xe1;
595
596         if(ppux >= XLEFT && ppux <= XRIGHT && ppuy < 0xf0){
597                 rx = ppux - XLEFT;
598                 if(ppuy < yvbl && (reg[INIDISP] & 0x80) == 0){
599                         pixelcol[0] = 0;
600                         pixelpri[0] = COL;
601                         pixelcol[1] = 0x10000 | subcolor;
602                         pixelpri[1] = COL;      
603                         bgs();
604                         sprites();
605                         if(ppuy != 0)
606                                 pixeldraw(rx, ppuy - 1, colormath());
607                 }else if(ppuy != 0)
608                         pixeldraw(rx, ppuy - 1, ppuy >= yvbl ? 0x31c8 : 0);
609         }
610
611         if(ppux == 0x116 && ppuy <= yvbl)
612                 hdma |= reg[0x420c];
613         if((reg[NMITIMEN] & HCNTIRQ) != 0 && htime == ppux && ((reg[NMITIMEN] & VCNTIRQ) == 0 || vtime == ppuy))
614                 irq |= IRQPPU;
615         if(++ppux >= 340){
616                 ppux = 0;
617                 if(++ppuy >= 262){
618                         ppuy = 0;
619                         reg[RDNMI] &= ~VBLANK;
620                         hdma = reg[0x420c]<<8;
621                         flush();
622                 }
623                 if(ppuy < yvbl)
624                         bgsinit();
625                 if(ppuy == yvbl){
626                         reg[RDNMI] |= VBLANK;
627                         if((reg[NMITIMEN] & VBLANK) != 0)
628                                 nmi = 2;
629                         if((reg[NMITIMEN] & AUTOJOY) != 0){
630                                 memwrite(0x4016, 1);
631                                 memwrite(0x4016, 0);
632                                 reg[0x4218] = keylatch >> 16;
633                                 reg[0x4219] = keylatch >> 24;
634                                 keylatch = keylatch << 16 | 0xffff;
635                         }
636                 }
637                 if((reg[NMITIMEN] & (HCNTIRQ|VCNTIRQ)) == VCNTIRQ && vtime == ppuy)
638                         irq |= IRQPPU;
639         }
640 }