improve --showvars output with help
[rofl0r-gnuboy.git] / lcd.c
blobb697f6c27729015a96d8688fa6da0258660df21c
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 # define MAX_SCALE 4
49 static int scale = 1;
50 static int density = 0;
52 static int rgb332;
54 static int sprsort = 1;
55 static int sprdebug;
57 #define DEF_PAL { 0x98d0e0, 0x68a0b0, 0x60707C, 0x2C3C3C }
59 static int dmg_pal[4][4] = { DEF_PAL, DEF_PAL, DEF_PAL, DEF_PAL };
61 static int usefilter, filterdmg;
62 static int filter[3][4] = {
63 { 195, 25, 0, 35 },
64 { 25, 170, 25, 35 },
65 { 25, 60, 125, 40 }
68 rcvar_t lcd_exports[] =
70 RCV_INT("scale", &scale, "scaling factor"),
71 RCV_INT("density", &density, "density!=scale: blank scanlines"),
72 RCV_BOOL("rgb332", &rgb332, "hi-color game hack in 8bit mode"),
73 RCV_VECTOR("dmg_bgp", dmg_pal[0], 4, "colors for bg palette"),
74 RCV_VECTOR("dmg_wndp", dmg_pal[1], 4, "colors for window palette"),
75 RCV_VECTOR("dmg_obp0", dmg_pal[2], 4, "colors for sprite pal 1"),
76 RCV_VECTOR("dmg_obp1", dmg_pal[3], 4, "colors for sprite pal 2"),
77 RCV_BOOL("sprsort", &sprsort, "speedhack: disable sprite sorting"),
78 RCV_BOOL("sprdebug", &sprdebug, "debug sprite visibility"),
79 RCV_BOOL("colorfilter", &usefilter, "washed out look like a real CGB"),
80 RCV_BOOL("filterdmg", &filterdmg, "like colorfilter but for DMG"),
81 RCV_VECTOR("red", filter[0], 4, "filter values for red"),
82 RCV_VECTOR("green", filter[1], 4, "filter values for green"),
83 RCV_VECTOR("blue", filter[2], 4, "filter values for blue"),
84 RCV_END
87 static byte *vdest;
89 #ifdef ALLOW_UNALIGNED_IO /* long long is ok since this is i386-only anyway? */
90 #define MEMCPY8(d, s) ((*(long long *)(d)) = (*(long long *)(s)))
91 #else
92 #define MEMCPY8(d, s) memcpy((d), (s), 8)
93 #endif
98 #ifndef ASM_UPDATEPATPIX
99 void updatepatpix()
101 int i, j, k;
102 int a, c;
103 byte *vram = lcd.vbank[0];
105 if (!anydirty) return;
106 for (i = 0; i < 1024; i++)
108 if (i == 384) i = 512;
109 if (i == 896) break;
110 if (!patdirty[i]) continue;
111 patdirty[i] = 0;
112 for (j = 0; j < 8; j++)
114 a = ((i<<4) | (j<<1));
115 for (k = 0; k < 8; k++)
117 c = vram[a] & (1<<k) ? 1 : 0;
118 c |= vram[a+1] & (1<<k) ? 2 : 0;
119 patpix[i+1024][j][k] = c;
121 for (k = 0; k < 8; k++)
122 patpix[i][j][k] =
123 patpix[i+1024][j][7-k];
125 for (j = 0; j < 8; j++)
127 for (k = 0; k < 8; k++)
129 patpix[i+2048][j][k] =
130 patpix[i][7-j][k];
131 patpix[i+3072][j][k] =
132 patpix[i+1024][7-j][k];
136 anydirty = 0;
138 #endif /* ASM_UPDATEPATPIX */
142 void tilebuf()
144 int i, cnt;
145 int base;
146 byte *tilemap, *attrmap;
147 int *tilebuf;
148 int *wrap;
149 static int wraptable[64] =
151 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
152 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,-32
155 base = ((R_LCDC&0x08)?0x1C00:0x1800) + (T<<5) + S;
156 tilemap = lcd.vbank[0] + base;
157 attrmap = lcd.vbank[1] + base;
158 tilebuf = BG;
159 wrap = wraptable + S;
160 cnt = ((WX + 7) >> 3) + 1;
162 if (hw.cgb)
164 if (R_LCDC & 0x10)
165 for (i = cnt; i > 0; i--)
167 *(tilebuf++) = *tilemap
168 | (((int)*attrmap & 0x08) << 6)
169 | (((int)*attrmap & 0x60) << 5);
170 *(tilebuf++) = (((int)*attrmap & 0x07) << 2);
171 attrmap += *wrap + 1;
172 tilemap += *(wrap++) + 1;
174 else
175 for (i = cnt; i > 0; i--)
177 *(tilebuf++) = (256 + ((n8)*tilemap))
178 | (((int)*attrmap & 0x08) << 6)
179 | (((int)*attrmap & 0x60) << 5);
180 *(tilebuf++) = (((int)*attrmap & 0x07) << 2);
181 attrmap += *wrap + 1;
182 tilemap += *(wrap++) + 1;
185 else
187 if (R_LCDC & 0x10)
188 for (i = cnt; i > 0; i--)
190 *(tilebuf++) = *(tilemap++);
191 tilemap += *(wrap++);
193 else
194 for (i = cnt; i > 0; i--)
196 *(tilebuf++) = (256 + ((n8)*(tilemap++)));
197 tilemap += *(wrap++);
201 if (WX >= 160) return;
203 base = ((R_LCDC&0x40)?0x1C00:0x1800) + (WT<<5);
204 tilemap = lcd.vbank[0] + base;
205 attrmap = lcd.vbank[1] + base;
206 tilebuf = WND;
207 cnt = ((160 - WX) >> 3) + 1;
209 if (hw.cgb)
211 if (R_LCDC & 0x10)
212 for (i = cnt; i > 0; i--)
214 *(tilebuf++) = *(tilemap++)
215 | (((int)*attrmap & 0x08) << 6)
216 | (((int)*attrmap & 0x60) << 5);
217 *(tilebuf++) = (((int)*(attrmap++)&7) << 2);
219 else
220 for (i = cnt; i > 0; i--)
222 *(tilebuf++) = (256 + ((n8)*(tilemap++)))
223 | (((int)*attrmap & 0x08) << 6)
224 | (((int)*attrmap & 0x60) << 5);
225 *(tilebuf++) = (((int)*(attrmap++)&7) << 2);
228 else
230 if (R_LCDC & 0x10)
231 for (i = cnt; i > 0; i--)
232 *(tilebuf++) = *(tilemap++);
233 else
234 for (i = cnt; i > 0; i--)
235 *(tilebuf++) = (256 + ((n8)*(tilemap++)));
240 void bg_scan()
242 int cnt;
243 byte *src, *dest;
244 int *tile;
246 if (WX <= 0) return;
247 cnt = WX;
248 tile = BG;
249 dest = BUF;
251 src = patpix[*(tile++)][V] + U;
252 memcpy(dest, src, 8-U);
253 dest += 8-U;
254 cnt -= 8-U;
255 if (cnt <= 0) return;
256 while (cnt >= 8)
258 src = patpix[*(tile++)][V];
259 MEMCPY8(dest, src);
260 dest += 8;
261 cnt -= 8;
263 src = patpix[*tile][V];
264 while (cnt--)
265 *(dest++) = *(src++);
268 void wnd_scan()
270 int cnt;
271 byte *src, *dest;
272 int *tile;
274 if (WX >= 160) return;
275 cnt = 160 - WX;
276 tile = WND;
277 dest = BUF + WX;
279 while (cnt >= 8)
281 src = patpix[*(tile++)][WV];
282 MEMCPY8(dest, src);
283 dest += 8;
284 cnt -= 8;
286 src = patpix[*tile][WV];
287 while (cnt--)
288 *(dest++) = *(src++);
291 static void blendcpy(byte *dest, byte *src, byte b, int cnt)
293 while (cnt--) *(dest++) = *(src++) | b;
296 static int priused(void *attr)
298 un32 *a = attr;
299 return (int)((a[0]|a[1]|a[2]|a[3]|a[4]|a[5]|a[6]|a[7])&0x80808080);
302 void bg_scan_pri()
304 int cnt, i;
305 byte *src, *dest;
307 if (WX <= 0) return;
308 i = S;
309 cnt = WX;
310 dest = PRI;
311 src = lcd.vbank[1] + ((R_LCDC&0x08)?0x1C00:0x1800) + (T<<5);
313 if (!priused(src))
315 memset(dest, 0, cnt);
316 return;
319 memset(dest, src[i++&31]&128, 8-U);
320 dest += 8-U;
321 cnt -= 8-U;
322 if (cnt <= 0) return;
323 while (cnt >= 8)
325 memset(dest, src[i++&31]&128, 8);
326 dest += 8;
327 cnt -= 8;
329 memset(dest, src[i&31]&128, cnt);
332 void wnd_scan_pri()
334 int cnt, i;
335 byte *src, *dest;
337 if (WX >= 160) return;
338 i = 0;
339 cnt = 160 - WX;
340 dest = PRI + WX;
341 src = lcd.vbank[1] + ((R_LCDC&0x40)?0x1C00:0x1800) + (WT<<5);
343 if (!priused(src))
345 memset(dest, 0, cnt);
346 return;
349 while (cnt >= 8)
351 memset(dest, src[i++]&128, 8);
352 dest += 8;
353 cnt -= 8;
355 memset(dest, src[i]&128, cnt);
358 #ifndef ASM_BG_SCAN_COLOR
359 void bg_scan_color()
361 int cnt;
362 byte *src, *dest;
363 int *tile;
365 if (WX <= 0) return;
366 cnt = WX;
367 tile = BG;
368 dest = BUF;
370 src = patpix[*(tile++)][V] + U;
371 blendcpy(dest, src, *(tile++), 8-U);
372 dest += 8-U;
373 cnt -= 8-U;
374 if (cnt <= 0) return;
375 while (cnt >= 8)
377 src = patpix[*(tile++)][V];
378 blendcpy(dest, src, *(tile++), 8);
379 dest += 8;
380 cnt -= 8;
382 src = patpix[*(tile++)][V];
383 blendcpy(dest, src, *(tile++), cnt);
385 #endif
387 void wnd_scan_color()
389 int cnt;
390 byte *src, *dest;
391 int *tile;
393 if (WX >= 160) return;
394 cnt = 160 - WX;
395 tile = WND;
396 dest = BUF + WX;
398 while (cnt >= 8)
400 src = patpix[*(tile++)][WV];
401 blendcpy(dest, src, *(tile++), 8);
402 dest += 8;
403 cnt -= 8;
405 src = patpix[*(tile++)][WV];
406 blendcpy(dest, src, *(tile++), cnt);
409 static void recolor(byte *buf, byte fill, int cnt)
411 while (cnt--) *(buf++) |= fill;
414 void spr_count()
416 int i;
417 struct obj *o;
419 NS = 0;
420 if (!(R_LCDC & 0x02)) return;
422 o = lcd.oam.obj;
424 for (i = 40; i; i--, o++)
426 if (L >= o->y || L + 16 < o->y)
427 continue;
428 if (L + 8 >= o->y && !(R_LCDC & 0x04))
429 continue;
430 if (++NS == 10) break;
434 void spr_enum()
436 int i, j;
437 struct obj *o;
438 struct vissprite ts[10];
439 int v, pat;
440 int l, x;
442 NS = 0;
443 if (!(R_LCDC & 0x02)) return;
445 o = lcd.oam.obj;
447 for (i = 40; i; i--, o++)
449 if (L >= o->y || L + 16 < o->y)
450 continue;
451 if (L + 8 >= o->y && !(R_LCDC & 0x04))
452 continue;
453 VS[NS].x = (int)o->x - 8;
454 v = L - (int)o->y + 16;
455 if (hw.cgb)
457 pat = o->pat | (((int)o->flags & 0x60) << 5)
458 | (((int)o->flags & 0x08) << 6);
459 VS[NS].pal = 32 + ((o->flags & 0x07) << 2);
461 else
463 pat = o->pat | (((int)o->flags & 0x60) << 5);
464 VS[NS].pal = 32 + ((o->flags & 0x10) >> 2);
466 VS[NS].pri = (o->flags & 0x80) >> 7;
467 if ((R_LCDC & 0x04))
469 pat &= ~1;
470 if (v >= 8)
472 v -= 8;
473 pat++;
475 if (o->flags & 0x40) pat ^= 1;
477 VS[NS].buf = patpix[pat][v];
478 if (++NS == 10) break;
480 if (!sprsort || hw.cgb) return;
481 /* not quite optimal but it finally works! */
482 for (i = 0; i < NS; i++)
484 l = 0;
485 x = VS[0].x;
486 for (j = 1; j < NS; j++)
488 if (VS[j].x < x)
490 l = j;
491 x = VS[j].x;
494 ts[i] = VS[l];
495 VS[l].x = 160;
497 memcpy(VS, ts, sizeof VS);
500 void spr_scan()
502 int i, x;
503 byte pal, b, ns = NS;
504 byte *src, *dest, *bg, *pri;
505 struct vissprite *vs;
506 static byte bgdup[256];
508 if (!ns) return;
510 memcpy(bgdup, BUF, 256);
511 vs = &VS[ns-1];
513 for (; ns; ns--, vs--)
515 x = vs->x;
516 if (x >= 160) continue;
517 if (x <= -8) continue;
518 if (x < 0)
520 src = vs->buf - x;
521 dest = BUF;
522 i = 8 + x;
524 else
526 src = vs->buf;
527 dest = BUF + x;
528 if (x > 152) i = 160 - x;
529 else i = 8;
531 pal = vs->pal;
532 if (vs->pri)
534 bg = bgdup + (dest - BUF);
535 while (i--)
537 b = src[i];
538 if (b && !(bg[i]&3)) dest[i] = pal|b;
541 else if (hw.cgb)
543 bg = bgdup + (dest - BUF);
544 pri = PRI + (dest - BUF);
545 while (i--)
547 b = src[i];
548 if (b && (!pri[i] || !(bg[i]&3)))
549 dest[i] = pal|b;
552 else while (i--) if (src[i]) dest[i] = pal|src[i];
553 /* else while (i--) if (src[i]) dest[i] = 31 + ns; */
555 if (sprdebug) for (i = 0; i < NS; i++) BUF[i<<1] = 36;
563 void lcd_begin()
565 if (fb.indexed)
567 if (rgb332) pal_set332();
568 else pal_expire();
570 while (scale * 160 > fb.w || scale * 144 > fb.h) scale--;
571 vdest = fb.ptr + ((fb.w*fb.pelsize)>>1)
572 - (80*fb.pelsize) * scale
573 + ((fb.h>>1) - 72*scale) * fb.pitch;
574 WY = R_WY;
577 void lcd_refreshline()
579 int i, work_scale;
580 byte scalebuf[160*4*MAX_SCALE], *dest;
582 if (!fb.enabled) return;
584 if (!(R_LCDC & 0x80))
585 return; /* should not happen... */
587 updatepatpix();
589 L = R_LY;
590 X = R_SCX;
591 Y = (R_SCY + L) & 0xff;
592 S = X >> 3;
593 T = Y >> 3;
594 U = X & 7;
595 V = Y & 7;
597 WX = R_WX - 7;
598 if (WY>L || WY<0 || WY>143 || WX<-7 || WX>159 || !(R_LCDC&0x20))
599 WX = 160;
600 WT = (L - WY) >> 3;
601 WV = (L - WY) & 7;
603 spr_enum();
605 tilebuf();
606 if (hw.cgb)
608 bg_scan_color();
609 wnd_scan_color();
610 if (NS)
612 bg_scan_pri();
613 wnd_scan_pri();
616 else
618 bg_scan();
619 wnd_scan();
620 recolor(BUF+WX, 0x04, 160-WX);
622 spr_scan();
624 if (fb.dirty) memset(fb.ptr, 0, fb.pitch * fb.h);
625 fb.dirty = 0;
626 if (density > scale) density = scale;
627 if (scale == 1 || fb.delegate_scaling) density = 1;
629 work_scale = fb.delegate_scaling ? 1: scale;
631 dest = (density != 1) ? scalebuf : vdest;
632 if(work_scale > MAX_SCALE) {
633 die("error: no code available to scale > %d!", MAX_SCALE);
636 switch (work_scale)
638 case 0:
639 case 1:
640 switch (fb.pelsize)
642 case 1:
643 refresh_1(dest, BUF, PAL1, 160);
644 break;
645 case 2:
646 refresh_2(dest, BUF, PAL2, 160);
647 break;
648 case 3:
649 refresh_3(dest, BUF, PAL4, 160);
650 break;
651 case 4:
652 refresh_4(dest, BUF, PAL4, 160);
653 break;
655 break;
656 case 2:
657 switch (fb.pelsize)
659 case 1:
660 refresh_2(dest, BUF, PAL2, 160);
661 break;
662 case 2:
663 refresh_4(dest, BUF, PAL4, 160);
664 break;
665 case 3:
666 refresh_3_2x(dest, BUF, PAL4, 160);
667 break;
668 case 4:
669 refresh_4_2x(dest, BUF, PAL4, 160);
670 break;
672 break;
673 case 3:
674 switch (fb.pelsize)
676 case 1:
677 refresh_3(dest, BUF, PAL4, 160);
678 break;
679 case 2:
680 refresh_2_3x(dest, BUF, PAL2, 160);
681 break;
682 case 3:
683 refresh_3_3x(dest, BUF, PAL4, 160);
684 break;
685 case 4:
686 refresh_4_3x(dest, BUF, PAL4, 160);
687 break;
689 break;
690 case 4:
691 switch (fb.pelsize)
693 case 1:
694 refresh_4(dest, BUF, PAL4, 160);
695 break;
696 case 2:
697 refresh_4_2x(dest, BUF, PAL4, 160);
698 break;
699 case 3:
700 refresh_3_4x(dest, BUF, PAL4, 160);
701 break;
702 case 4:
703 refresh_4_4x(dest, BUF, PAL4, 160);
704 break;
706 break;
707 default:
708 break;
711 if (density != 1)
713 for (i = 0; i < scale; i++)
715 if ((i < density) || ((density <= 0) && !(i&1)))
716 memcpy(vdest, scalebuf, 160 * fb.pelsize * scale);
717 vdest += fb.pitch;
720 else vdest += fb.pitch * work_scale;
729 static void updatepalette(int i)
731 int c, r, g, b, y, u, v, rr, gg;
733 c = (lcd.pal[i<<1] | ((int)lcd.pal[(i<<1)|1] << 8)) & 0x7FFF;
734 r = (c & 0x001F) << 3;
735 g = (c & 0x03E0) >> 2;
736 b = (c & 0x7C00) >> 7;
737 r |= (r >> 5);
738 g |= (g >> 5);
739 b |= (b >> 5);
741 if (usefilter && (filterdmg || hw.cgb))
743 rr = ((r * filter[0][0] + g * filter[0][1] + b * filter[0][2]) >> 8) + filter[0][3];
744 gg = ((r * filter[1][0] + g * filter[1][1] + b * filter[1][2]) >> 8) + filter[1][3];
745 b = ((r * filter[2][0] + g * filter[2][1] + b * filter[2][2]) >> 8) + filter[2][3];
746 r = rr;
747 g = gg;
750 if (fb.yuv)
752 y = (((r * 263) + (g * 516) + (b * 100)) >> 10) + 16;
753 u = (((r * 450) - (g * 377) - (b * 73)) >> 10) + 128;
754 v = (((r * -152) - (g * 298) + (b * 450)) >> 10) + 128;
755 if (y < 0) y = 0;
756 if (y > 255) y = 255;
757 if (u < 0) u = 0;
758 if (u > 255) u = 255;
759 if (v < 0) v = 0;
760 if (v > 255) v = 255;
761 PAL4[i] = (y<<fb.cc[0].l) | (y<<fb.cc[3].l)
762 | (u<<fb.cc[1].l) | (v<<fb.cc[2].l);
763 return;
766 if (fb.indexed)
768 pal_release(PAL1[i]);
769 c = pal_getcolor(c, r, g, b);
770 PAL1[i] = c;
771 PAL2[i] = (c<<8) | c;
772 PAL4[i] = (c<<24) | (c<<16) | (c<<8) | c;
773 return;
776 r = (r >> fb.cc[0].r) << fb.cc[0].l;
777 g = (g >> fb.cc[1].r) << fb.cc[1].l;
778 b = (b >> fb.cc[2].r) << fb.cc[2].l;
779 c = r|g|b;
781 switch (fb.pelsize)
783 case 1:
784 PAL1[i] = c;
785 PAL2[i] = (c<<8) | c;
786 PAL4[i] = (c<<24) | (c<<16) | (c<<8) | c;
787 break;
788 case 2:
789 PAL2[i] = c;
790 PAL4[i] = (c<<16) | c;
791 break;
792 case 3:
793 case 4:
794 PAL4[i] = c;
795 break;
799 void pal_write(int i, byte b)
801 if (lcd.pal[i] == b) return;
802 lcd.pal[i] = b;
803 updatepalette(i>>1);
806 void pal_write_dmg(int i, int mapnum, byte d)
808 int j;
809 int *cmap = dmg_pal[mapnum];
810 int c, r, g, b;
812 if (hw.cgb) return;
814 /* if (mapnum >= 2) d = 0xe4; */
815 for (j = 0; j < 8; j += 2)
817 c = cmap[(d >> j) & 3];
818 r = (c & 0xf8) >> 3;
819 g = (c & 0xf800) >> 6;
820 b = (c & 0xf80000) >> 9;
821 c = r|g|b;
822 /* FIXME - handle directly without faking cgb */
823 pal_write(i+j, c & 0xff);
824 pal_write(i+j+1, c >> 8);
828 void vram_write(int a, byte b)
830 lcd.vbank[R_VBK&1][a] = b;
831 if (a >= 0x1800) return;
832 patdirty[((R_VBK&1)<<9)+(a>>4)] = 1;
833 anydirty = 1;
836 void vram_dirty()
838 anydirty = 1;
839 memset(patdirty, 1, sizeof patdirty);
842 void pal_dirty()
844 int i;
845 if (!hw.cgb)
847 pal_write_dmg(0, 0, R_BGP);
848 pal_write_dmg(8, 1, R_BGP);
849 pal_write_dmg(64, 2, R_OBP0);
850 pal_write_dmg(72, 3, R_OBP1);
852 for (i = 0; i < 64; i++)
853 updatepalette(i);
856 void lcd_reset()
858 memset(&lcd, 0, sizeof lcd);
859 lcd_begin();
860 vram_dirty();
861 pal_dirty();