]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/snes/ppu.c
games/snes: bg 3
[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 = 6 };
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 void
235 bg(int n, int nb, int prilo, int prihi)
236 {
237         static struct bg {
238                 u8int sz, szsh;
239                 u16int tx, ty, tnx, tny;
240                 u16int t;
241                 u32int c[2];
242                 int pal;
243                 u8int msz, mv, mx;
244         } bgs[4];
245         struct bg *p;
246         int v, sx, sy;
247
248         p = bgs + n;
249         if(rx == 0){
250                 p->szsh = (reg[BGMODE] & (1<<(4+n))) != 0 ? 4 : 3;
251                 p->sz = 1<<p->szsh;
252                 sx = hofs[n];
253                 sy = vofs[n] + ppuy;
254                 if(reg[MOSAIC] != 0 && (reg[MOSAIC] & (1<<n)) != 0){
255                         p->msz = (reg[MOSAIC] >> 4) + 1;
256                         if(p->msz != 1){
257                                 sx -= p->mx = sx % p->msz;
258                                 sy -= sy % p->msz;
259                         }
260                 }else
261                         p->msz = 1;
262         redo:
263                 p->tx = sx >> p->szsh;
264                 p->tnx = sx & (p->sz - 1);
265                 p->ty = sy >> p->szsh;
266                 p->tny = sy & (p->sz - 1);
267                 p->t = tile(n, p->tx, p->ty);
268                 chr(n, nb, p->sz, p->t, p->tnx, p->tny, p->c);
269                 p->pal = palette(n, p->t >> 10 & 7);
270                 if(p->tnx != 0)
271                         shift(p->c, nb, p->tnx, p->t & 0x4000);
272                 if(p->msz != 1 && p->mx != 0 && sx % p->msz == 0){
273                         p->mv = bgpixel(p->c, nb, p->t & 0x4000);
274                         if(p->tnx + p->mx >= 8){
275                                 sx += p->mx;
276                                 goto redo;
277                         }else if(p->mx > 1)
278                                 shift(p->c, nb, p->mx - 1, p->t & 0x4000);
279                 }
280         }
281         v = bgpixel(p->c, nb, p->t & 0x4000);
282         if(p->msz != 1)
283                 if(p->mx++ == 0)
284                         p->mv = v;
285                 else{
286                         if(p->mx == p->msz)
287                                 p->mx = 0;
288                         v = p->mv;
289                 }
290         if(v != 0)
291                 pixel(n, p->pal + v, (p->t & 0x2000) != 0 ? prihi : prilo);
292         if(++p->tnx == p->sz){
293                 p->tx++;
294                 p->tnx = 0;
295                 p->t = tile(n, p->tx, p->ty);
296                 p->pal = palette(n, p->t >> 10 & 7);
297         }
298         if((p->tnx & 7) == 0)
299                 chr(n, nb, p->sz, p->t, p->tnx, p->tny, p->c);
300 }
301
302 static void
303 bgs(void)
304 {
305         static int bitch[8];
306
307         switch(mode){
308         case 0:
309                 bg(0, 2, 0x80, 0xb0);
310                 bg(1, 2, 0x71, 0xa1);
311                 bg(2, 2, 0x22, 0x52);
312                 bg(3, 2, 0x13, 0x43);
313                 break;
314         case 1:
315                 bg(0, 4, 0x80, 0xb0);
316                 bg(1, 4, 0x71, 0xa1);
317                 bg(2, 2, 0x12, (reg[BGMODE] & 8) != 0 ? 0xd2 : 0x42);
318                 break;
319         case 3:
320                 bg(0, 8, 0x40, 0xa0);
321                 bg(1, 4, 0x11, 0x71);
322                 break;
323         default:
324                 if(bitch[mode]++ == 0)
325                         print("bg mode %d not implemented\n", mode);
326         }
327 }
328
329 static void
330 sprites(void)
331 {
332         static struct {
333                 short x;
334                 u8int y, i, c, sx, sy;
335                 u16int t0, t1;
336         } s[32], *sp;
337         static struct {
338                 short x;
339                 u8int sx, i, c, pal, pri;
340                 u32int *ch;
341         } t[32], *tp;
342         static u32int ch[34];
343         static u8int *p, q, over;
344         static int n, m;
345         static int *sz;
346         static int szs[] = {
347                 8, 8, 16, 16, 8, 8, 32, 32,
348                 8, 8, 64, 64, 16, 16, 32, 32,
349                 16, 16, 64, 64, 32, 32, 64, 64,
350                 16, 32, 32, 64, 16, 32, 32, 32
351         };
352         static u16int base[2];
353         u8int dy, v, col, pri0, pri1, prio;
354         u16int a;
355         u32int w, *cp;
356         int i, nt, dx;
357
358         if(rx == 0){
359                 n = 0;
360                 over = 1;
361                 sp = s;
362                 sz = szs + ((reg[OBSEL] & 0xe0) >> 3);
363                 base[0] = (reg[OBSEL] & 0x07) << 14;
364                 base[1] = base[0] + (((reg[OBSEL] & 0x18) + 8) << 10);
365         }
366         if((rx & 1) == 0){
367                 p = oam + 2 * rx;
368                 if(p[1] == 0xf0)
369                         goto nope;
370                 q = (oam[512 + (rx >> 3)] >> (rx & 6)) & 3;
371                 dy = ppuy - p[1];
372                 sp->sx = sz[q & 2];
373                 sp->sy = sz[(q & 2) + 1];
374                 if(dy >= sp->sy)
375                         goto nope;
376                 sp->x = p[0];
377                 if((q & 1) != 0)
378                         sp->x |= 0xff00;
379                 if(sp->x < -(short)sp->sx && sp->x != -256)
380                         goto nope;
381                 if(n == 32){
382                         over |= 0x40;
383                         goto nope;
384                 }
385                 sp->i = rx >> 1;
386                 sp->y = p[1];
387                 sp->c = p[3];
388                 sp->t0 = p[2] << 5;
389                 sp->t1 = base[sp->c & 1];
390                 sp++;
391                 n++;
392         }
393 nope:
394         if(ppuy != 0){
395                 col = 0;
396                 pri0 = 0;
397                 pri1 = 128;
398                 if((reg[OAMADDH] & 0x80) != 0)
399                         prio = oamaddr >> 2;
400                 else
401                         prio = 0;
402                 for(i = 0, tp = t; i < m; i++, tp++){
403                         dx = rx - tp->x;
404                         if(dx < 0 || dx >= tp->sx)
405                                 continue;
406                         w = *tp->ch;
407                         if((tp->c & 0x40) != 0){
408                                 v = w & 1 | w >> 7 & 2 | w >> 14 & 4 | w >> 21 & 8;
409                                 *tp->ch = w >> 1;
410                         }else{
411                                 v = w >> 7 & 1 | w >> 14 & 2 | w >> 21 & 4 | w >> 28 & 8;
412                                 *tp->ch = w << 1;
413                         }
414                         if((dx & 7) == 7)
415                                 tp->ch++;
416                         nt = (tp->i - prio) & 0x7f;
417                         if(v != 0 && nt < pri1){
418                                 col = tp->pal + v;
419                                 pri0 = tp->pri;
420                                 pri1 = nt;
421                         }
422                 }
423                 if(col > 0)
424                         pixel(OBJ, col, pri0);
425         }
426         if(rx == 255){
427                 cp = ch;
428                 m = n;
429                 for(sp = s + n - 1, tp = t + n - 1; sp >= s; sp--, tp--){
430                         tp->x = sp->x;
431                         tp->sx = 0;
432                         tp->c = sp->c;
433                         tp->pal = 0x80 | sp->c << 3 & 0x70;
434                         tp->pri = 3 * (0x10 + (sp->c & 0x30));
435                         if((tp->c & 8) != 0)
436                                 tp->pri |= OBJ;
437                         else
438                                 tp->pri |= OBJNC;
439                         tp->ch = cp;
440                         tp->i = sp->i;
441                         nt = sp->sx >> 3;
442                         dy = ppuy - sp->y;
443                         if((sp->c & 0x80) != 0)
444                                 dy = sp->sy - 1 - dy;
445                         a = sp->t0 | (dy & 7) << 1;
446                         if(dy >= 8)
447                                 a += (dy & ~7) << 6;
448                         if((sp->c & 0x40) != 0){
449                                 a += sp->sx * 4;
450                                 for(i = 0; i < nt; i++){
451                                         if(cp < ch + nelem(ch)){
452                                                 w  = vram[sp->t1 | (a -= 16) & 0x1fff] << 16;
453                                                 w |= vram[sp->t1 | (a + 1) & 0x1fff] << 24;
454                                                 w |= vram[sp->t1 | (a -= 16) & 0x1fff] << 0;
455                                                 w |= vram[sp->t1 | (a + 1) & 0x1fff] << 8;
456                                                 *cp++ = w;
457                                                 tp->sx += 8;
458                                         }else
459                                                 over |= 0x80;
460                                 }
461                         }else
462                                 for(i = 0; i < nt; i++){
463                                         if(cp < ch + nelem(ch)){
464                                                 w  = vram[sp->t1 | a & 0x1fff];
465                                                 w |= vram[sp->t1 | ++a & 0x1fff] << 8;
466                                                 w |= vram[sp->t1 | (a += 15) & 0x1fff] << 16;
467                                                 w |= vram[sp->t1 | ++a & 0x1fff] << 24;
468                                                 *cp++ = w;
469                                                 tp->sx += 8;
470                                                 a += 15;
471                                         }else
472                                                 over |= 0x80;
473                                 }
474                 }
475                 reg[0x213e] = over;
476         }
477 }
478
479 static u16int
480 colormath(void)
481 {
482         u16int v, w, r, g, b;
483         u8int m, m2;
484         int cw;
485         
486         m = reg[CGWSEL];
487         m2 = reg[CGADSUB];
488         cw = -1;
489         switch(m >> 6){
490         default: v = 1; break;
491         case 1: v = cw = window(COL); break;
492         case 2: v = !(cw = window(COL)); break;
493         case 3: v = 0; break;
494         }
495         if(v){
496                 if((pixelcol[0] & 0x10000) != 0)
497                         v = pixelcol[0];
498                 else
499                         v = cgram[pixelcol[0] & 0xff];
500         }
501         if((m2 & (1 << (pixelpri[0] & 0xf))) == 0)
502                 return v;
503         switch((m >> 4) & 3){
504         case 0: break;
505         case 1: if(cw < 0) cw = window(COL); if(!cw) return v; break;
506         case 2: if(cw < 0) cw = window(COL); if(cw) return v; break;
507         default: return v;
508         }
509         if((m & 2) != 0){
510                 if((pixelcol[1] & 0x10000) != 0)
511                         w = pixelcol[1];
512                 else
513                         w = cgram[pixelcol[1] & 0xff];
514         }else
515                 w = subcolor;
516         if((m2 & 0x80) != 0){
517                 r = (v & 0x7c00) - (w & 0x7c00);
518                 g = (v & 0x03e0) - (w & 0x03e0);
519                 b = (v & 0x001f) - (w & 0x001f);
520                 if((m2 & 0x40) != 0){
521                         r = (r >> 1) & 0xfc00;
522                         g = (g >> 1) & 0xffe0;
523                         b >>= 1;
524                 }
525                 if(r > 0x7c00) r = 0;
526                 if(g > 0x03e0) g = 0;
527                 if(b > 0x001f) b = 0;
528                 return r | g | b;
529         }else{
530                 r = (v & 0x7c00) + (w & 0x7c00);
531                 g = (v & 0x03e0) + (w & 0x03e0);
532                 b = (v & 0x001f) + (w & 0x001f);
533                 if((m2 & 0x40) != 0){
534                         r = (r >> 1) & 0xfc00;
535                         g = (g >> 1) & 0xffe0;
536                         b >>= 1;
537                 }
538                 if(r > 0x7c00) r = 0x7c00;
539                 if(g > 0x03e0) g = 0x03e0;
540                 if(b > 0x001f) b = 0x001f;
541                 return r | g | b;
542         }
543 }
544
545 void
546 ppustep(void)
547 {
548         int yvbl;
549
550         mode = reg[BGMODE] & 7;
551         bright = reg[INIDISP] & 0xf;
552         yvbl = (reg[SETINI] & OVERSCAN) != 0 ? 0xf0 : 0xe1;
553
554         if(ppux >= XLEFT && ppux <= XRIGHT && ppuy < 0xf0){
555                 rx = ppux - XLEFT;
556                 if(ppuy < yvbl && (reg[INIDISP] & 0x80) == 0){
557                         pixelcol[0] = 0;
558                         pixelpri[0] = COL;
559                         pixelcol[1] = 0x10000 | subcolor;
560                         pixelpri[1] = COL;      
561                         bgs();
562                         sprites();
563                         if(ppuy != 0)
564                                 pixeldraw(rx, ppuy - 1, colormath());
565                 }else if(ppuy != 0)
566                         pixeldraw(rx, ppuy - 1, ppuy >= yvbl ? 0x31c8 : 0);
567         }
568
569         if(ppux == 0x116 && ppuy <= yvbl)
570                 hdma |= reg[0x420c];
571         if((reg[NMITIMEN] & HCNTIRQ) != 0 && htime == ppux && ((reg[NMITIMEN] & VCNTIRQ) == 0 || vtime == ppuy))
572                 irq |= IRQPPU;
573         if(++ppux >= 340){
574                 ppux = 0;
575                 if(++ppuy >= 262){
576                         ppuy = 0;
577                         reg[RDNMI] &= ~VBLANK;
578                         hdma = reg[0x420c]<<8;
579                         flush();
580                 }
581                 if(ppuy == yvbl){
582                         reg[RDNMI] |= VBLANK;
583                         if((reg[NMITIMEN] & VBLANK) != 0)
584                                 nmi = 2;
585                         if((reg[NMITIMEN] & AUTOJOY) != 0){
586                                 memwrite(0x4016, 1);
587                                 memwrite(0x4016, 0);
588                                 reg[0x4218] = keylatch >> 16;
589                                 reg[0x4219] = keylatch >> 24;
590                                 keylatch = keylatch << 16 | 0xffff;
591                         }
592                 }
593                 if((reg[NMITIMEN] & (HCNTIRQ|VCNTIRQ)) == VCNTIRQ && vtime == ppuy)
594                         irq |= IRQPPU;
595         }
596 }