add new libao audio backend
[rofl0r-gnuboy.git] / lcd.c
blobb3c2face4254c530ff790873523a2915c7c46f0a
1 #include <string.h>
3 #include "refresh.h"
4 #include "palette.h"
5 #include "defs.h"
6 #include "regs.h"
7 #include "hw.h"
8 #include "mem.h"
9 #include "lcd.h"
10 #include "rc.h"
11 #include "fb.h"
12 #ifdef USE_ASM
13 #include "asm.h"
14 #endif
16 struct lcd lcd;
18 struct scan scan;
20 #define BG (scan.bg)
21 #define WND (scan.wnd)
22 #define BUF (scan.buf)
23 #define PRI (scan.pri)
25 #define PAL1 (scan.pal1)
26 #define PAL2 (scan.pal2)
27 #define PAL4 (scan.pal4)
29 #define VS (scan.vs) /* vissprites */
30 #define NS (scan.ns)
32 #define L (scan.l) /* line */
33 #define X (scan.x) /* screen position */
34 #define Y (scan.y)
35 #define S (scan.s) /* tilemap position */
36 #define T (scan.t)
37 #define U (scan.u) /* position within tile */
38 #define V (scan.v)
39 #define WX (scan.wx)
40 #define WY (scan.wy)
41 #define WT (scan.wt)
42 #define WV (scan.wv)
44 byte patpix[4096][8][8];
45 byte patdirty[1024];
46 byte anydirty;
48 static int scale = 1;
49 static int density = 0;
51 static int rgb332;
53 static int sprsort = 1;
54 static int sprdebug;
56 #define DEF_PAL { 0x98d0e0, 0x68a0b0, 0x60707C, 0x2C3C3C }
58 static int dmg_pal[4][4] = { DEF_PAL, DEF_PAL, DEF_PAL, DEF_PAL };
60 static int usefilter, filterdmg;
61 static int filter[3][4] = {
62 { 195, 25, 0, 35 },
63 { 25, 170, 25, 35 },
64 { 25, 60, 125, 40 }
67 rcvar_t lcd_exports[] =
69 RCV_INT("scale", &scale),
70 RCV_INT("density", &density),
71 RCV_BOOL("rgb332", &rgb332),
72 RCV_VECTOR("dmg_bgp", dmg_pal[0], 4),
73 RCV_VECTOR("dmg_wndp", dmg_pal[1], 4),
74 RCV_VECTOR("dmg_obp0", dmg_pal[2], 4),
75 RCV_VECTOR("dmg_obp1", dmg_pal[3], 4),
76 RCV_BOOL("sprsort", &sprsort),
77 RCV_BOOL("sprdebug", &sprdebug),
78 RCV_BOOL("colorfilter", &usefilter),
79 RCV_BOOL("filterdmg", &filterdmg),
80 RCV_VECTOR("red", filter[0], 4),
81 RCV_VECTOR("green", filter[1], 4),
82 RCV_VECTOR("blue", filter[2], 4),
83 RCV_END
86 static byte *vdest;
88 #ifdef ALLOW_UNALIGNED_IO /* long long is ok since this is i386-only anyway? */
89 #define MEMCPY8(d, s) ((*(long long *)(d)) = (*(long long *)(s)))
90 #else
91 #define MEMCPY8(d, s) memcpy((d), (s), 8)
92 #endif
97 #ifndef ASM_UPDATEPATPIX
98 void updatepatpix()
100 int i, j, k;
101 int a, c;
102 byte *vram = lcd.vbank[0];
104 if (!anydirty) return;
105 for (i = 0; i < 1024; i++)
107 if (i == 384) i = 512;
108 if (i == 896) break;
109 if (!patdirty[i]) continue;
110 patdirty[i] = 0;
111 for (j = 0; j < 8; j++)
113 a = ((i<<4) | (j<<1));
114 for (k = 0; k < 8; k++)
116 c = vram[a] & (1<<k) ? 1 : 0;
117 c |= vram[a+1] & (1<<k) ? 2 : 0;
118 patpix[i+1024][j][k] = c;
120 for (k = 0; k < 8; k++)
121 patpix[i][j][k] =
122 patpix[i+1024][j][7-k];
124 for (j = 0; j < 8; j++)
126 for (k = 0; k < 8; k++)
128 patpix[i+2048][j][k] =
129 patpix[i][7-j][k];
130 patpix[i+3072][j][k] =
131 patpix[i+1024][7-j][k];
135 anydirty = 0;
137 #endif /* ASM_UPDATEPATPIX */
141 void tilebuf()
143 int i, cnt;
144 int base;
145 byte *tilemap, *attrmap;
146 int *tilebuf;
147 int *wrap;
148 static int wraptable[64] =
150 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
151 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,-32
154 base = ((R_LCDC&0x08)?0x1C00:0x1800) + (T<<5) + S;
155 tilemap = lcd.vbank[0] + base;
156 attrmap = lcd.vbank[1] + base;
157 tilebuf = BG;
158 wrap = wraptable + S;
159 cnt = ((WX + 7) >> 3) + 1;
161 if (hw.cgb)
163 if (R_LCDC & 0x10)
164 for (i = cnt; i > 0; i--)
166 *(tilebuf++) = *tilemap
167 | (((int)*attrmap & 0x08) << 6)
168 | (((int)*attrmap & 0x60) << 5);
169 *(tilebuf++) = (((int)*attrmap & 0x07) << 2);
170 attrmap += *wrap + 1;
171 tilemap += *(wrap++) + 1;
173 else
174 for (i = cnt; i > 0; i--)
176 *(tilebuf++) = (256 + ((n8)*tilemap))
177 | (((int)*attrmap & 0x08) << 6)
178 | (((int)*attrmap & 0x60) << 5);
179 *(tilebuf++) = (((int)*attrmap & 0x07) << 2);
180 attrmap += *wrap + 1;
181 tilemap += *(wrap++) + 1;
184 else
186 if (R_LCDC & 0x10)
187 for (i = cnt; i > 0; i--)
189 *(tilebuf++) = *(tilemap++);
190 tilemap += *(wrap++);
192 else
193 for (i = cnt; i > 0; i--)
195 *(tilebuf++) = (256 + ((n8)*(tilemap++)));
196 tilemap += *(wrap++);
200 if (WX >= 160) return;
202 base = ((R_LCDC&0x40)?0x1C00:0x1800) + (WT<<5);
203 tilemap = lcd.vbank[0] + base;
204 attrmap = lcd.vbank[1] + base;
205 tilebuf = WND;
206 cnt = ((160 - WX) >> 3) + 1;
208 if (hw.cgb)
210 if (R_LCDC & 0x10)
211 for (i = cnt; i > 0; i--)
213 *(tilebuf++) = *(tilemap++)
214 | (((int)*attrmap & 0x08) << 6)
215 | (((int)*attrmap & 0x60) << 5);
216 *(tilebuf++) = (((int)*(attrmap++)&7) << 2);
218 else
219 for (i = cnt; i > 0; i--)
221 *(tilebuf++) = (256 + ((n8)*(tilemap++)))
222 | (((int)*attrmap & 0x08) << 6)
223 | (((int)*attrmap & 0x60) << 5);
224 *(tilebuf++) = (((int)*(attrmap++)&7) << 2);
227 else
229 if (R_LCDC & 0x10)
230 for (i = cnt; i > 0; i--)
231 *(tilebuf++) = *(tilemap++);
232 else
233 for (i = cnt; i > 0; i--)
234 *(tilebuf++) = (256 + ((n8)*(tilemap++)));
239 void bg_scan()
241 int cnt;
242 byte *src, *dest;
243 int *tile;
245 if (WX <= 0) return;
246 cnt = WX;
247 tile = BG;
248 dest = BUF;
250 src = patpix[*(tile++)][V] + U;
251 memcpy(dest, src, 8-U);
252 dest += 8-U;
253 cnt -= 8-U;
254 if (cnt <= 0) return;
255 while (cnt >= 8)
257 src = patpix[*(tile++)][V];
258 MEMCPY8(dest, src);
259 dest += 8;
260 cnt -= 8;
262 src = patpix[*tile][V];
263 while (cnt--)
264 *(dest++) = *(src++);
267 void wnd_scan()
269 int cnt;
270 byte *src, *dest;
271 int *tile;
273 if (WX >= 160) return;
274 cnt = 160 - WX;
275 tile = WND;
276 dest = BUF + WX;
278 while (cnt >= 8)
280 src = patpix[*(tile++)][WV];
281 MEMCPY8(dest, src);
282 dest += 8;
283 cnt -= 8;
285 src = patpix[*tile][WV];
286 while (cnt--)
287 *(dest++) = *(src++);
290 static void blendcpy(byte *dest, byte *src, byte b, int cnt)
292 while (cnt--) *(dest++) = *(src++) | b;
295 static int priused(void *attr)
297 un32 *a = attr;
298 return (int)((a[0]|a[1]|a[2]|a[3]|a[4]|a[5]|a[6]|a[7])&0x80808080);
301 void bg_scan_pri()
303 int cnt, i;
304 byte *src, *dest;
306 if (WX <= 0) return;
307 i = S;
308 cnt = WX;
309 dest = PRI;
310 src = lcd.vbank[1] + ((R_LCDC&0x08)?0x1C00:0x1800) + (T<<5);
312 if (!priused(src))
314 memset(dest, 0, cnt);
315 return;
318 memset(dest, src[i++&31]&128, 8-U);
319 dest += 8-U;
320 cnt -= 8-U;
321 if (cnt <= 0) return;
322 while (cnt >= 8)
324 memset(dest, src[i++&31]&128, 8);
325 dest += 8;
326 cnt -= 8;
328 memset(dest, src[i&31]&128, cnt);
331 void wnd_scan_pri()
333 int cnt, i;
334 byte *src, *dest;
336 if (WX >= 160) return;
337 i = 0;
338 cnt = 160 - WX;
339 dest = PRI + WX;
340 src = lcd.vbank[1] + ((R_LCDC&0x40)?0x1C00:0x1800) + (WT<<5);
342 if (!priused(src))
344 memset(dest, 0, cnt);
345 return;
348 while (cnt >= 8)
350 memset(dest, src[i++]&128, 8);
351 dest += 8;
352 cnt -= 8;
354 memset(dest, src[i]&128, cnt);
357 #ifndef ASM_BG_SCAN_COLOR
358 void bg_scan_color()
360 int cnt;
361 byte *src, *dest;
362 int *tile;
364 if (WX <= 0) return;
365 cnt = WX;
366 tile = BG;
367 dest = BUF;
369 src = patpix[*(tile++)][V] + U;
370 blendcpy(dest, src, *(tile++), 8-U);
371 dest += 8-U;
372 cnt -= 8-U;
373 if (cnt <= 0) return;
374 while (cnt >= 8)
376 src = patpix[*(tile++)][V];
377 blendcpy(dest, src, *(tile++), 8);
378 dest += 8;
379 cnt -= 8;
381 src = patpix[*(tile++)][V];
382 blendcpy(dest, src, *(tile++), cnt);
384 #endif
386 void wnd_scan_color()
388 int cnt;
389 byte *src, *dest;
390 int *tile;
392 if (WX >= 160) return;
393 cnt = 160 - WX;
394 tile = WND;
395 dest = BUF + WX;
397 while (cnt >= 8)
399 src = patpix[*(tile++)][WV];
400 blendcpy(dest, src, *(tile++), 8);
401 dest += 8;
402 cnt -= 8;
404 src = patpix[*(tile++)][WV];
405 blendcpy(dest, src, *(tile++), cnt);
408 static void recolor(byte *buf, byte fill, int cnt)
410 while (cnt--) *(buf++) |= fill;
413 void spr_count()
415 int i;
416 struct obj *o;
418 NS = 0;
419 if (!(R_LCDC & 0x02)) return;
421 o = lcd.oam.obj;
423 for (i = 40; i; i--, o++)
425 if (L >= o->y || L + 16 < o->y)
426 continue;
427 if (L + 8 >= o->y && !(R_LCDC & 0x04))
428 continue;
429 if (++NS == 10) break;
433 void spr_enum()
435 int i, j;
436 struct obj *o;
437 struct vissprite ts[10];
438 int v, pat;
439 int l, x;
441 NS = 0;
442 if (!(R_LCDC & 0x02)) return;
444 o = lcd.oam.obj;
446 for (i = 40; i; i--, o++)
448 if (L >= o->y || L + 16 < o->y)
449 continue;
450 if (L + 8 >= o->y && !(R_LCDC & 0x04))
451 continue;
452 VS[NS].x = (int)o->x - 8;
453 v = L - (int)o->y + 16;
454 if (hw.cgb)
456 pat = o->pat | (((int)o->flags & 0x60) << 5)
457 | (((int)o->flags & 0x08) << 6);
458 VS[NS].pal = 32 + ((o->flags & 0x07) << 2);
460 else
462 pat = o->pat | (((int)o->flags & 0x60) << 5);
463 VS[NS].pal = 32 + ((o->flags & 0x10) >> 2);
465 VS[NS].pri = (o->flags & 0x80) >> 7;
466 if ((R_LCDC & 0x04))
468 pat &= ~1;
469 if (v >= 8)
471 v -= 8;
472 pat++;
474 if (o->flags & 0x40) pat ^= 1;
476 VS[NS].buf = patpix[pat][v];
477 if (++NS == 10) break;
479 if (!sprsort || hw.cgb) return;
480 /* not quite optimal but it finally works! */
481 for (i = 0; i < NS; i++)
483 l = 0;
484 x = VS[0].x;
485 for (j = 1; j < NS; j++)
487 if (VS[j].x < x)
489 l = j;
490 x = VS[j].x;
493 ts[i] = VS[l];
494 VS[l].x = 160;
496 memcpy(VS, ts, sizeof VS);
499 void spr_scan()
501 int i, x;
502 byte pal, b, ns = NS;
503 byte *src, *dest, *bg, *pri;
504 struct vissprite *vs;
505 static byte bgdup[256];
507 if (!ns) return;
509 memcpy(bgdup, BUF, 256);
510 vs = &VS[ns-1];
512 for (; ns; ns--, vs--)
514 x = vs->x;
515 if (x >= 160) continue;
516 if (x <= -8) continue;
517 if (x < 0)
519 src = vs->buf - x;
520 dest = BUF;
521 i = 8 + x;
523 else
525 src = vs->buf;
526 dest = BUF + x;
527 if (x > 152) i = 160 - x;
528 else i = 8;
530 pal = vs->pal;
531 if (vs->pri)
533 bg = bgdup + (dest - BUF);
534 while (i--)
536 b = src[i];
537 if (b && !(bg[i]&3)) dest[i] = pal|b;
540 else if (hw.cgb)
542 bg = bgdup + (dest - BUF);
543 pri = PRI + (dest - BUF);
544 while (i--)
546 b = src[i];
547 if (b && (!pri[i] || !(bg[i]&3)))
548 dest[i] = pal|b;
551 else while (i--) if (src[i]) dest[i] = pal|src[i];
552 /* else while (i--) if (src[i]) dest[i] = 31 + ns; */
554 if (sprdebug) for (i = 0; i < NS; i++) BUF[i<<1] = 36;
562 void lcd_begin()
564 if (fb.indexed)
566 if (rgb332) pal_set332();
567 else pal_expire();
569 while (scale * 160 > fb.w || scale * 144 > fb.h) scale--;
570 vdest = fb.ptr + ((fb.w*fb.pelsize)>>1)
571 - (80*fb.pelsize) * scale
572 + ((fb.h>>1) - 72*scale) * fb.pitch;
573 WY = R_WY;
576 void lcd_refreshline()
578 int i;
579 byte scalebuf[160*4*4], *dest;
581 if (!fb.enabled) return;
583 if (!(R_LCDC & 0x80))
584 return; /* should not happen... */
586 updatepatpix();
588 L = R_LY;
589 X = R_SCX;
590 Y = (R_SCY + L) & 0xff;
591 S = X >> 3;
592 T = Y >> 3;
593 U = X & 7;
594 V = Y & 7;
596 WX = R_WX - 7;
597 if (WY>L || WY<0 || WY>143 || WX<-7 || WX>159 || !(R_LCDC&0x20))
598 WX = 160;
599 WT = (L - WY) >> 3;
600 WV = (L - WY) & 7;
602 spr_enum();
604 tilebuf();
605 if (hw.cgb)
607 bg_scan_color();
608 wnd_scan_color();
609 if (NS)
611 bg_scan_pri();
612 wnd_scan_pri();
615 else
617 bg_scan();
618 wnd_scan();
619 recolor(BUF+WX, 0x04, 160-WX);
621 spr_scan();
623 if (fb.dirty) memset(fb.ptr, 0, fb.pitch * fb.h);
624 fb.dirty = 0;
625 if (density > scale) density = scale;
626 if (scale == 1) density = 1;
628 dest = (density != 1) ? scalebuf : vdest;
630 switch (scale)
632 case 0:
633 case 1:
634 switch (fb.pelsize)
636 case 1:
637 refresh_1(dest, BUF, PAL1, 160);
638 break;
639 case 2:
640 refresh_2(dest, BUF, PAL2, 160);
641 break;
642 case 3:
643 refresh_3(dest, BUF, PAL4, 160);
644 break;
645 case 4:
646 refresh_4(dest, BUF, PAL4, 160);
647 break;
649 break;
650 case 2:
651 switch (fb.pelsize)
653 case 1:
654 refresh_2(dest, BUF, PAL2, 160);
655 break;
656 case 2:
657 refresh_4(dest, BUF, PAL4, 160);
658 break;
659 case 3:
660 refresh_3_2x(dest, BUF, PAL4, 160);
661 break;
662 case 4:
663 refresh_4_2x(dest, BUF, PAL4, 160);
664 break;
666 break;
667 case 3:
668 switch (fb.pelsize)
670 case 1:
671 refresh_3(dest, BUF, PAL4, 160);
672 break;
673 case 2:
674 refresh_2_3x(dest, BUF, PAL2, 160);
675 break;
676 case 3:
677 refresh_3_3x(dest, BUF, PAL4, 160);
678 break;
679 case 4:
680 refresh_4_3x(dest, BUF, PAL4, 160);
681 break;
683 break;
684 case 4:
685 switch (fb.pelsize)
687 case 1:
688 refresh_4(dest, BUF, PAL4, 160);
689 break;
690 case 2:
691 refresh_4_2x(dest, BUF, PAL4, 160);
692 break;
693 case 3:
694 refresh_3_4x(dest, BUF, PAL4, 160);
695 break;
696 case 4:
697 refresh_4_4x(dest, BUF, PAL4, 160);
698 break;
700 break;
701 default:
702 break;
705 if (density != 1)
707 for (i = 0; i < scale; i++)
709 if ((i < density) || ((density <= 0) && !(i&1)))
710 memcpy(vdest, scalebuf, 160 * fb.pelsize * scale);
711 vdest += fb.pitch;
714 else vdest += fb.pitch * scale;
723 static void updatepalette(int i)
725 int c, r, g, b, y, u, v, rr, gg;
727 c = (lcd.pal[i<<1] | ((int)lcd.pal[(i<<1)|1] << 8)) & 0x7FFF;
728 r = (c & 0x001F) << 3;
729 g = (c & 0x03E0) >> 2;
730 b = (c & 0x7C00) >> 7;
731 r |= (r >> 5);
732 g |= (g >> 5);
733 b |= (b >> 5);
735 if (usefilter && (filterdmg || hw.cgb))
737 rr = ((r * filter[0][0] + g * filter[0][1] + b * filter[0][2]) >> 8) + filter[0][3];
738 gg = ((r * filter[1][0] + g * filter[1][1] + b * filter[1][2]) >> 8) + filter[1][3];
739 b = ((r * filter[2][0] + g * filter[2][1] + b * filter[2][2]) >> 8) + filter[2][3];
740 r = rr;
741 g = gg;
744 if (fb.yuv)
746 y = (((r * 263) + (g * 516) + (b * 100)) >> 10) + 16;
747 u = (((r * 450) - (g * 377) - (b * 73)) >> 10) + 128;
748 v = (((r * -152) - (g * 298) + (b * 450)) >> 10) + 128;
749 if (y < 0) y = 0;
750 if (y > 255) y = 255;
751 if (u < 0) u = 0;
752 if (u > 255) u = 255;
753 if (v < 0) v = 0;
754 if (v > 255) v = 255;
755 PAL4[i] = (y<<fb.cc[0].l) | (y<<fb.cc[3].l)
756 | (u<<fb.cc[1].l) | (v<<fb.cc[2].l);
757 return;
760 if (fb.indexed)
762 pal_release(PAL1[i]);
763 c = pal_getcolor(c, r, g, b);
764 PAL1[i] = c;
765 PAL2[i] = (c<<8) | c;
766 PAL4[i] = (c<<24) | (c<<16) | (c<<8) | c;
767 return;
770 r = (r >> fb.cc[0].r) << fb.cc[0].l;
771 g = (g >> fb.cc[1].r) << fb.cc[1].l;
772 b = (b >> fb.cc[2].r) << fb.cc[2].l;
773 c = r|g|b;
775 switch (fb.pelsize)
777 case 1:
778 PAL1[i] = c;
779 PAL2[i] = (c<<8) | c;
780 PAL4[i] = (c<<24) | (c<<16) | (c<<8) | c;
781 break;
782 case 2:
783 PAL2[i] = c;
784 PAL4[i] = (c<<16) | c;
785 break;
786 case 3:
787 case 4:
788 PAL4[i] = c;
789 break;
793 void pal_write(int i, byte b)
795 if (lcd.pal[i] == b) return;
796 lcd.pal[i] = b;
797 updatepalette(i>>1);
800 void pal_write_dmg(int i, int mapnum, byte d)
802 int j;
803 int *cmap = dmg_pal[mapnum];
804 int c, r, g, b;
806 if (hw.cgb) return;
808 /* if (mapnum >= 2) d = 0xe4; */
809 for (j = 0; j < 8; j += 2)
811 c = cmap[(d >> j) & 3];
812 r = (c & 0xf8) >> 3;
813 g = (c & 0xf800) >> 6;
814 b = (c & 0xf80000) >> 9;
815 c = r|g|b;
816 /* FIXME - handle directly without faking cgb */
817 pal_write(i+j, c & 0xff);
818 pal_write(i+j+1, c >> 8);
822 void vram_write(int a, byte b)
824 lcd.vbank[R_VBK&1][a] = b;
825 if (a >= 0x1800) return;
826 patdirty[((R_VBK&1)<<9)+(a>>4)] = 1;
827 anydirty = 1;
830 void vram_dirty()
832 anydirty = 1;
833 memset(patdirty, 1, sizeof patdirty);
836 void pal_dirty()
838 int i;
839 if (!hw.cgb)
841 pal_write_dmg(0, 0, R_BGP);
842 pal_write_dmg(8, 1, R_BGP);
843 pal_write_dmg(64, 2, R_OBP0);
844 pal_write_dmg(72, 3, R_OBP1);
846 for (i = 0; i < 64; i++)
847 updatepalette(i);
850 void lcd_reset()
852 memset(&lcd, 0, sizeof lcd);
853 lcd_begin();
854 vram_dirty();
855 pal_dirty();