]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/snes/ppu.c
games/snes: mouse support
[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         default:
320                 if(bitch[mode]++ == 0)
321                         print("bg mode %d not implemented\n", mode);
322         }
323 }
324
325 static void
326 sprites(void)
327 {
328         static struct {
329                 short x;
330                 u8int y, i, c, sx, sy;
331                 u16int t0, t1;
332         } s[32], *sp;
333         static struct {
334                 short x;
335                 u8int sx, i, c, pal, pri;
336                 u32int *ch;
337         } t[32], *tp;
338         static u32int ch[34];
339         static u8int *p, q, over;
340         static int n, m;
341         static int *sz;
342         static int szs[] = {
343                 8, 8, 16, 16, 8, 8, 32, 32,
344                 8, 8, 64, 64, 16, 16, 32, 32,
345                 16, 16, 64, 64, 32, 32, 64, 64,
346                 16, 32, 32, 64, 16, 32, 32, 32
347         };
348         static u16int base[2];
349         u8int dy, v, col, pri0, pri1, prio;
350         u16int a;
351         u32int w, *cp;
352         int i, nt, dx;
353
354         if(rx == 0){
355                 n = 0;
356                 over = 1;
357                 sp = s;
358                 sz = szs + ((reg[OBSEL] & 0xe0) >> 3);
359                 base[0] = (reg[OBSEL] & 0x07) << 14;
360                 base[1] = base[0] + (((reg[OBSEL] & 0x18) + 8) << 10);
361         }
362         if((rx & 1) == 0){
363                 p = oam + 2 * rx;
364                 if(p[1] == 0xf0)
365                         goto nope;
366                 q = (oam[512 + (rx >> 3)] >> (rx & 6)) & 3;
367                 dy = ppuy - p[1];
368                 sp->sx = sz[q & 2];
369                 sp->sy = sz[(q & 2) + 1];
370                 if(dy >= sp->sy)
371                         goto nope;
372                 sp->x = p[0];
373                 if((q & 1) != 0)
374                         sp->x |= 0xff00;
375                 if(sp->x < -(short)sp->sx && sp->x != -256)
376                         goto nope;
377                 if(n == 32){
378                         over |= 0x40;
379                         goto nope;
380                 }
381                 sp->i = rx >> 1;
382                 sp->y = p[1];
383                 sp->c = p[3];
384                 sp->t0 = p[2] << 5;
385                 sp->t1 = base[sp->c & 1];
386                 sp++;
387                 n++;
388         }
389 nope:
390         if(ppuy != 0){
391                 col = 0;
392                 pri0 = 0;
393                 pri1 = 128;
394                 if((reg[OAMADDH] & 0x80) != 0)
395                         prio = oamaddr >> 2;
396                 else
397                         prio = 0;
398                 for(i = 0, tp = t; i < m; i++, tp++){
399                         dx = rx - tp->x;
400                         if(dx < 0 || dx >= tp->sx)
401                                 continue;
402                         w = *tp->ch;
403                         if((tp->c & 0x40) != 0){
404                                 v = w & 1 | w >> 7 & 2 | w >> 14 & 4 | w >> 21 & 8;
405                                 *tp->ch = w >> 1;
406                         }else{
407                                 v = w >> 7 & 1 | w >> 14 & 2 | w >> 21 & 4 | w >> 28 & 8;
408                                 *tp->ch = w << 1;
409                         }
410                         if((dx & 7) == 7)
411                                 tp->ch++;
412                         nt = (tp->i - prio) & 0x7f;
413                         if(v != 0 && nt < pri1){
414                                 col = tp->pal + v;
415                                 pri0 = tp->pri;
416                                 pri1 = nt;
417                         }
418                 }
419                 if(col > 0)
420                         pixel(OBJ, col, pri0);
421         }
422         if(rx == 255){
423                 cp = ch;
424                 m = n;
425                 for(sp = s + n - 1, tp = t + n - 1; sp >= s; sp--, tp--){
426                         tp->x = sp->x;
427                         tp->sx = 0;
428                         tp->c = sp->c;
429                         tp->pal = 0x80 | sp->c << 3 & 0x70;
430                         tp->pri = 3 * (0x10 + (sp->c & 0x30));
431                         if((tp->c & 8) != 0)
432                                 tp->pri |= OBJ;
433                         else
434                                 tp->pri |= OBJNC;
435                         tp->ch = cp;
436                         tp->i = sp->i;
437                         nt = sp->sx >> 3;
438                         dy = ppuy - sp->y;
439                         if((sp->c & 0x80) != 0)
440                                 dy = sp->sy - 1 - dy;
441                         a = sp->t0 | (dy & 7) << 1;
442                         if(dy >= 8)
443                                 a += (dy & ~7) << 6;
444                         if((sp->c & 0x40) != 0){
445                                 a += sp->sx * 4;
446                                 for(i = 0; i < nt; i++){
447                                         if(cp < ch + nelem(ch)){
448                                                 w  = vram[sp->t1 | (a -= 16) & 0x1fff] << 16;
449                                                 w |= vram[sp->t1 | (a + 1) & 0x1fff] << 24;
450                                                 w |= vram[sp->t1 | (a -= 16) & 0x1fff] << 0;
451                                                 w |= vram[sp->t1 | (a + 1) & 0x1fff] << 8;
452                                                 *cp++ = w;
453                                                 tp->sx += 8;
454                                         }else
455                                                 over |= 0x80;
456                                 }
457                         }else
458                                 for(i = 0; i < nt; i++){
459                                         if(cp < ch + nelem(ch)){
460                                                 w  = vram[sp->t1 | a & 0x1fff];
461                                                 w |= vram[sp->t1 | ++a & 0x1fff] << 8;
462                                                 w |= vram[sp->t1 | (a += 15) & 0x1fff] << 16;
463                                                 w |= vram[sp->t1 | ++a & 0x1fff] << 24;
464                                                 *cp++ = w;
465                                                 tp->sx += 8;
466                                                 a += 15;
467                                         }else
468                                                 over |= 0x80;
469                                 }
470                 }
471                 reg[0x213e] = over;
472         }
473 }
474
475 static u16int
476 colormath(void)
477 {
478         u16int v, w, r, g, b;
479         u8int m, m2;
480         int cw;
481         
482         m = reg[CGWSEL];
483         m2 = reg[CGADSUB];
484         cw = -1;
485         switch(m >> 6){
486         default: v = 1; break;
487         case 1: v = cw = window(COL); break;
488         case 2: v = !(cw = window(COL)); break;
489         case 3: v = 0; break;
490         }
491         if(v){
492                 if((pixelcol[0] & 0x10000) != 0)
493                         v = pixelcol[0];
494                 else
495                         v = cgram[pixelcol[0] & 0xff];
496         }
497         if((m2 & (1 << (pixelpri[0] & 0xf))) == 0)
498                 return v;
499         switch((m >> 4) & 3){
500         case 0: break;
501         case 1: if(cw < 0) cw = window(COL); if(!cw) return v; break;
502         case 2: if(cw < 0) cw = window(COL); if(cw) return v; break;
503         default: return v;
504         }
505         if((m & 2) != 0){
506                 if((pixelcol[1] & 0x10000) != 0)
507                         w = pixelcol[1];
508                 else
509                         w = cgram[pixelcol[1] & 0xff];
510         }else
511                 w = subcolor;
512         if((m2 & 0x80) != 0){
513                 r = (v & 0x7c00) - (w & 0x7c00);
514                 g = (v & 0x03e0) - (w & 0x03e0);
515                 b = (v & 0x001f) - (w & 0x001f);
516                 if((m2 & 0x40) != 0){
517                         r = (r >> 1) & 0xfc00;
518                         g = (g >> 1) & 0xffe0;
519                         b >>= 1;
520                 }
521                 if(r > 0x7c00) r = 0;
522                 if(g > 0x03e0) g = 0;
523                 if(b > 0x001f) b = 0;
524                 return r | g | b;
525         }else{
526                 r = (v & 0x7c00) + (w & 0x7c00);
527                 g = (v & 0x03e0) + (w & 0x03e0);
528                 b = (v & 0x001f) + (w & 0x001f);
529                 if((m2 & 0x40) != 0){
530                         r = (r >> 1) & 0xfc00;
531                         g = (g >> 1) & 0xffe0;
532                         b >>= 1;
533                 }
534                 if(r > 0x7c00) r = 0x7c00;
535                 if(g > 0x03e0) g = 0x03e0;
536                 if(b > 0x001f) b = 0x001f;
537                 return r | g | b;
538         }
539 }
540
541 void
542 ppustep(void)
543 {
544         int yvbl;
545
546         mode = reg[BGMODE] & 7;
547         bright = reg[INIDISP] & 0xf;
548         yvbl = (reg[SETINI] & OVERSCAN) != 0 ? 0xf0 : 0xe1;
549
550         if(ppux >= XLEFT && ppux <= XRIGHT && ppuy < 0xf0){
551                 rx = ppux - XLEFT;
552                 if(ppuy < yvbl && (reg[INIDISP] & 0x80) == 0){
553                         pixelcol[0] = 0;
554                         pixelpri[0] = COL;
555                         pixelcol[1] = 0x10000 | subcolor;
556                         pixelpri[1] = COL;      
557                         bgs();
558                         sprites();
559                         if(ppuy != 0)
560                                 pixeldraw(rx, ppuy - 1, colormath());
561                 }else if(ppuy != 0)
562                         pixeldraw(rx, ppuy - 1, ppuy >= yvbl ? 0x31c8 : 0);
563         }
564
565         if(ppux == 0x116 && ppuy <= yvbl)
566                 hdma |= reg[0x420c];
567         if((reg[NMITIMEN] & HCNTIRQ) != 0 && htime == ppux && ((reg[NMITIMEN] & VCNTIRQ) == 0 || vtime == ppuy))
568                 irq |= IRQPPU;
569         if(++ppux >= 340){
570                 ppux = 0;
571                 if(++ppuy >= 262){
572                         ppuy = 0;
573                         reg[RDNMI] &= ~VBLANK;
574                         hdma = reg[0x420c]<<8;
575                         flush();
576                 }
577                 if(ppuy == yvbl){
578                         reg[RDNMI] |= VBLANK;
579                         if((reg[NMITIMEN] & VBLANK) != 0)
580                                 nmi = 2;
581                         if((reg[NMITIMEN] & AUTOJOY) != 0){
582                                 memwrite(0x4016, 1);
583                                 memwrite(0x4016, 0);
584                                 reg[0x4218] = keylatch >> 16;
585                                 reg[0x4219] = keylatch >> 24;
586                                 keylatch = keylatch << 16 | 0xffff;
587                         }
588                 }
589                 if((reg[NMITIMEN] & (HCNTIRQ|VCNTIRQ)) == VCNTIRQ && vtime == ppuy)
590                         irq |= IRQPPU;
591         }
592 }