]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/gba/ppu.c
games/gba: fix dma repeat bit and eeprom addresses
[plan9front.git] / sys / src / games / gba / 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;
8 uchar pic[240*160*2*3*3];
9 u8int bldy, blda, bldb;
10
11 typedef struct bg bg;
12 struct bg {
13         uchar n;
14         uchar depth;
15
16         s32int rpx0, rpy0, rpx, rpy;
17         s32int sx, sy;
18         
19         u16int tx, ty;
20         u8int tnx, tny;
21         u16int t;
22         u8int *chr;
23         u16int *pal;
24 };
25 static u8int mode=-1;
26 static bg bgst[4] = {{.n = 0}, {.n = 1}, {.n = 2}, {.n = 3}};
27 static u32int pixeldat[2], pixelpri[2];
28 static u16int bgmask;
29 static u8int objwin, objtrans;
30
31 typedef struct sprite sprite;
32 struct sprite {
33         uchar w, wb, h;
34         s16int x;
35         uchar ysh;
36         
37         uchar *base;
38         u16int *pal;
39         u16int inc;
40
41         u32int t0;
42         u16int t1;
43         uchar depth;
44         
45         s32int rx, ry;
46         s16int dx, dy;
47 };
48 static sprite sprt[128], *sp = sprt;
49 enum {
50         SPRROT = 1<<8,
51         SPRDIS = 1<<9,
52         SPRDOUB = 1<<9,
53         SPR8 = 1<<13,
54         SPRWIDE = 1<<14,
55         SPRTALL = 1<<15,
56         SPRHFLIP = 1<<28,
57         SPRVFLIP = 1<<29,
58         SPRSIZE0 = 1<<30,
59         SPRSIZE1 = 1<<31
60 };
61
62 void
63 pixeldraw(int x, int y, u16int v)
64 {
65         uchar *p;
66         u16int *q;
67         union { u16int w; u8int b[2]; } u;
68
69         if(scale == 1){
70                 p = pic + (x + y * 240) * 2;
71                 p[0] = v;
72                 p[1] = v >> 8;
73                 return;
74         }
75         u.b[0] = v;
76         u.b[1] = v >> 8;
77         if(scale == 2){
78                 q = (u16int*)pic + (x + y * 240) * 2;
79                 q[0] = u.w;
80                 q[1] = u.w;
81         }else{
82                 q = (u16int*)pic + (x + y * 240) * 3;
83                 q[0] = u.w;
84                 q[1] = u.w;
85                 q[2] = u.w;
86         }
87 }
88
89 void
90 pixel(u16int c, int n, int p)
91 {
92         if(p < pixelpri[0]){
93                 pixeldat[1] = pixeldat[0];
94                 pixelpri[1] = pixelpri[0];
95                 pixelpri[0] = p;
96                 pixeldat[0] = c | n << 16;
97         }else if(p < pixelpri[1]){
98                 pixelpri[1] = p;
99                 pixeldat[1] = c | n << 16;
100         }
101 }
102
103 void
104 tile(bg *b)
105 {
106         u16int bgcnt, ta, tx, ty, y, t;
107         u8int d;
108         u8int *chr;
109         
110         bgcnt = reg[BG0CNT + b->n];
111         d = bgcnt >> 7 & 1;
112         tx = b->tx;
113         ty = b->ty;
114         ta = (bgcnt << 3 & 0xf800) + ((tx & 0x1f) << 1) + ((ty & 0x1f) << 6);
115         switch(bgcnt >> 14){
116         case 1: ta += tx << 6 & 0x800; break;
117         case 2: ta += ty << 6 & 0x800; break;
118         case 3: ta += tx << 6 & 0x800 | ty << 7 & 0x1000; break;
119         }
120         t = vram[ta] | vram[ta+1] << 8;
121         b->t = t;
122         chr = vram + (bgcnt << 12 & 0xc000) + ((t & 0x3ff) << 5+d);
123         y = b->tny;
124         if((t & 1<<11) != 0)
125                 y ^= 7;
126         chr = chr + (y << 2+d);
127         b->chr = chr;
128         if(d != 0)
129                 b->pal = pram;
130         else
131                 b->pal = pram + (t >> 8 & 0xf0);
132 }
133
134 void
135 bginit(bg *b, int scal, int)
136 {
137         u16int cnt, x, y;
138         u16int *rr;
139         
140         cnt = reg[DISPCNT];
141         if(scal){
142                 rr = reg + (b->n - 2 << 3);
143                 if(ppuy == 0){
144                         b->rpx0 = (s32int)(rr[BG2XL] | rr[BG2XH] << 16) << 4 >> 4;
145                         b->rpy0 = (s32int)(rr[BG2YL] | rr[BG2YH] << 16) << 4 >> 4;
146                 }
147                 b->rpx = b->rpx0;
148                 b->rpy = b->rpy0;
149                 b->rpx0 += (s16int)rr[BG2PB];
150                 b->rpy0 += (s16int)rr[BG2PD];
151                 switch(cnt & 7){
152                 case 3:
153                 case 4:
154                         b->sx = 240 << 8;
155                         b->sy = 160 << 8;
156                         b->depth = (cnt & 7) == 3;
157                         break;
158                 case 5:
159                         b->sx = 160 << 8;
160                         b->sy = 128 << 8;
161                         b->depth = 1;
162                         break;
163                 }
164         }else{
165                 rr = reg + (b->n << 1);
166                 x = rr[BG0HOFS] & 0x1ff;
167                 y = (rr[BG0VOFS] & 0x1ff) + ppuy;
168                 b->tx = x >> 3;
169                 b->ty = y >> 3;
170                 b->tnx = x & 7;
171                 b->tny = y & 7;
172                 tile(b);
173         }
174 }
175
176 void
177 bgsinit(void)
178 {
179         mode = reg[DISPCNT] & 7;
180         switch(mode){
181         case 0:
182                 bginit(&bgst[0], 0, 0);
183                 bginit(&bgst[1], 0, 0);
184                 bginit(&bgst[2], 0, 0);
185                 bginit(&bgst[3], 0, 0);
186                 break;
187         case 1:
188                 bginit(&bgst[0], 0, 0);
189                 bginit(&bgst[1], 0, 0);
190                 bginit(&bgst[2], 1, 0);
191                 break;
192         case 2:
193                 bginit(&bgst[2], 1, 0);
194                 bginit(&bgst[3], 1, 0);
195                 break;
196         case 3:
197         case 4:
198         case 5:
199                 bginit(&bgst[2], 1, 1);
200                 break;
201         }       
202 }
203
204 void
205 bitbg(bg *b)
206 {
207         u16int cnt;
208         int v;
209         uchar *p;
210         u16int *rr;
211         uchar *base;
212         
213         cnt = reg[DISPCNT];
214         rr = reg - 8 + (b->n << 3);
215         if((bgmask & 1<<b->n) == 0)
216                 goto next;
217         if(b->rpx >= 0 && b->rpy >= 0 && b->rpx <= b->sx && b->rpy <= b->sy){
218                 base = vram;
219                 if((cnt & FRAME) != 0 && (cnt & 7) != 3)
220                         base += 0xa000;
221                 if(b->depth){
222                         p = base + 2 * (b->rpx >> 8) + 480 * (b->rpy >> 8);
223                         v = p[0] | p[1] << 8;
224                 }else{
225                         v = base[(b->rpx >> 8) + 240 * (b->rpy >> 8)];
226                         if(v != 0)
227                                 v = pram[v];
228                         else
229                                 v = -1;
230                 }
231         }else
232                 v = -1;
233         if(v >= 0)
234                 pixel(v, b->n, reg[BG0CNT + b->n] & 3);
235 next:
236         b->rpx += (s16int) rr[BG2PA];
237         b->rpy += (s16int) rr[BG2PC];
238 }
239
240 void
241 rotbg(bg *b)
242 {
243         u16int *rr, ta;
244         u16int bgcnt;
245         int row, sz, x, y;
246         uchar *p, v;
247
248         rr = reg - 8 + (b->n << 3);
249         if((bgmask & 1<<b->n) == 0)
250                 goto next;
251         bgcnt = reg[BG0CNT + b->n];
252         row = (bgcnt >> 14) + 4;
253         sz = 1 << 3 + row;
254         x = b->rpx >> 8;
255         y = b->rpy >> 8;
256         if((bgcnt & DISPWRAP) != 0){
257                 x &= sz - 1;
258                 y &= sz - 1;
259         }else if((uint)x >= sz || (uint)y >= sz)
260                 goto next;
261         ta = (bgcnt << 3 & 0xf800) + ((y >> 3) << row) + (x >> 3);
262         p = vram + (bgcnt << 12 & 0xc000) + (vram[ta] << 6);
263         p += (x & 7) + ((y & 7) << 3);
264         if((v = *p) != 0)
265                 pixel(pram[v], b->n, bgcnt & 3);
266 next:
267         b->rpx += (s16int) rr[BG2PA];
268         b->rpy += (s16int) rr[BG2PC];
269 }
270
271 void
272 txtbg(bg *b)
273 {
274         u16int bgcnt;
275         u8int x, v;
276
277         bgcnt = reg[BG0CNT + b->n];
278         if((bgmask & 1<<b->n) == 0)
279                 goto next;
280         x = b->tnx;
281         if((b->t & 1<<10) != 0)
282                 x ^= 7;
283         if((bgcnt & BG8) != 0)
284                 v = b->chr[x];
285         else{
286                 v = b->chr[x>>1];
287                 if((x & 1) != 0)
288                         v >>= 4;
289                 else
290                         v &= 0xf;
291         }
292         if(v != 0)
293                 pixel(b->pal[v], b->n, bgcnt & 3);
294 next:
295         if(++b->tnx == 8){
296                 b->tnx = 0;
297                 b->tx++;
298                 tile(b);
299         }
300 }
301
302 void
303 bgs(void)
304 {
305         switch(mode){
306         case 0:
307                 txtbg(&bgst[0]);
308                 txtbg(&bgst[1]);
309                 txtbg(&bgst[2]);
310                 txtbg(&bgst[3]);
311                 break;
312         case 1:
313                 txtbg(&bgst[0]);
314                 txtbg(&bgst[1]);
315                 rotbg(&bgst[2]);
316                 break;
317         case 2:
318                 rotbg(&bgst[2]);
319                 rotbg(&bgst[3]);
320                 break;
321         case 3:
322         case 4:
323         case 5:
324                 bitbg(&bgst[2]);
325                 break;
326         }
327 }
328
329 void
330 sprinit(void)
331 {
332         u16int *p, *pp;
333         u16int cnt, t1;
334         u32int t0;
335         int budg;
336         uchar ws, h, hb, d, dy, s;
337         static uchar wss[16] = {3, 4, 5, 6, 4, 5, 5, 6, 3, 3, 4, 5};
338         static uchar hss[16] = {3, 4, 5, 6, 3, 3, 4, 5, 4, 5, 5, 6};
339
340         sp = sprt;
341         cnt = reg[DISPCNT];
342         budg = (cnt & HBLFREE) != 0 ? 954 : 1210;
343         for(p = oam; p < oam + 512; p += 4){
344                 t0 = p[0];
345                 if((t0 & (SPRROT|SPRDIS)) == SPRDIS)
346                         continue;
347                 t0 |= p[1] << 16;
348                 s = t0 >> 30 & 3 | t0 >> 12 & 12;
349                 hb = h = 1 << hss[s];
350                 dy = ppuy - (u8int) t0;
351                 if((t0 & (SPRROT|SPRDOUB)) == (SPRROT|SPRDOUB))
352                         hb <<= 1;
353                 if(dy >= hb || (u8int)t0 + hb > 256 && ppuy + 256 - (u8int)t0 >= hb)
354                         continue;
355                 sp->x = (s32int)(t0 << 7) >> 23;
356                 sp->t0 = t0;
357                 ws = wss[s];
358                 sp->wb = sp->w = 1<<ws;
359                 sp->h = h;
360                 sp->t1 = t1 = p[2];
361                 sp->base = vram + 0x10000 + ((t1 & 0x3ff) << 5);
362                 d = (t0 & SPR8) != 0;
363                 sp->ysh = (cnt & OBJNOMAT) != 0 ? 2 + d + ws : 10;
364                 if((t0 & SPRROT) != 0){
365                         if((t0 & SPRDOUB) != 0)
366                                 sp->wb <<= 1;
367                         budg -= 10 + sp->w*2;
368                         pp = oam + 3 + (t0 >> 21 & 0x1f0);
369                         sp->dx = pp[0];
370                         sp->dy = pp[8];
371                         sp->rx = (dy - hb/2) * (s16int) pp[4] + (sp->w << 7) - sp->dx * sp->wb/2;
372                         sp->ry = (dy - hb/2) * (s16int)pp[12] + (sp->h << 7) - sp->dy * sp->wb/2;
373                         if(sp->x < 0){
374                                 sp->rx -= sp->x * sp->dx;
375                                 sp->ry -= sp->x * sp->dy;
376                         }
377                 }else{
378                         budg -= sp->w;
379                         if((t0 & SPRVFLIP) != 0)
380                                 dy = h - 1 - dy;
381                         sp->base += (dy & 7) << 2 + d;
382                         sp->base += dy >> 3 << sp->ysh;
383                         if((t0 & SPRHFLIP) != 0)
384                                 sp->base += sp->w - 7 << 2 + d;
385                         sp->inc = (1 << 5 + d) - (1 << 2 + d);
386                         if(sp->x < 0)
387                                 if((t0 & SPRHFLIP) != 0){
388                                         sp->base -= ((-sp->x & 7) >> 1 - d) + (-sp->x >> 3 << 5 + d);
389                                         if((t0 & SPR8) == 0 && (sp->x & 1) != 0)
390                                                 sp->base--;
391                                 }else
392                                         sp->base += ((-sp->x & 7) >> 1 - d) + (-sp->x >> 3 << 5 + d);
393                 }
394                 if((t0 & SPR8) != 0)
395                         sp->pal = pram + 0x100;
396                 else
397                         sp->pal = pram + 0x100 + (t1 >> 8 & 0xf0);
398                 if(budg < 0)
399                         break;
400                 sp++;
401         }
402 }
403
404 void
405 spr(void)
406 {
407         sprite *s;
408         ushort dx;
409         u32int t0;
410         uchar v;
411         ushort x, y;
412         u16int c;
413         int pv, ppri, pri;
414         uchar d;
415         uchar *b;
416         
417         pv = -1;
418         ppri = 6;;
419         for(s = sprt; s < sp; s++){
420                 dx = ppux - s->x;
421                 if(dx >= s->wb)
422                         continue;
423                 t0 = s->t0;
424                 if((t0 & SPRROT) != 0){
425                         x = s->rx >> 8;
426                         y = s->ry >> 8;
427                         if(x < s->w && y < s->h){
428                                 b = s->base;
429                                 d = (t0 & SPR8) != 0;
430                                 b += (y & 7) << 2 + d;
431                                 b += y >> 3 << s->ysh;
432                                 b += (x & 7) >> 1 - d;
433                                 b += x >> 3 << 5 + d;
434                                 v = *b;
435                                 if(!d)
436                                         if((x & 1) != 0)
437                                                 v >>= 4;
438                                         else
439                                                 v &= 0xf;
440                         }else
441                                 v = 0;
442                         s->rx += s->dx;
443                         s->ry += s->dy;
444                 }else if((t0 & SPRHFLIP) != 0){
445                         if((t0 & SPR8) != 0)
446                                 v = *--s->base;
447                         else if((dx & 1) != 0)
448                                 v = *s->base & 0x0f;
449                         else
450                                 v = *--s->base >> 4;
451                         if((dx & 7) == 7)
452                                 s->base -= s->inc;
453                 }else{
454                         v = *s->base;
455                         if((t0 & SPR8) != 0)
456                                 s->base++;
457                         else if((dx & 1) != 0){
458                                 v >>= 4;
459                                 s->base++;
460                         }else
461                                 v &= 0xf;
462                         if((dx & 7) == 7)
463                                 s->base += s->inc;
464                 }
465                 if(v != 0){
466                         pri = s->t1 >> 10 & 3;
467                         c = s->pal[v];
468                         switch(s->t0 >> 10 & 3){
469                         case 1:
470                                 c |= 1<<16;
471                         case 0:
472                                 if(ppri > pri){
473                                         pv = c;
474                                         ppri = pri;
475                                 }
476                                 break;
477                         case 2:
478                                 objwin = 1;
479                                 break;
480                         }
481                 }
482         }
483         if(pv >= 0){
484                 pixel(pv, 4, ppri);
485                 if(pv >> 16 != 0)
486                         objtrans = 1;
487         }
488 }
489
490 u16int
491 mix(u16int c1, u16int c2)
492 {
493         u16int r, g, b, eva, evb;
494
495         eva = blda;
496         evb = bldb;
497         b = ((c1 & 0x7c00) * eva + (c2 & 0x7c00) * evb) >> 4;
498         g = ((c1 & 0x03e0) * eva + (c2 & 0x03e0) * evb) >> 4;
499         r = ((c1 & 0x001f) * eva + (c2 & 0x001f) * evb) >> 4;
500         if(b > 0x7c00) b = 0x7c00;
501         if(g > 0x03e0) g = 0x03e0;
502         if(r > 0x001f) r = 0x001f;
503         return b & 0x7c00 | g & 0x03e0 | r;
504 }
505
506 u16int
507 brighten(u16int c1)
508 {
509         u16int r, g, b, y;
510         
511         y = bldy;
512         b = c1 & 0x7c00;
513         b = b + (0x7c00 - b) * y / 16;
514         g = c1 & 0x03e0;
515         g = g + (0x03e0 - g) * y / 16;
516         r = c1 & 0x001f;
517         r = r + (0x001f - r) * y / 16;
518         if(b > 0x7c00) b = 0x7c00;
519         if(g > 0x03e0) g = 0x03e0;
520         if(r > 0x001f) r = 0x001f;
521         return b & 0x7c00 | g & 0x03e0 | r;
522 }
523
524 u16int
525 darken(u16int c1)
526 {
527         u16int r, g, b, y;
528
529         y = 16 - bldy;
530         b = c1 & 0x7c00;
531         b = b * y / 16;
532         g = c1 & 0x03e0;
533         g = g * y / 16;
534         r = c1 & 0x001f;
535         r = r * y / 16;
536         return b & 0x7c00 | g & 0x03e0 | r;
537 }
538
539 void
540 windows(void)
541 {
542         u16int dispcnt;
543         u16int v, h;
544
545         dispcnt = reg[DISPCNT];
546         bgmask = dispcnt >> 8 | 1<<5;
547         if((dispcnt >> 13) != 0){
548                 if((dispcnt & 1<<13) != 0){
549                         v = reg[WIN0V];
550                         h = reg[WIN0H];
551                         if(ppuy < (u8int)v && ppuy >= v >> 8 &&
552                                 ppux < (u8int)h && ppux >= h >> 8){
553                                 bgmask &= reg[WININ];
554                                 goto windone;
555                         }
556                 }
557                 if((dispcnt & 1<<14) != 0){
558                         v = reg[WIN1V];
559                         h = reg[WIN1H];
560                         if(ppuy < (u8int)v && ppuy >= v >> 8 &&
561                                 ppux < (u8int)h && ppux >= h >> 8){
562                                 bgmask &= reg[WININ] >> 8;
563                                 goto windone;
564                         }
565                 }
566                 if((dispcnt & 1<<15) != 0 && objwin != 0){
567                         bgmask &= reg[WINOUT] >> 8;
568                         goto windone;
569                 }
570                 bgmask &= reg[WINOUT];
571         }
572 windone:
573         if(pixelpri[0] != 8 && (bgmask & 1<<4) == 0){
574                 pixelpri[0] = 8;
575                 pixeldat[0] = pram[0] | 5 << 16;
576         }
577 }
578
579 void
580 colormath(void)
581 {
582         u8int src0;
583         u16int bldcnt;
584         
585         if((bgmask & 1<<5) == 0)
586                 return;
587         bldcnt = reg[BLDCNT];
588         src0 = pixeldat[0] >> 16;
589         if(objtrans && src0 == 4)
590                 goto alpha;
591         if((bldcnt & 3<<6) == 0 || (bldcnt & 1<<src0) == 0)
592                 return;
593         switch(bldcnt >> 6 & 3){
594         case 1:
595         alpha:
596                 if((bldcnt & 1<<8+(pixeldat[1]>>16)) == 0)
597                         return;
598                 pixeldat[0] = mix(pixeldat[0], pixeldat[1]);
599                 break;
600         case 2:
601                 pixeldat[0] = brighten(pixeldat[0]);
602                 break;
603         case 3:
604                 pixeldat[0] = darken(pixeldat[0]);
605                 break;
606         }
607 }
608
609 void
610 ppustep(void)
611 {
612         u16int stat;
613         u16int cnt;
614         
615         stat = reg[DISPSTAT];
616         cnt = reg[DISPCNT];
617         if(ppuy < 160 && ppux < 240)
618                 if((cnt & FBLANK) == 0){
619                         objwin = 0;
620                         objtrans = 0;
621                         pixelpri[0] = 8;
622                         pixeldat[0] = pram[0] | 5 << 16;
623                         if((cnt & 1<<12) != 0)
624                                 spr();
625                         windows();
626                         bgs();
627                         colormath();
628                         pixeldraw(ppux, ppuy, pixeldat[0]);
629                 }else
630                         pixeldraw(ppux, ppuy, 0xffff);
631         if(ppux == 240 && ppuy < 160){
632                 if((stat & IRQHBLEN) != 0)
633                         setif(IRQHBL);
634                 dmastart(DMAHBL);
635         }
636         if(++ppux >= 308){
637                 ppux = 0;
638                 if(++ppuy >= 228){
639                         ppuy = 0;
640                         flush();
641                 }
642                 if((stat & IRQVCTREN) != 0 && ppuy == stat >> 8)
643                         setif(IRQVCTR);
644                 if(ppuy < 160){
645                         bgsinit();
646                         sprinit();
647                 }else if(ppuy == 160){
648                         if((stat & IRQVBLEN) != 0)
649                                 setif(IRQVBL);
650                         dmastart(DMAVBL);
651                 }
652         }
653 }