]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/gba/ppu.c
add libemu
[plan9front.git] / sys / src / games / gba / ppu.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <emu.h>
5 #include "dat.h"
6 #include "fns.h"
7
8 int hblank, ppuy;
9 u8int bldy, blda, bldb;
10 u32int hblclock;
11 int ppux0;
12 u32int pixcol[480];
13 u8int pixpri[480];
14 u8int pixwin[240];
15 int objalpha;
16
17 typedef struct bg bg;
18 struct bg {
19         uchar n;
20         s32int rpx0, rpy0, rpx1, rpy1, rpx, rpy;
21         u16int tx, ty;
22         u8int tnx, tny;
23         
24         u8int mosaic, mctr, lasti;
25         u32int curc;
26         u8int curpri;
27 };
28 static bg bgst[4] = {{.n = 0}, {.n = 1}, {.n = 2}, {.n = 3}};
29
30 Var ppuvars[] = {
31         VAR(hblank), VAR(ppuy), VAR(hblclock),
32         VAR(bldy), VAR(blda), VAR(bldb), VAR(objalpha),
33         VAR(bgst[2].rpx0), VAR(bgst[2].rpy0), VAR(bgst[3].rpx0), VAR(bgst[3].rpy0),
34         {nil, 0, 0},
35 };
36
37 typedef struct sprite sprite;
38 struct sprite {
39         uchar w, wb, h;
40         s16int x;
41         uchar ysh;
42         
43         uchar *base;
44         u16int *pal;
45         u16int inc;
46
47         u32int t0;
48         u16int t1;
49         uchar depth;
50         
51         s32int rx, ry;
52         s16int dx, dy;
53         
54         u8int mctr, mcol;
55 };
56 static sprite sprt[128], *sp = sprt;
57 enum {
58         SPRROT = 1<<8,
59         SPRDIS = 1<<9,
60         SPRDOUB = 1<<9,
61         SPRMOSA = 1<<12,
62         SPR8 = 1<<13,
63         SPRWIDE = 1<<14,
64         SPRTALL = 1<<15,
65         SPRHFLIP = 1<<28,
66         SPRVFLIP = 1<<29,
67         SPRSIZE0 = 1<<30,
68         SPRSIZE1 = 1<<31,
69
70         NOWIN = 0,
71         OBJWIN = 1,
72         WIN2 = 2,
73         WIN1 = 4,
74         
75         OBJALPHA = 1<<16,
76         SRCOBJ = 4<<17,
77         SRCBACK = 5<<17,
78         
79         VACANT = 0x10,
80         BACKDROP = 8,
81 };
82 #define SRCBG(n) ((n)<<17)
83
84 void
85 sprinit(void)
86 {
87         u16int *p, *pp;
88         u16int cnt, t1;
89         u32int t0;
90         int budg;
91         uchar ws, h, hb, d, dy, s;
92         static uchar wss[16] = {3, 4, 5, 6, 4, 5, 5, 6, 3, 3, 4, 5};
93         static uchar hss[16] = {3, 4, 5, 6, 3, 3, 4, 5, 4, 5, 5, 6};
94
95         sp = sprt;
96         cnt = reg[DISPCNT];
97         budg = (cnt & HBLFREE) != 0 ? 954 : 1210;
98         for(p = oam; p < oam + 512; p += 4){
99                 t0 = p[0];
100                 if((t0 & (SPRROT|SPRDIS)) == SPRDIS)
101                         continue;
102                 t0 |= p[1] << 16;
103                 s = t0 >> 30 & 3 | t0 >> 12 & 12;
104                 hb = h = 1 << hss[s];
105                 dy = ppuy - (u8int) t0;
106                 if((t0 & (SPRROT|SPRDOUB)) == (SPRROT|SPRDOUB))
107                         hb <<= 1;
108                 if(dy >= hb || (u8int)t0 + hb > 256 && ppuy + 256 - (u8int)t0 >= hb)
109                         continue;
110                 if((t0 & SPRMOSA) != 0){
111                         dy = dy - dy % ((reg[MOSAIC] >> 12 & 15) + 1);
112                         sp->mctr = 0;
113                 }
114                 sp->x = (s32int)(t0 << 7) >> 23;
115                 sp->t0 = t0;
116                 ws = wss[s];
117                 sp->wb = sp->w = 1<<ws;
118                 sp->h = h;
119                 sp->t1 = t1 = p[2];
120                 sp->base = vram + 0x10000 + ((t1 & 0x3ff) << 5);
121                 d = (t0 & SPR8) != 0;
122                 sp->ysh = (cnt & OBJNOMAT) != 0 ? 2 + d + ws : 10;
123                 if((t0 & SPRROT) != 0){
124                         if((t0 & SPRDOUB) != 0)
125                                 sp->wb <<= 1;
126                         budg -= 10 + sp->w*2;
127                         pp = oam + 3 + (t0 >> 21 & 0x1f0);
128                         sp->dx = pp[0];
129                         sp->dy = pp[8];
130                         sp->rx = (dy - hb/2) * (s16int) pp[4] + (sp->w << 7) - sp->dx * sp->wb/2;
131                         sp->ry = (dy - hb/2) * (s16int)pp[12] + (sp->h << 7) - sp->dy * sp->wb/2;
132                         if(sp->x < 0){
133                                 sp->rx -= sp->x * sp->dx;
134                                 sp->ry -= sp->x * sp->dy;
135                         }
136                 }else{
137                         budg -= sp->w;
138                         if((t0 & SPRVFLIP) != 0)
139                                 dy = h - 1 - dy;
140                         sp->base += (dy & 7) << 2 + d;
141                         sp->base += dy >> 3 << sp->ysh;
142                         if((t0 & SPRHFLIP) != 0)
143                                 sp->base += sp->w - 7 << 2 + d;
144                         sp->inc = (1 << 5 + d) - (1 << 2 + d);
145                         if(sp->x < 0)
146                                 if((t0 & SPRHFLIP) != 0){
147                                         sp->base -= ((-sp->x & 7) >> 1 - d) + (-sp->x >> 3 << 5 + d);
148                                         if((t0 & SPR8) == 0 && (sp->x & 1) != 0)
149                                                 sp->base--;
150                                 }else
151                                         sp->base += ((-sp->x & 7) >> 1 - d) + (-sp->x >> 3 << 5 + d);
152                 }
153                 if((t0 & SPR8) != 0)
154                         sp->pal = pram + 0x100;
155                 else
156                         sp->pal = pram + 0x100 + (t1 >> 8 & 0xf0);
157                 if(budg < 0)
158                         break;
159                 sp++;
160         }
161 }
162
163 void
164 spr(int x1)
165 {
166         int x0, i, dx, sx0, sx1;
167         u8int pri, v, d, *b;
168         u16int x, y;
169         u32int c, t0;
170         sprite *s;
171         
172         x0 = ppux0;
173         for(s = sprt; s < sp; s++){
174                 if(s->x >= x1 || s->x + s->wb <= x0)
175                         continue;
176                 t0 = s->t0;
177                 pri = s->t1 >> 10 & 3;
178                 sx0 = s->x >= x0 ? s->x : x0;
179                 sx1 = s->x + s->wb;
180                 if(x1 < sx1)
181                         sx1 = x1;
182                 dx = sx0 - s->x;
183                 for(i = sx0; i < sx1; i++, dx++){
184                         if((t0 & SPRROT) != 0){
185                                 d = (t0 & SPR8) != 0;
186                                 x = s->rx >> 8;
187                                 y = s->ry >> 8;
188                                 s->rx += s->dx;
189                                 s->ry += s->dy;
190                                 if(x < s->w && y < s->h){
191                                         b = s->base;
192                                         b += (y & 7) << 2 + d;
193                                         b += y >> 3 << s->ysh;
194                                         b += (x & 7) >> 1 - d;
195                                         b += x >> 3 << 5 + d;
196                                         v = *b;
197                                         if(!d)
198                                                 if((x & 1) != 0)
199                                                         v >>= 4;
200                                                 else
201                                                         v &= 0xf;
202                                 }else
203                                         v = 0;
204                         }else if((t0 & SPRHFLIP) != 0){
205                                 if((t0 & SPR8) != 0)
206                                         v = *--s->base;
207                                 else if((dx & 1) != 0)
208                                         v = *s->base & 0x0f;
209                                 else
210                                         v = *--s->base >> 4;
211                                 if((dx & 7) == 7)
212                                         s->base -= s->inc;
213                         }else{
214                                 v = *s->base;
215                                 if((t0 & SPR8) != 0)
216                                         s->base++;
217                                 else if((dx & 1) != 0){
218                                         v >>= 4;
219                                         s->base++;
220                                 }else
221                                         v &= 0xf;
222                                 if((dx & 7) == 7)
223                                         s->base += s->inc;
224                         }
225                         if((t0 & SPRMOSA) != 0)
226                                 if(s->mctr == 0){
227                                         s->mctr = reg[MOSAIC] >> 8 & 15;
228                                         s->mcol = v;
229                                 }else{
230                                         --s->mctr;
231                                         v = s->mcol;
232                                 }
233                         if(v != 0){
234                                 c = s->pal[v] | SRCOBJ;
235                                 switch(t0 >> 10 & 3){
236                                 case 1:
237                                         c |= OBJALPHA;
238                                         objalpha++;
239                                 case 0:
240                                         if(pri < pixpri[i]){
241                                                 pixcol[i] = c;
242                                                 pixpri[i] = pri;
243                                         }
244                                         break;
245                                 case 2:
246                                         if((reg[DISPCNT] & 1<<15) != 0)
247                                                 pixwin[i] |= OBJWIN;
248                                         break;
249                                 }
250                         }
251                 }
252         }
253 }
254
255 void
256 bgpixel(bg *b, int i, u32int c, int pri)
257 {
258         u8int *p;
259         u32int *q;
260         int j;
261
262         if(b != nil){
263                 c |= SRCBG(b->n);
264                 if(b->mosaic){
265                         for(j = (u8int)(b->lasti+1); j <= i; j++){
266                                 if(b->mctr == 0){
267                                         if(j == i){
268                                                 b->curc = c;
269                                                 b->curpri = pri;
270                                         }else
271                                                 b->curpri = VACANT;
272                                         b->mctr = reg[MOSAIC] & 15;
273                                 }else
274                                         b->mctr--;
275                                 if(b->curpri != VACANT && (pixwin[j] & 1<<b->n) == 0)
276                                         bgpixel(nil, j, b->curc, b->curpri);
277                         }
278                         b->lasti = i;
279                         return;
280                 }
281         }
282         p = pixpri + i;
283         q = pixcol + i;
284         if(pri < p[0]){
285                 p[240] = p[0];
286                 p[0] = pri;
287                 q[240] = q[0];
288                 q[0] = c;
289         }else if(pri < p[240]){
290                 p[240] = pri;
291                 q[240] = c;
292         }
293 }
294
295
296
297 void
298 bginit(bg *b, int scal, int)
299 {
300         u16int x, y;
301         u16int *rr;
302         int msz;
303
304         b->mosaic = (reg[BG0CNT + b->n] & BGMOSAIC) != 0;
305         if(b->mosaic){
306                 b->mctr = 0;
307                 b->lasti = -1;
308         }
309         if(scal){
310                 rr = reg + (b->n - 2 << 3);
311                 if(ppuy == 0){
312                         b->rpx0 = (s32int)(rr[BG2XL] | rr[BG2XH] << 16) << 4 >> 4;
313                         b->rpy0 = (s32int)(rr[BG2YL] | rr[BG2YH] << 16) << 4 >> 4;
314                 }
315                 if(!b->mosaic || ppuy % ((reg[MOSAIC] >> 4 & 15) + 1) == 0){
316                         b->rpx1 = b->rpx0;
317                         b->rpy1 = b->rpy0;
318                 }
319                 b->rpx = b->rpx1;
320                 b->rpy = b->rpy1;
321                 b->rpx0 += (s16int)rr[BG2PB];
322                 b->rpy0 += (s16int)rr[BG2PD];
323         }else{
324                 rr = reg + (b->n << 1);
325                 x = rr[BG0HOFS] & 0x1ff;
326                 y = ppuy;
327                 if(b->mosaic){
328                         msz = (reg[MOSAIC] >> 4 & 15) + 1;
329                         y = y - y % msz;
330                 }
331                 y += (rr[BG0VOFS] & 0x1ff);
332                 b->tx = x >> 3;
333                 b->ty = y >> 3;
334                 b->tnx = x & 7;
335                 b->tny = y & 7;
336         }
337 }
338
339 void
340 bgsinit(void)
341 {
342         switch(reg[DISPCNT] & 7){
343         case 0:
344                 bginit(&bgst[0], 0, 0);
345                 bginit(&bgst[1], 0, 0);
346                 bginit(&bgst[2], 0, 0);
347                 bginit(&bgst[3], 0, 0);
348                 break;
349         case 1:
350                 bginit(&bgst[0], 0, 0);
351                 bginit(&bgst[1], 0, 0);
352                 bginit(&bgst[2], 1, 0);
353                 break;
354         case 2:
355                 bginit(&bgst[2], 1, 0);
356                 bginit(&bgst[3], 1, 0);
357                 break;
358         case 3:
359         case 4:
360         case 5:
361                 bginit(&bgst[2], 1, 1);
362                 break;
363         }       
364 }
365
366 void
367 bitbg(bg *b, int x1)
368 {
369         u8int *base, *p, pri, d;
370         u16int cnt, *rr, sx, sy;
371         int i, v;
372         
373         cnt = reg[DISPCNT];
374         if((cnt & 1<<8 + b->n) == 0)
375                 return;
376         rr = reg + (b->n - 2 << 3);
377         if((cnt & 7) != 5){
378                 sx = 240 << 8;
379                 sy = 160 << 8;
380                 d = (cnt & 7) == 3;
381         }else{
382                 sx = 160 << 8;
383                 sy = 128 << 8;
384                 d = 1;
385         }
386         base = vram;
387         if((cnt & FRAME) != 0 && (cnt & 7) != 3)
388                 base += 0xa000;
389         pri = reg[BG0CNT + b->n] & 3;
390         for(i = ppux0; i < x1; i++){
391                 if(((pixwin[i] & 1<<b->n) == 0 || b->mosaic) && (u32int)b->rpx < sx && (u32int)b->rpy < sy){
392                         if(d){
393                                 p = base + 2 * (b->rpx >> 8) + 480 * (b->rpy >> 8);
394                                 v = p[0] | p[1] << 8;
395                         }else{
396                                 v = base[(b->rpx >> 8) + 240 * (b->rpy >> 8)];
397                                 if(v != 0)
398                                         v = pram[v];
399                                 else
400                                         v = -1;
401                         }
402                         if(v >= 0)
403                                 bgpixel(b, i, v, pri);
404         
405                 }
406                 b->rpx += (s16int) rr[BG2PA];
407                 b->rpy += (s16int) rr[BG2PC];
408         }
409 }
410
411 void
412 txtbg(bg *b, int x1)
413 {
414         u8int y, v, d, *cp;
415         u16int bgcnt, ta0, ta, tx, ty, t, *pal;
416         u32int ca;
417         int i, x, mx;
418         
419         if((reg[DISPCNT] & 1<<8 + b->n) == 0)
420                 return;
421         bgcnt = reg[BG0CNT + b->n];
422         d = bgcnt >> 7 & 1;
423         tx = b->tx;
424         ty = b->ty;
425         ta0 = (bgcnt << 3 & 0xf800) + ((ty & 0x1f) << 6);
426         switch(bgcnt >> 14){
427         case 2: ta0 += ty << 6 & 0x800; break;
428         case 3: ta0 += ty << 7 & 0x1000; break;
429         }
430         x = ppux0;
431         i = b->tnx;
432         for(; x < x1; tx++, i = 0){
433                 ta = ta0 + ((tx & 0x1f) << 1);
434                 if((bgcnt & 1<<14) != 0)
435                         ta += tx << 6 & 0x800;
436                 t = vram[ta] | vram[ta+1] << 8;
437                 if(d)
438                         pal = pram;
439                 else
440                         pal = pram + (t >> 8 & 0xf0);
441                 ca = (bgcnt << 12 & 0xc000) + ((t & 0x3ff) << 5+d);
442                 if(ca >= 0x10000)
443                         continue;
444                 y = b->tny;
445                 if((t & 1<<11) != 0)
446                         y ^= 7;
447                 ca += y << 2+d;
448                 cp = vram + ca;
449                 for(; i < 8; i++, x++){
450                         if(x >= x1)
451                                 goto out;
452                         if((pixwin[x] & 1<<b->n) != 0 && !b->mosaic)
453                                 continue;
454                         mx = i;
455                         if((t & 1<<10) != 0)
456                                 mx ^= 7;
457                         v = cp[mx >> 1-d];
458                         if(!d)
459                                 if((mx & 1) != 0)
460                                         v >>= 4;
461                                 else
462                                         v &= 0xf;
463                         if(v != 0)
464                                 bgpixel(b, x, pal[v], bgcnt & 3);
465                 }
466         }
467 out:
468         b->tx = tx;
469         b->tnx = i;
470 }
471
472 void
473 rotbg(bg *b, int x1)
474 {
475         uchar *p, v;
476         u16int bgcnt, *rr, ta;
477         int i, row, sz, x, y;
478
479         rr = reg + (b->n - 2 << 3);
480         if((reg[DISPCNT] & 1<<8 + b->n) == 0)
481                 return;
482         bgcnt = reg[BG0CNT + b->n];
483         row = (bgcnt >> 14) + 4;
484         sz = 1 << 3 + row;
485         for(i = ppux0; i < x1; i++){
486                 x = b->rpx >> 8;
487                 y = b->rpy >> 8;
488                 b->rpx += (s16int) rr[BG2PA];
489                 b->rpy += (s16int) rr[BG2PC];
490                 if((pixwin[i] & 1<<b->n) != 0 && !b->mosaic)
491                         continue;
492                 if((bgcnt & DISPWRAP) != 0){
493                         x &= sz - 1;
494                         y &= sz - 1;
495                 }else if((uint)x >= sz || (uint)y >= sz)
496                          continue;
497                 ta = (bgcnt << 3 & 0xf800) + ((y >> 3) << row) + (x >> 3);
498                 p = vram + (bgcnt << 12 & 0xc000) + (vram[ta] << 6);
499                 p += (x & 7) + ((y & 7) << 3);
500                 if((v = *p) != 0)
501                         bgpixel(b, i, pram[v], bgcnt & 3);
502                 
503         }
504 }
505
506 void
507 windows(int x1)
508 {
509         static u8int wintab[8] = {2, 3, 1, 1, 0, 0, 0, 0};
510         int i, sx0, sx1;
511         u16int v, h;
512         u16int cnt;
513         
514         cnt = reg[DISPCNT];
515         if((cnt >> 13) != 0){
516                 if((cnt & 1<<13) != 0){
517                         v = reg[WIN0V];
518                         h = reg[WIN0H];
519                         if(ppuy < (u8int)v && ppuy >= v >> 8){
520                                 sx1 = (u8int)h;
521                                 sx0 = h >> 8;
522                                 if(sx0 < ppux0)
523                                         sx0 = ppux0;
524                                 if(sx1 > x1)
525                                         sx1 = x1;
526                                 for(i = sx0; i < sx1; i++)
527                                         pixwin[i] |= WIN1;
528                         }
529                 }
530                 if((cnt & 1<<14) != 0){
531                         v = reg[WIN1V];
532                         h = reg[WIN1H];
533                         if(ppuy < (u8int)v && ppuy >= v >> 8){
534                                 sx1 = (u8int)h;
535                                 sx0 = h >> 8;
536                                 if(sx0 < ppux0)
537                                         sx0 = ppux0;
538                                 if(sx1 > x1)
539                                         sx1 = x1;
540                                 for(i = sx0; i < sx1; i++)
541                                         pixwin[i] |= WIN2;
542                         }
543                 }
544                 for(i = ppux0; i < x1; i++){
545                         v = wintab[pixwin[i]];
546                         h = reg[WININ + (v & 2) / 2];
547                         if((v & 1) != 0)
548                                 h >>= 8;
549                         pixwin[i] = ~h;
550                 }
551         }
552         for(i = ppux0; i < x1; i++)
553                 if(pixpri[i] == VACANT || (pixwin[i] & 1<<4) != 0){
554                         pixcol[i] = pram[0] | SRCBACK;
555                         pixpri[i] = BACKDROP;
556                 }else{
557                         pixcol[i+240] = pram[0] | SRCBACK;
558                         pixpri[i+240] = BACKDROP;
559                 }
560         objalpha = 0;
561 }
562
563 u16int
564 mix(u16int c1, u16int c2)
565 {
566         u16int r, g, b, eva, evb;
567
568         eva = blda;
569         evb = bldb;
570         b = ((c1 & 0x7c00) * eva + (c2 & 0x7c00) * evb) >> 4;
571         g = ((c1 & 0x03e0) * eva + (c2 & 0x03e0) * evb) >> 4;
572         r = ((c1 & 0x001f) * eva + (c2 & 0x001f) * evb) >> 4;
573         if(b > 0x7c00) b = 0x7c00;
574         if(g > 0x03e0) g = 0x03e0;
575         if(r > 0x001f) r = 0x001f;
576         return b & 0x7c00 | g & 0x03e0 | r;
577 }
578
579 u16int
580 brighten(u16int c1)
581 {
582         u16int r, g, b, y;
583         
584         y = bldy;
585         b = c1 & 0x7c00;
586         b = b + (0x7c00 - b) * y / 16;
587         g = c1 & 0x03e0;
588         g = g + (0x03e0 - g) * y / 16;
589         r = c1 & 0x001f;
590         r = r + (0x001f - r) * y / 16;
591         if(b > 0x7c00) b = 0x7c00;
592         if(g > 0x03e0) g = 0x03e0;
593         if(r > 0x001f) r = 0x001f;
594         return b & 0x7c00 | g & 0x03e0 | r;
595 }
596
597 u16int
598 darken(u16int c1)
599 {
600         u16int r, g, b, y;
601
602         y = 16 - bldy;
603         b = c1 & 0x7c00;
604         b = b * y / 16;
605         g = c1 & 0x03e0;
606         g = g * y / 16;
607         r = c1 & 0x001f;
608         r = r * y / 16;
609         return b & 0x7c00 | g & 0x03e0 | r;
610 }
611
612 void
613 colormath(int x1)
614 {
615         u16int bldcnt;
616         u32int *p;
617         int i;
618         
619         bldcnt = reg[BLDCNT];
620         if((bldcnt & 3<<6) == 0 && objalpha == 0)
621                 return;
622         p = pixcol + ppux0;
623         for(i = ppux0; i < x1; i++, p++){
624                 if((*p & OBJALPHA) != 0)
625                         goto alpha;
626                 if((pixwin[i] & 1<<5) != 0 || (bldcnt & 1<<(*p >> 17)) == 0)
627                         continue;
628                 switch(bldcnt >> 6 & 3){
629                 case 1:
630                 alpha:
631                         if((bldcnt & 1<<8+(p[240] >> 17)) == 0)
632                                 continue;
633                         *p = mix(*p, p[240]);
634                         break;
635                 case 2:
636                         *p = brighten(*p);
637                         break;
638                 case 3:
639                         *p = darken(*p);
640                         break;
641                 }
642         }
643 }
644
645 void
646 syncppu(int x1)
647 {
648         int i;
649         u16int cnt;
650
651         if(hblank || ppuy >= 160)
652                 return;
653         if(x1 >= 240)
654                 x1 = 240;
655         else if(x1 <= ppux0)
656                 return;
657         cnt = reg[DISPCNT];
658         if((cnt & FBLANK) != 0){
659                 for(i = ppux0; i < x1; i++)
660                         pixcol[i] = 0xffff;
661                 ppux0 = x1;
662                 return;
663         }
664
665         if((cnt & 1<<12) != 0)
666                 spr(x1);
667         windows(x1);
668         switch(cnt & 7){
669         case 0:
670                 txtbg(&bgst[0], x1);
671                 txtbg(&bgst[1], x1);
672                 txtbg(&bgst[2], x1);
673                 txtbg(&bgst[3], x1);
674                 break;
675         case 1:
676                 txtbg(&bgst[0], x1);
677                 txtbg(&bgst[1], x1);
678                 rotbg(&bgst[2], x1);
679                 break;
680         case 2:
681                 rotbg(&bgst[2], x1);
682                 rotbg(&bgst[3], x1);
683                 break;
684         case 3:
685         case 4:
686         case 5:
687                 bitbg(&bgst[2], x1);
688         }
689         colormath(x1);
690         ppux0 = x1;
691 }
692
693 void
694 linecopy(u32int *p, int y)
695 {
696         uchar *q;
697         u16int *r;
698         u16int v;
699         union { u16int w; u8int b[2]; } u;
700         int n;
701
702         q = pic + y * 240 * 2 * scale;
703         r = (u16int*)q;
704         n = 240;
705         while(n--){
706                 v = *p++;
707                 u.b[0] = v;
708                 u.b[1] = v >> 8;
709                 switch(scale){
710                 case 16: *r++ = u.w;
711                 case 15: *r++ = u.w;
712                 case 14: *r++ = u.w;
713                 case 13: *r++ = u.w;
714                 case 12: *r++ = u.w;
715                 case 11: *r++ = u.w;
716                 case 10: *r++ = u.w;
717                 case 9: *r++ = u.w;
718                 case 8: *r++ = u.w;
719                 case 7: *r++ = u.w;
720                 case 6: *r++ = u.w;
721                 case 5: *r++ = u.w;
722                 case 4: *r++ = u.w;
723                 case 3: *r++ = u.w;
724                 case 2: *r++ = u.w;
725                 default: *r++ = u.w;
726                 }
727         }
728 }
729
730 void
731 hblanktick(void *)
732 {
733         extern Event evhblank;
734         u16int stat;
735
736         stat = reg[DISPSTAT];
737         if(hblank){
738                 hblclock = clock + evhblank.time;
739                 addevent(&evhblank, 240*4);
740                 hblank = 0;
741                 ppux0 = 0;
742                 memset(pixpri, VACANT, sizeof(pixpri));
743                 memset(pixwin, 0, 240);
744                 if(++ppuy >= 228){
745                         ppuy = 0;
746                         flush();
747                 }
748                 if(ppuy < 160){
749                         sprinit();
750                         bgsinit();
751                 }else if(ppuy == 160){
752                         dmastart(DMAVBL);
753                         if((stat & IRQVBLEN) != 0)
754                                 setif(IRQVBL);
755                 }
756                 if((stat & IRQVCTREN) != 0 && ppuy == stat >> 8)
757                         setif(IRQVCTR);
758         }else{
759                 syncppu(240);
760                 if(ppuy < 160)
761                         linecopy(pixcol, ppuy);
762                 addevent(&evhblank, 68*4);
763                 hblank = 1;
764                 if((stat & IRQHBLEN) != 0)
765                         setif(IRQHBL);
766                 if(ppuy < 160)
767                         dmastart(DMAHBL);
768         }
769 }
770
771 void
772 ppuwrite(u16int a, u16int v)
773 {
774         syncppu((clock - hblclock) / 4);
775         switch(a){
776         case BLDALPHA*2:
777                 blda = v & 0x1f;
778                 if(blda > 16)
779                         blda = 16;
780                 bldb = v >> 8 & 0x1f;
781                 if(bldb > 16)
782                         bldb = 16;
783                 break;
784         case BLDY*2:
785                 bldy = v & 0x1f;
786                 if(bldy > 16)
787                         bldy = 16;
788                 break;
789         case BG2XL*2: bgst[2].rpx0 = bgst[2].rpx0 & 0xffff0000 | v; break;
790         case BG2XH*2: bgst[2].rpx0 = bgst[2].rpx0 & 0xffff | (s32int)(v << 20) >> 4; break;
791         case BG2YL*2: bgst[2].rpy0 = bgst[2].rpy0 & 0xffff0000 | v; break;
792         case BG2YH*2: bgst[2].rpy0 = bgst[2].rpy0 & 0xffff | (s32int)(v << 20) >> 4; break;
793         case BG3XL*2: bgst[3].rpx0 = bgst[3].rpx0 & 0xffff0000 | v; break;
794         case BG3XH*2: bgst[3].rpx0 = bgst[3].rpx0 & 0xffff | (s32int)(v << 20) >> 4; break;
795         case BG3YL*2: bgst[3].rpy0 = bgst[3].rpy0 & 0xffff0000 | v; break;
796         case BG3YH*2: bgst[3].rpy0 = bgst[3].rpy0 & 0xffff | (s32int)(v << 20) >> 4; break;
797         }
798 }