]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/snes/ppu.c
0ae2ccfb9b7f2c54e279b8d04fa86fbcc8fff89c
[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], hires;
9 static u32int pixelcol[2];
10 u16int vtime = 0x1ff, htime = 0x1ff, subcolor;
11 uchar pic[256*239*2*3];
12 u16int hofs[5], vofs[5];
13 s16int m7[6];
14
15 enum {
16         M7A,
17         M7B,
18         M7C,
19         M7D,
20         M7X,
21         M7Y
22 };
23
24 enum { OBJ = 4, COL = 5, OBJNC = 8 };
25
26 static u16int
27 darken(u16int v)
28 {
29         u8int r, g, b;
30         
31         r = (v >> 10) & 0x1f;
32         g = (v >> 5) & 0x1f;
33         b = v & 0x1f;
34         r = r * bright / 15;
35         g = g * bright / 15;
36         b = b * bright / 15;
37         return r << 10 | g << 5 | b;
38 }
39
40 static void
41 pixeldraw(int x, int y, u16int v, int s)
42 {
43         uchar *p;
44         u16int *q;
45         union { u16int w; u8int b[2]; } u;
46
47         if(bright != 0xf && s >= 0)
48                 v = darken(v);
49         if(scale == 1){
50                 p = pic + (x + y * 256) * 2;
51                 p[0] = v;
52                 p[1] = v >> 8;
53                 return;
54         }
55         u.b[0] = v;
56         u.b[1] = v >> 8;
57         if(scale == 2){
58                 q = (u16int*)pic + (x + y * 256) * 2;
59                 if(s < 1)
60                         q[0] = u.w;
61                 q[1] = u.w;
62         }else{
63                 q = (u16int*)pic + (x + y * 256) * 3;
64                 q[0] = u.w;
65                 q[1] = u.w;
66                 q[2] = u.w;
67         }
68 }
69
70 static int
71 window(int n)
72 {
73         int a, w1, w2;
74
75         a = reg[0x2123 + (n >> 1)];
76         if((n & 1) != 0)
77                 a >>= 4;
78         if((a & (WIN1|WIN2)) == 0)
79                 return 0;
80         w1 = rx >= reg[0x2126] && rx <= reg[0x2127];
81         w2 = rx >= reg[0x2128] && rx <= reg[0x2129];
82         if((a & INVW1) != 0)
83                 w1 = !w1;
84         if((a & INVW2) != 0)
85                 w2 = !w2;
86         if((a & (WIN1|WIN2)) != (WIN1|WIN2))
87                 return (a & WIN1) != 0 ? w1 : w2;
88         a = reg[0x212a + (n >> 2)] >> ((n & 3) << 1);
89         switch(a & 3){
90         case 1: return w1 & w2;
91         case 2: return w1 ^ w2;
92         case 3: return w1 ^ w2 ^ 1;
93         }
94         return w1 | w2;
95 }
96
97 static void
98 npixel(int n, int v, int pri)
99 {
100         int a;
101         
102         a = 1<<n;
103         if((reg[TM] & a) != 0 && pri > pixelpri[0] && ((reg[TMW] & a) == 0 || !window(n))){
104                 pixelcol[0] = v;
105                 pixelpri[0] = pri;
106         }
107         if((reg[TS] & a) != 0 && pri > pixelpri[1] && ((reg[TSW] & a) == 0 || !window(n))){
108                 pixelcol[1] = v;
109                 pixelpri[1] = pri;
110         }
111 }
112
113 static void
114 spixel(int n, int v, int pri)
115 {
116         int a;
117         
118         a = 1<<n;
119         if((reg[TS] & a) != 0 && pri > pixelpri[1] && ((reg[TSW] & a) == 0 || !window(n))){
120                 pixelcol[1] = v;
121                 pixelpri[1] = pri;
122         }
123 }
124
125 static void
126 mpixel(int n, int v, int pri)
127 {
128         int a;
129         
130         a = 1<<n;
131         if((reg[TM] & a) != 0 && pri > pixelpri[0] && ((reg[TMW] & a) == 0 || !window(n))){
132                 pixelcol[0] = v;
133                 pixelpri[0] = pri;
134         }
135 }
136
137 static void (*pixel)(int, int, int) = npixel;
138
139 static u16int
140 tile(int n, int tx, int ty)
141 {
142         int a;
143         u16int ta;
144         u16int t;
145
146         a = reg[0x2107 + n];
147         ta = ((a & ~3) << 9) + ((tx & 0x1f) << 1) + ((ty & 0x1f) << 6);
148         if((a & 1) != 0)
149                 ta += (tx & 0x20) << 6;
150         if((a & 2) != 0)
151                 ta += (ty & 0x20) << (6 + (a & 1));
152         t = vram[ta++];
153         return t | vram[ta] << 8;
154 }
155
156 static void
157 chr(int n, int nb, int w, int h, u16int t, int x, int y, u32int c[])
158 {
159         u16int a;
160
161         if(w == 16)
162                 t += (x >> 3 ^ t >> 14) & 1;
163         if(h == 16){
164                 if(y >= 8){
165                         t += (~t >> 11) & 16;
166                         y -= 8;
167                 }else
168                         t += (t >> 11) & 16;
169         }
170         if((t & 0x8000) != 0)
171                 y = 7 - y;
172         a = reg[0x210b + (n >> 1)];
173         if((n & 1) != 0)
174                 a >>= 4;
175         else
176                 a &= 0xf;
177         a = (a << 13) + (t & 0x3ff) * 8 * nb + y * 2;
178         c[0] = vram[a++];
179         c[0] |= vram[a] << 8;
180         if(nb != 2){
181                 a += 15;
182                 c[0] |= vram[a++] << 16;
183                 c[0] |= vram[a] << 24;
184                 if(nb == 8){
185                         a += 15;
186                         c[1] = vram[a++];
187                         c[1] |= vram[a] << 8;
188                         a += 15;
189                         c[1] |= vram[a++] << 16;
190                         c[1] |= vram[a] << 24;
191                 }
192         }
193 }
194
195 static int
196 palette(int n, int p)
197 {
198         switch(mode){
199         case 0:
200                 return p << 2 | n << 5;
201         case 1:
202                 if(n >= 2)
203                         return p << 2;
204         case 2:
205         case 6:
206                 return p << 4;
207         case 5:
208                 if(n == 0)
209                         return p << 4;
210                 return p << 2;
211         case 3:
212                 if(n != 0)
213                         return p << 4;
214         case 4:
215                 if(n != 0)
216                         return p << 2;
217                 if((reg[CGWSEL] & DIRCOL) != 0)
218                         return 0x10000;
219         }
220         return 0;
221 }
222
223 static void
224 shift(u32int *c, int nb, int n, int d)
225 {
226         if(d){
227                 c[0] >>= n;
228                 if(nb == 8)
229                         c[1] >>= n;
230         }else{
231                 c[0] <<= n;
232                 if(nb == 8)
233                         c[1] <<= n;
234         }
235 }
236
237 static u8int
238 bgpixel(u32int *c, int nb, int d)
239 {
240         u8int v;
241         
242         if(d){
243                 v = c[0] & 1 | c[0] >> 7 & 2;
244                 if(nb != 2){
245                         v |= c[0] >> 14 & 4 | c[0] >> 21 & 8;
246                         if(nb == 8){
247                                 v |= c[1] << 4 & 16 | c[1] >> 3 & 32 | c[1] >> 10 & 64 | c[1] >> 17 & 128;
248                                 c[1] >>= 1;
249                         }
250                 }
251                 c[0] >>= 1;
252         }else{
253                 v = c[0] >> 7 & 1 | c[0] >> 14 & 2;
254                 if(nb != 2){
255                         v |= c[0] >> 21 & 4 | c[0] >> 28 & 8;
256                         if(nb == 8){
257                                 v |= c[1] >> 3 & 16 | c[1] >> 10 & 32 | c[1] >> 17 & 64 | c[1] >> 24 & 128;
258                                 c[1] <<= 1;
259                         }
260                 }
261                 c[0] <<= 1;
262         }
263         return v;
264 }
265
266 static struct bgctxt {
267         u8int w, h, wsh, hsh, nb, pri[2];
268         u16int tx, ty, tnx, tny;
269         u16int t;
270         u32int c[2];
271         int pal;
272         u8int msz, mv, mx;
273         u16int otx, oty, otny;
274 } bgctxts[4];
275
276 static void
277 bginit(int n, int nb, int prilo, int prihi)
278 {
279         struct bgctxt *p;
280         int sx, sy;
281
282         p = bgctxts + n;
283         p->hsh = ((reg[BGMODE] & (1<<(4+n))) != 0) ? 4 : 3;
284         p->wsh = mode >= 5 ? 4 : p->hsh;
285         p->h = 1<<p->hsh;
286         p->w = 1<<p->wsh;
287         p->nb = nb;
288         p->pri[0] = prilo;
289         p->pri[1] = prihi;
290         sx = hofs[n];
291         sy = vofs[n] + ppuy;
292         if(reg[MOSAIC] != 0 && (reg[MOSAIC] & (1<<n)) != 0){
293                 p->msz = (reg[MOSAIC] >> 4) + 1;
294                 if(p->msz != 1){
295                         sx -= p->mx = sx % p->msz;
296                         sy -= sy % p->msz;
297                 }
298         }else
299                 p->msz = 1;
300         if(mode >= 5)
301                 sx <<= 1;
302 redo:
303         p->tx = sx >> p->wsh;
304         p->tnx = sx & (p->w - 1);
305         p->ty = sy >> p->hsh;
306         p->tny = sy & (p->h - 1);
307         p->t = tile(n, p->tx, p->ty);
308         chr(n, nb, p->w, p->h, p->t, p->tnx, p->tny, p->c);
309         p->pal = palette(n, p->t >> 10 & 7);
310         if((p->tnx & 7) != 0)
311                 shift(p->c, nb, p->tnx & 7, p->t & 0x4000);
312         if(p->msz != 1 && p->mx != 0 && sx % p->msz == 0){
313                 p->mv = bgpixel(p->c, nb, p->t & 0x4000);
314                 if(p->tnx + p->mx >= 8){
315                         sx += p->mx;
316                         goto redo;
317                 }else if(p->mx > 1)
318                         shift(p->c, nb, p->mx - 1, p->t & 0x4000);
319         }
320
321 }
322
323 static void
324 bg(int n)
325 {
326         struct bgctxt *p;
327         u8int v;
328
329         p = bgctxts + n;
330         v = bgpixel(p->c, p->nb, p->t & 0x4000);
331         if(p->msz != 1)
332                 if(p->mx++ == 0)
333                         p->mv = v;
334                 else{
335                         if(p->mx == p->msz)
336                                 p->mx = 0;
337                         v = p->mv;
338                 }
339         if(v != 0)
340                 pixel(n, p->pal + v, p->pri[(p->t & 0x2000) != 0]);
341         if(++p->tnx == p->w){
342                 p->tx++;
343                 p->tnx = 0;
344                 p->t = tile(n, p->tx, p->ty);
345                 p->pal = palette(n, p->t >> 10 & 7);
346         }
347         if((p->tnx & 7) == 0)
348                 chr(n, p->nb, p->w, p->h, p->t, p->tnx, p->tny, p->c);
349 }
350
351 static void
352 optinit(void)
353 {
354         struct bgctxt *p;
355         
356         p = bgctxts + 2;
357         p->hsh = (reg[BGMODE] & (1<<6)) != 0 ? 4 : 3;
358         p->wsh = mode >= 5 ? 4 : p->hsh;
359         p->w = 1<<p->wsh;
360         p->h = 1<<p->hsh;
361         p->tnx = hofs[2] & (p->w - 1);
362         p->tx = hofs[2] >> p->wsh;
363         bgctxts[0].otx = bgctxts[1].otx = 0xffff;
364         bgctxts[0].oty = bgctxts[0].ty;
365         bgctxts[0].otny = bgctxts[0].tny;
366         bgctxts[1].oty = bgctxts[1].ty;
367         bgctxts[1].otny = bgctxts[1].tny;
368 }
369
370 static void
371 opt(void)
372 {
373         struct bgctxt *p;
374         u16int hval, vval;
375         int sx, sy;
376         
377         p = bgctxts + 2;
378         if(++p->tnx == p->w){
379                 if(mode == 4){
380                         hval = tile(2, p->tx, vofs[2] >> p->hsh);
381                         if((hval & 0x8000) != 0){
382                                 vval = hval;
383                                 hval = 0;
384                         }else
385                                 vval = 0;
386                 }else{
387                         hval = tile(2, p->tx, vofs[2] >> p->hsh);
388                         vval = tile(2, p->tx, (vofs[2]+8) >> p->hsh);
389                 }
390                 sx = (rx & ~7) + (hval & 0x1ff8);
391                 sy = ppuy + (vval & 0x1fff);
392                 if((vval & 0x2000) != 0){
393                         bgctxts[0].oty = sy >> bgctxts[0].hsh;
394                         bgctxts[0].otny = sy & (bgctxts[0].h - 1);
395                 }else{
396                         bgctxts[0].oty = bgctxts[0].ty;
397                         bgctxts[0].otny = bgctxts[0].tny;
398                 }
399                 if((vval & 0x4000) != 0){
400                         bgctxts[1].oty = sy >> bgctxts[1].hsh;
401                         bgctxts[1].otny = sy & (bgctxts[1].h - 1);
402                 }else{
403                         bgctxts[1].oty = bgctxts[1].ty;
404                         bgctxts[1].otny = bgctxts[1].tny;
405                 }
406                 if((hval & 0x2000) != 0)
407                         bgctxts[0].otx = sx >> bgctxts[0].wsh;
408                 else
409                         bgctxts[0].otx = 0xffff;
410                 if((hval & 0x4000) != 0)
411                         bgctxts[1].otx = sx >> bgctxts[1].wsh;
412                 else
413                         bgctxts[1].otx = 0xffff;
414                 p->tnx = 0;
415                 p->tx++;
416         }
417 }
418
419 static void
420 bgopt(int n)
421 {
422         struct bgctxt *p;
423         u8int v;
424         
425         p = bgctxts + n;
426         v = bgpixel(p->c, p->nb, p->t & 0x4000);
427         if(p->msz != 1)
428                 if(p->mx++ == 0)
429                         p->mv = v;
430                 else{
431                         if(p->mx == p->msz)
432                                 p->mx = 0;
433                         v = p->mv;
434                 }
435         if(v != 0)
436                 pixel(n, p->pal + v, p->pri[(p->t & 0x2000) != 0]);
437         if(++p->tnx == p->w){
438                 p->tx++;
439                 p->tnx = 0;
440         }
441         if((p->tnx & 7) == 0){
442                 p->t = tile(n, p->otx == 0xffff ? p->tx : p->otx, p->oty);
443                 p->pal = palette(n, p->t >> 10 & 7);
444                 chr(n, p->nb, p->w, p->h, p->t, p->tnx, p->otny, p->c);
445         }
446 }
447
448 struct bg7ctxt {
449         int x, y, x0, y0;
450         u8int msz, mx, mv;
451 } b7[2];
452
453 void
454 calc7(void)
455 {
456         s16int t;
457
458         if((reg[0x2105] & 7) != 7)
459                 return;
460         t = hofs[4] - m7[M7X];
461         if((t & 0x2000) != 0)
462                 t |= ~0x3ff;
463         else
464                 t &= 0x3ff;
465         b7->x0 = (t * m7[M7A]) & ~63;
466         b7->y0 = (t * m7[M7C]) & ~63;
467         t = vofs[4] - m7[M7Y];
468         if((t & 0x2000) != 0)
469                 t |= ~0x3ff;
470         else
471                 t &= 0x3ff;
472         b7->x0 += (t * m7[M7B]) & ~63;
473         b7->y0 += (t * m7[M7D]) & ~63;
474         b7->x0 += m7[M7X] << 8;
475         b7->y0 += m7[M7Y] << 8;
476 }
477
478 static void
479 bg7init(int n)
480 {
481         u8int m, y;
482         struct bg7ctxt *p;
483         
484         p = b7 + n;
485         m = reg[M7SEL];
486         y = ppuy;
487         p->msz = 1;
488         if((reg[MOSAIC] & 1) != 0){
489                 p->msz = (reg[MOSAIC] >> 4) + 1;
490                 if(p->msz != 1)
491                         y -= y % p->msz;
492         }
493         if(n == 1)
494                 if((reg[MOSAIC] & 2) != 0)
495                         p->msz = (reg[MOSAIC] >> 4) + 1;
496                 else
497                         p->msz = 1;
498         if((m & 2) != 0)
499                 y = 255 - y;
500         p->x = b7->x0 + ((m7[M7B] * y) & ~63);
501         p->y = b7->y0 + ((m7[M7D] * y) & ~63);
502         if((m & 1) != 0){
503                 p->x += 255 * m7[M7A];
504                 p->y += 255 * m7[M7C];
505         }
506 }
507
508 static void
509 bg7(int n)
510 {
511         u16int x, y;
512         u8int m, v, t;
513         struct bg7ctxt *p;
514
515         p = b7 + n;
516         m = reg[M7SEL];
517         x = p->x >> 8;
518         y = p->y >> 8;
519         if((m & 0x80) == 0){
520                 x &= 1023;
521                 y &= 1023;
522         }else if(x > 1023 || y > 1023){
523                 if((m & 0x40) != 0){
524                         t = 0;
525                         goto lookup;
526                 }
527                 v = 0;
528                 goto end;
529         }
530         t = vram[x >> 2 & 0xfe | y << 5 & 0x7f00];
531 lookup:
532         v = vram[t << 7 | y << 4 & 0x70 | x << 1 & 0x0e | 1];
533 end:
534         if(p->msz != 1){
535                 if(p->mx == 0)
536                         p->mv = v;
537                 else
538                         v = p->mv;
539                 if(++p->mx == p->msz)
540                         p->mx = 0;
541         }
542         if(n == 1){
543                 if((v & 0x7f) != 0)
544                         if((v & 0x80) != 0)
545                                 pixel(1, v & 0x7f, 0x71);
546                         else
547                                 pixel(1, v, 0x11);
548         }else if(v != 0)
549                 pixel(0, v, 0x40);
550         if((m & 1) != 0){
551                 p->x -= m7[M7A];
552                 p->y -= m7[M7C];
553         }else{
554                 p->x += m7[M7A];
555                 p->y += m7[M7C];
556         }
557 }
558
559 static void
560 bgsinit(void)
561 {
562         static int bitch[8];
563
564         pixel = npixel;
565         switch(mode){
566         case 0:
567                 bginit(0, 2, 0x80, 0xb0);
568                 bginit(1, 2, 0x71, 0xa1);
569                 bginit(2, 2, 0x22, 0x52);
570                 bginit(3, 2, 0x13, 0x43);
571                 break;
572         case 1:
573                 bginit(0, 4, 0x80, 0xb0);
574                 bginit(1, 4, 0x71, 0xa1);
575                 bginit(2, 2, 0x12, (reg[BGMODE] & 8) != 0 ? 0xd2 : 0x42);
576                 break;
577         case 2:
578                 bginit(0, 4, 0x40, 0xa0);
579                 bginit(1, 4, 0x11, 0x71);
580                 optinit();
581                 break;
582         case 3:
583                 bginit(0, 8, 0x40, 0xa0);
584                 bginit(1, 4, 0x11, 0x71);
585                 break;
586         case 4:
587                 bginit(0, 8, 0x40, 0xa0);
588                 bginit(1, 2, 0x11, 0x71);
589                 optinit();
590                 break;
591         case 5:
592                 bginit(0, 4, 0x40, 0xa0);
593                 bginit(1, 2, 0x11, 0x71);
594                 break;
595         case 6:
596                 bginit(0, 4, 0x40, 0xa0);
597                 optinit();
598                 break;
599         case 7:
600                 bg7init(0);
601                 if((reg[SETINI] & EXTBG) != 0)
602                         bg7init(1);
603                 break;
604         default:
605                 bgctxts[0].w = bgctxts[1].w = 0;
606                 if(bitch[mode]++ == 0)
607                         print("bg mode %d not implemented\n", mode);
608         }
609 }
610
611 static void
612 bgs(void)
613 {
614         switch(mode){
615         case 0:
616                 bg(0);
617                 bg(1);
618                 bg(2);
619                 bg(3);
620                 break;
621         case 1:
622                 bg(0);
623                 bg(1);
624                 bg(2);
625                 break;
626         case 2:
627         case 4:
628                 opt();
629                 bgopt(0);
630                 bgopt(1);
631                 break;
632         case 3:
633                 bg(0);
634                 bg(1);
635                 break;
636         case 5:
637                 pixel = spixel;
638                 bg(0);
639                 bg(1);
640                 pixel = mpixel;
641                 bg(0);
642                 bg(1);
643                 pixel = npixel;
644                 break;
645         case 6:
646                 opt();
647                 pixel = spixel;
648                 bgopt(0);
649                 pixel = mpixel;
650                 bgopt(0);
651                 pixel = npixel;
652                 break;
653         case 7:
654                 bg7(0);
655                 if((reg[SETINI] & EXTBG) != 0)
656                         bg7(1);
657                 break;
658         }
659 }
660
661 static void
662 sprites(void)
663 {
664         static struct {
665                 short x;
666                 u8int y, i, c, sx, sy;
667                 u16int t0, t1;
668         } s[32], *sp;
669         static struct {
670                 short x;
671                 u8int sx, i, c, pal, pri;
672                 u32int *ch;
673         } t[32], *tp;
674         static u32int ch[34];
675         static u8int *p, q;
676         static int n, m;
677         static int *sz;
678         static int szs[] = {
679                 8, 8, 16, 16, 8, 8, 32, 32,
680                 8, 8, 64, 64, 16, 16, 32, 32,
681                 16, 16, 64, 64, 32, 32, 64, 64,
682                 16, 32, 32, 64, 16, 32, 32, 32
683         };
684         static u16int base[2];
685         u8int dy, v, col, pri0, pri1, prio;
686         u16int a;
687         u32int w, *cp;
688         int i, nt, dx;
689
690         if(rx == 0){
691                 n = 0;
692                 sp = s;
693                 sz = szs + ((reg[OBSEL] & 0xe0) >> 3);
694                 base[0] = (reg[OBSEL] & 0x07) << 14;
695                 base[1] = base[0] + (((reg[OBSEL] & 0x18) + 8) << 10);
696         }
697         if((rx & 1) == 0){
698                 p = oam + 2 * rx;
699                 if(p[1] == 0xf0)
700                         goto nope;
701                 q = (oam[512 + (rx >> 3)] >> (rx & 6)) & 3;
702                 dy = ppuy - p[1];
703                 sp->sx = sz[q & 2];
704                 sp->sy = sz[(q & 2) + 1];
705                 if(dy >= sp->sy)
706                         goto nope;
707                 sp->x = p[0];
708                 if((q & 1) != 0)
709                         sp->x |= 0xff00;
710                 if(sp->x <= -(short)sp->sx && sp->x != -256)
711                         goto nope;
712                 if(n == 32){
713                         reg[0x213e] |= 0x40;
714                         goto nope;
715                 }
716                 sp->i = rx >> 1;
717                 sp->y = p[1];
718                 sp->c = p[3];
719                 sp->t0 = p[2] << 5;
720                 sp->t1 = base[sp->c & 1];
721                 sp++;
722                 n++;
723         }
724 nope:
725         if(ppuy != 0){
726                 col = 0;
727                 pri0 = 0;
728                 pri1 = 128;
729                 if((reg[OAMADDH] & 0x80) != 0)
730                         prio = oamaddr >> 2;
731                 else
732                         prio = 0;
733                 for(i = 0, tp = t; i < m; i++, tp++){
734                         dx = rx - tp->x;
735                         if(dx < 0 || dx >= tp->sx)
736                                 continue;
737                         w = *tp->ch;
738                         if((tp->c & 0x40) != 0){
739                                 v = w & 1 | w >> 7 & 2 | w >> 14 & 4 | w >> 21 & 8;
740                                 *tp->ch = w >> 1;
741                         }else{
742                                 v = w >> 7 & 1 | w >> 14 & 2 | w >> 21 & 4 | w >> 28 & 8;
743                                 *tp->ch = w << 1;
744                         }
745                         if((dx & 7) == 7)
746                                 tp->ch++;
747                         nt = (tp->i - prio) & 0x7f;
748                         if(v != 0 && nt < pri1){
749                                 col = tp->pal + v;
750                                 pri0 = tp->pri;
751                                 pri1 = nt;
752                         }
753                 }
754                 if(col > 0)
755                         pixel(OBJ, col, pri0);
756         }
757         if(rx == 255){
758                 cp = ch;
759                 m = n;
760                 for(sp = s + n - 1, tp = t + n - 1; sp >= s; sp--, tp--){
761                         tp->x = sp->x;
762                         tp->sx = 0;
763                         tp->c = sp->c;
764                         tp->pal = 0x80 | sp->c << 3 & 0x70;
765                         tp->pri = 3 * (0x10 + (sp->c & 0x30));
766                         if((tp->c & 8) != 0)
767                                 tp->pri |= OBJ;
768                         else
769                                 tp->pri |= OBJNC;
770                         tp->ch = cp;
771                         tp->i = sp->i;
772                         nt = sp->sx >> 3;
773                         dy = ppuy - sp->y;
774                         if((sp->c & 0x80) != 0)
775                                 dy = sp->sy - 1 - dy;
776                         a = sp->t0 | (dy & 7) << 1;
777                         if(dy >= 8)
778                                 a += (dy & ~7) << 6;
779                         if(sp->x < 0 && (i = (-sp->x >> 3)) != 0){
780                                 if((sp->c & 0x40) != 0)
781                                         a -= i << 5;
782                                 else
783                                         a += i << 5;
784                                 nt -= i;
785                                 tp->sx += i << 3;
786                         }
787                         if((sp->c & 0x40) != 0){
788                                 a += sp->sx * 4;
789                                 for(i = 0; i < nt; i++){
790                                         if(cp < ch + nelem(ch)){
791                                                 w  = vram[sp->t1 | (a -= 16) & 0x1fff] << 16;
792                                                 w |= vram[sp->t1 | (a + 1) & 0x1fff] << 24;
793                                                 w |= vram[sp->t1 | (a -= 16) & 0x1fff] << 0;
794                                                 w |= vram[sp->t1 | (a + 1) & 0x1fff] << 8;
795                                                 *cp++ = w;
796                                                 tp->sx += 8;
797                                         }else
798                                                 reg[0x213e] |= 0x80;
799                                 }
800                         }else
801                                 for(i = 0; i < nt; i++){
802                                         if(cp < ch + nelem(ch)){
803                                                 w  = vram[sp->t1 | a & 0x1fff];
804                                                 w |= vram[sp->t1 | ++a & 0x1fff] << 8;
805                                                 w |= vram[sp->t1 | (a += 15) & 0x1fff] << 16;
806                                                 w |= vram[sp->t1 | ++a & 0x1fff] << 24;
807                                                 *cp++ = w;
808                                                 tp->sx += 8;
809                                                 a += 15;
810                                         }else
811                                                 reg[0x213e] |= 0x80;
812                                 }
813                         if(sp->x < 0 && (i = (-sp->x) & 7) != 0)
814                                 if((sp->c & 0x40) != 0)
815                                         *tp->ch >>= i;
816                                 else
817                                         *tp->ch <<= i;
818                 }
819         }
820 }
821
822 static u16int
823 colormath(int n)
824 {
825         u16int v, w, r, g, b;
826         u8int m, m2, div;
827         int cw;
828         
829         m = reg[CGWSEL];
830         m2 = reg[CGADSUB];
831         cw = -1;
832         switch(m >> 6){
833         default: v = 1; break;
834         case 1: v = cw = window(COL); break;
835         case 2: v = !(cw = window(COL)); break;
836         case 3: v = 0; break;
837         }
838         if(v){
839                 if((pixelcol[n] & 0x10000) != 0)
840                         v = pixelcol[n];
841                 else
842                         v = cgram[pixelcol[n] & 0xff];
843         }
844         if((m2 & (1 << (pixelpri[n] & 0xf))) == 0)
845                 return v;
846         switch((m >> 4) & 3){
847         case 0: break;
848         case 1: if(cw < 0) cw = window(COL); if(!cw) return v; break;
849         case 2: if(cw < 0) cw = window(COL); if(cw) return v; break;
850         default: return v;
851         }
852         div = (m2 & 0x40) != 0;
853         if((m & 2) != 0){
854                 if((pixelcol[1-n] & 0x10000) != 0)
855                         w = pixelcol[1-n];
856                 else
857                         w = cgram[pixelcol[1-n] & 0xff];
858                 div = div && (pixelpri[1-n] & 0xf) != COL;
859         }else
860                 w = subcolor;
861         if((m2 & 0x80) != 0){
862                 r = (v & 0x7c00) - (w & 0x7c00);
863                 g = (v & 0x03e0) - (w & 0x03e0);
864                 b = (v & 0x001f) - (w & 0x001f);
865                 if(r > 0x7c00) r = 0;
866                 if(g > 0x03e0) g = 0;
867                 if(b > 0x001f) b = 0;
868                 if(div){
869                         r = (r >> 1) & 0xfc00;
870                         g = (g >> 1) & 0xffe0;
871                         b >>= 1;
872                 }
873                 return r | g | b;
874         }else{
875                 r = (v & 0x7c00) + (w & 0x7c00);
876                 g = (v & 0x03e0) + (w & 0x03e0);
877                 b = (v & 0x001f) + (w & 0x001f);
878                 if(div){
879                         r = (r >> 1) & 0xfc00;
880                         g = (g >> 1) & 0xffe0;
881                         b >>= 1;
882                 }
883                 if(r > 0x7c00) r = 0x7c00;
884                 if(g > 0x03e0) g = 0x03e0;
885                 if(b > 0x001f) b = 0x001f;
886                 return r | g | b;
887         }
888 }
889
890 void
891 ppustep(void)
892 {
893         int yvbl;
894
895         mode = reg[BGMODE] & 7;
896         bright = reg[INIDISP] & 0xf;
897         hires = mode >= 5 && mode < 7 || (reg[SETINI] & 8) != 0;
898         yvbl = (reg[SETINI] & OVERSCAN) != 0 ? 0xf0 : 0xe1;
899
900         if(ppux >= XLEFT && ppux <= XRIGHT && ppuy < 0xf0){
901                 rx = ppux - XLEFT;
902                 if(ppuy < yvbl && (reg[INIDISP] & 0x80) == 0){
903                         pixelcol[0] = 0;
904                         pixelpri[0] = COL;
905                         pixelcol[1] = 0x10000 | subcolor;
906                         pixelpri[1] = COL;      
907                         bgs();
908                         sprites();
909                         if(ppuy != 0)
910                                 if(hires){
911                                         pixeldraw(rx, ppuy - 1, colormath(1), 0);
912                                         pixeldraw(rx, ppuy - 1, colormath(0), 1);
913                                 }else
914                                         pixeldraw(rx, ppuy - 1, colormath(0), 0);
915                 }else if(ppuy != 0)
916                         pixeldraw(rx, ppuy - 1, ppuy >= yvbl ? 0x6739 : 0, -1);
917         }
918
919         if(ppux == 134)
920                 cpupause = 1;
921         if(ppux == 0x116 && ppuy <= yvbl)
922                 hdma |= reg[0x420c];
923         if((reg[NMITIMEN] & HCNTIRQ) != 0 && htime+4 == ppux && ((reg[NMITIMEN] & VCNTIRQ) == 0 || vtime == ppuy))
924                 irq |= IRQPPU;
925         if(++ppux >= 340){
926                 ppux = 0;
927                 if(++ppuy >= 262){
928                         ppuy = 0;
929                         reg[RDNMI] &= ~VBLANK;
930                         reg[0x213e] = 1;
931                         reg[0x213f] ^= 0x80;
932                         hdma = reg[0x420c]<<8;
933                         flush();
934                 }
935                 if(ppuy < yvbl)
936                         bgsinit();
937                 if(ppuy == yvbl){
938                         reg[RDNMI] |= VBLANK;
939                         if((reg[NMITIMEN] & VBLANK) != 0)
940                                 nmi = 2;
941                         if((reg[INIDISP] & 0x80) == 0)
942                                 oamaddr = reg[0x2102] << 1 | (reg[0x2103] & 1) << 9;
943                         if((reg[NMITIMEN] & AUTOJOY) != 0){
944                                 memwrite(0x4016, 1);
945                                 memwrite(0x4016, 0);
946                                 reg[0x4218] = keylatch >> 16;
947                                 reg[0x4219] = keylatch >> 24;
948                                 keylatch = keylatch << 16 | 0xffff;
949                         }
950                 }
951                 if((reg[NMITIMEN] & (HCNTIRQ|VCNTIRQ)) == VCNTIRQ && vtime == ppuy)
952                         irq |= IRQPPU;
953         }
954 }