update README
[rofl0r-gnuboy.git] / lcd.c
blob05eff819c2bb368671d40aff2b06bb1b114a7fd8
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 #if defined(ALLOW_UNALIGNED_IO) && defined(__GNUC__)
90 #include <stdint.h>
91 static __inline void memcpy8(void *__restrict dest, const void *__restrict src)
93 typedef uint64_t u64a __attribute__((__may_alias__));
94 u64a *d = dest; u64a const* s = src;
95 *d = *s;
97 #define MEMCPY8(d, s) memcpy8(d, s)
98 #else
99 #define MEMCPY8(d, s) memcpy((d), (s), 8)
100 #endif
105 #ifndef ASM_UPDATEPATPIX
106 void updatepatpix()
108 int i, j, k;
109 int a, c;
110 byte *vram = lcd.vbank[0];
112 if (!anydirty) return;
113 for (i = 0; i < 1024; i++)
115 if (i == 384) i = 512;
116 if (i == 896) break;
117 if (!patdirty[i]) continue;
118 patdirty[i] = 0;
119 for (j = 0; j < 8; j++)
121 a = ((i<<4) | (j<<1));
122 for (k = 0; k < 8; k++)
124 c = vram[a] & (1<<k) ? 1 : 0;
125 c |= vram[a+1] & (1<<k) ? 2 : 0;
126 patpix[i+1024][j][k] = c;
128 for (k = 0; k < 8; k++)
129 patpix[i][j][k] =
130 patpix[i+1024][j][7-k];
132 for (j = 0; j < 8; j++)
134 for (k = 0; k < 8; k++)
136 patpix[i+2048][j][k] =
137 patpix[i][7-j][k];
138 patpix[i+3072][j][k] =
139 patpix[i+1024][7-j][k];
143 anydirty = 0;
145 #endif /* ASM_UPDATEPATPIX */
149 void tilebuf()
151 int i, cnt;
152 int base;
153 byte *tilemap, *attrmap;
154 int *tilebuf;
155 int *wrap;
156 static int wraptable[64] =
158 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
159 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,-32
162 base = ((R_LCDC & LCDC_BIT_BG_MAP)?0x1C00:0x1800) + (T<<5) + S;
163 tilemap = lcd.vbank[0] + base;
164 attrmap = lcd.vbank[1] + base;
165 tilebuf = BG;
166 wrap = wraptable + S;
167 cnt = ((WX + 7) >> 3) + 1;
169 if (hw.cgb)
171 if (R_LCDC & LCDC_BIT_TILE_SEL)
172 for (i = cnt; i > 0; i--)
174 *(tilebuf++) = *tilemap
175 | (((int)*attrmap & 0x08) << 6)
176 | (((int)*attrmap & 0x60) << 5);
177 *(tilebuf++) = (((int)*attrmap & 0x07) << 2);
178 attrmap += *wrap + 1;
179 tilemap += *(wrap++) + 1;
181 else
182 for (i = cnt; i > 0; i--)
184 *(tilebuf++) = (256 + ((n8)*tilemap))
185 | (((int)*attrmap & 0x08) << 6)
186 | (((int)*attrmap & 0x60) << 5);
187 *(tilebuf++) = (((int)*attrmap & 0x07) << 2);
188 attrmap += *wrap + 1;
189 tilemap += *(wrap++) + 1;
192 else
194 if (R_LCDC & LCDC_BIT_TILE_SEL)
195 for (i = cnt; i > 0; i--)
197 *(tilebuf++) = *(tilemap++);
198 tilemap += *(wrap++);
200 else
201 for (i = cnt; i > 0; i--)
203 *(tilebuf++) = (256 + ((n8)*(tilemap++)));
204 tilemap += *(wrap++);
208 if (WX >= 160) return;
210 base = ((R_LCDC & LCDC_BIT_WIN_MAP)?0x1C00:0x1800) + (WT<<5);
211 tilemap = lcd.vbank[0] + base;
212 attrmap = lcd.vbank[1] + base;
213 tilebuf = WND;
214 cnt = ((160 - WX) >> 3) + 1;
216 if (hw.cgb)
218 if (R_LCDC & LCDC_BIT_TILE_SEL)
219 for (i = cnt; i > 0; i--)
221 *(tilebuf++) = *(tilemap++)
222 | (((int)*attrmap & 0x08) << 6)
223 | (((int)*attrmap & 0x60) << 5);
224 *(tilebuf++) = (((int)*(attrmap++)&7) << 2);
226 else
227 for (i = cnt; i > 0; i--)
229 *(tilebuf++) = (256 + ((n8)*(tilemap++)))
230 | (((int)*attrmap & 0x08) << 6)
231 | (((int)*attrmap & 0x60) << 5);
232 *(tilebuf++) = (((int)*(attrmap++)&7) << 2);
235 else
237 if (R_LCDC & LCDC_BIT_TILE_SEL)
238 for (i = cnt; i > 0; i--)
239 *(tilebuf++) = *(tilemap++);
240 else
241 for (i = cnt; i > 0; i--)
242 *(tilebuf++) = (256 + ((n8)*(tilemap++)));
247 void bg_scan()
249 int cnt;
250 byte *src, *dest;
251 int *tile;
253 if (WX <= 0) return;
254 cnt = WX;
255 tile = BG;
256 dest = BUF;
258 src = patpix[*(tile++)][V] + U;
259 memcpy(dest, src, 8-U);
260 dest += 8-U;
261 cnt -= 8-U;
262 if (cnt <= 0) return;
263 while (cnt >= 8)
265 src = patpix[*(tile++)][V];
266 MEMCPY8(dest, src);
267 dest += 8;
268 cnt -= 8;
270 src = patpix[*tile][V];
271 while (cnt--)
272 *(dest++) = *(src++);
275 void wnd_scan()
277 int cnt;
278 byte *src, *dest;
279 int *tile;
281 if (WX >= 160) return;
282 cnt = 160 - WX;
283 tile = WND;
284 dest = BUF + WX;
286 while (cnt >= 8)
288 src = patpix[*(tile++)][WV];
289 MEMCPY8(dest, src);
290 dest += 8;
291 cnt -= 8;
293 src = patpix[*tile][WV];
294 while (cnt--)
295 *(dest++) = *(src++);
298 static void blendcpy(byte *dest, byte *src, byte b, int cnt)
300 while (cnt--) *(dest++) = *(src++) | b;
303 static int priused(void *attr)
305 un32 *a = attr;
306 return (int)((a[0]|a[1]|a[2]|a[3]|a[4]|a[5]|a[6]|a[7])&0x80808080);
309 void bg_scan_pri()
311 int cnt, i;
312 byte *src, *dest;
314 if (WX <= 0) return;
315 i = S;
316 cnt = WX;
317 dest = PRI;
318 src = lcd.vbank[1] + ((R_LCDC & LCDC_BIT_BG_MAP)?0x1C00:0x1800) + (T<<5);
320 if (!priused(src))
322 memset(dest, 0, cnt);
323 return;
326 memset(dest, src[i++&31]&128, 8-U);
327 dest += 8-U;
328 cnt -= 8-U;
329 if (cnt <= 0) return;
330 while (cnt >= 8)
332 memset(dest, src[i++&31]&128, 8);
333 dest += 8;
334 cnt -= 8;
336 memset(dest, src[i&31]&128, cnt);
339 void wnd_scan_pri()
341 int cnt, i;
342 byte *src, *dest;
344 if (WX >= 160) return;
345 i = 0;
346 cnt = 160 - WX;
347 dest = PRI + WX;
348 src = lcd.vbank[1] + ((R_LCDC & LCDC_BIT_WIN_MAP)?0x1C00:0x1800) + (WT<<5);
350 if (!priused(src))
352 memset(dest, 0, cnt);
353 return;
356 while (cnt >= 8)
358 memset(dest, src[i++]&128, 8);
359 dest += 8;
360 cnt -= 8;
362 memset(dest, src[i]&128, cnt);
365 #ifndef ASM_BG_SCAN_COLOR
366 void bg_scan_color()
368 int cnt;
369 byte *src, *dest;
370 int *tile;
372 if (WX <= 0) return;
373 cnt = WX;
374 tile = BG;
375 dest = BUF;
377 src = patpix[*(tile++)][V] + U;
378 blendcpy(dest, src, *(tile++), 8-U);
379 dest += 8-U;
380 cnt -= 8-U;
381 if (cnt <= 0) return;
382 while (cnt >= 8)
384 src = patpix[*(tile++)][V];
385 blendcpy(dest, src, *(tile++), 8);
386 dest += 8;
387 cnt -= 8;
389 src = patpix[*(tile++)][V];
390 blendcpy(dest, src, *(tile++), cnt);
392 #endif
394 void wnd_scan_color()
396 int cnt;
397 byte *src, *dest;
398 int *tile;
400 if (WX >= 160) return;
401 cnt = 160 - WX;
402 tile = WND;
403 dest = BUF + WX;
405 while (cnt >= 8)
407 src = patpix[*(tile++)][WV];
408 blendcpy(dest, src, *(tile++), 8);
409 dest += 8;
410 cnt -= 8;
412 src = patpix[*(tile++)][WV];
413 blendcpy(dest, src, *(tile++), cnt);
416 static void recolor(byte *buf, byte fill, int cnt)
418 while (cnt--) *(buf++) |= fill;
421 void spr_count()
423 int i;
424 struct obj *o;
426 NS = 0;
427 if (!(R_LCDC & LCDC_BIT_OBJ_EN)) return;
429 o = lcd.oam.obj;
431 for (i = 40; i; i--, o++)
433 if (L >= o->y || L + 16 < o->y)
434 continue;
435 if (L + 8 >= o->y && !(R_LCDC & LCDC_BIT_OBJ_SIZE))
436 continue;
437 if (++NS == 10) break;
441 void spr_enum()
443 int i, j;
444 struct obj *o;
445 struct vissprite ts[10];
446 int v, pat;
447 int l, x;
449 NS = 0;
450 if (!(R_LCDC & LCDC_BIT_OBJ_EN)) return;
452 o = lcd.oam.obj;
454 for (i = 40; i; i--, o++)
456 if (L >= o->y || L + 16 < o->y)
457 continue;
458 if (L + 8 >= o->y && !(R_LCDC & LCDC_BIT_OBJ_SIZE))
459 continue;
460 VS[NS].x = (int)o->x - 8;
461 v = L - (int)o->y + 16;
462 if (hw.cgb)
464 pat = o->pat | (((int)o->flags & 0x60) << 5)
465 | (((int)o->flags & 0x08) << 6);
466 VS[NS].pal = 32 + ((o->flags & 0x07) << 2);
468 else
470 pat = o->pat | (((int)o->flags & 0x60) << 5);
471 VS[NS].pal = 32 + ((o->flags & 0x10) >> 2);
473 VS[NS].pri = (o->flags & 0x80) >> 7;
474 if ((R_LCDC & LCDC_BIT_OBJ_SIZE))
476 pat &= ~1;
477 if (v >= 8)
479 v -= 8;
480 pat++;
482 if (o->flags & 0x40) pat ^= 1;
484 VS[NS].buf = patpix[pat][v];
485 if (++NS == 10) break;
487 if (!sprsort || hw.cgb) return;
488 /* not quite optimal but it finally works! */
489 for (i = 0; i < NS; i++)
491 l = 0;
492 x = VS[0].x;
493 for (j = 1; j < NS; j++)
495 if (VS[j].x < x)
497 l = j;
498 x = VS[j].x;
501 ts[i] = VS[l];
502 VS[l].x = 160;
504 memcpy(VS, ts, sizeof ts);
507 void spr_scan()
509 int i, x;
510 byte pal, b, ns = NS;
511 byte *src, *dest, *bg, *pri;
512 struct vissprite *vs;
513 static byte bgdup[256];
515 if (!ns) return;
517 memcpy(bgdup, BUF, 256);
518 vs = &VS[ns-1];
520 for (; ns; ns--, vs--)
522 x = vs->x;
523 if (x >= 160) continue;
524 if (x <= -8) continue;
525 if (x < 0)
527 src = vs->buf - x;
528 dest = BUF;
529 i = 8 + x;
531 else
533 src = vs->buf;
534 dest = BUF + x;
535 if (x > 152) i = 160 - x;
536 else i = 8;
538 pal = vs->pal;
539 if (vs->pri)
541 bg = bgdup + (dest - BUF);
542 while (i--)
544 b = src[i];
545 if (b && !(bg[i]&3)) dest[i] = pal|b;
548 else if (hw.cgb)
550 bg = bgdup + (dest - BUF);
551 pri = PRI + (dest - BUF);
552 while (i--)
554 b = src[i];
555 if (b && (!pri[i] || !(bg[i]&3)))
556 dest[i] = pal|b;
559 else while (i--) if (src[i]) dest[i] = pal|src[i];
560 /* else while (i--) if (src[i]) dest[i] = 31 + ns; */
562 if (sprdebug) for (i = 0; i < NS; i++) BUF[i<<1] = 36;
570 void lcd_begin()
572 if (fb.indexed)
574 if (rgb332) pal_set332();
575 else pal_expire();
577 while (scale * 160 > fb.w || scale * 144 > fb.h) scale--;
578 vdest = fb.ptr + ((fb.w*fb.pelsize)>>1)
579 - (80*fb.pelsize) * scale
580 + ((fb.h>>1) - 72*scale) * fb.pitch;
581 WY = R_WY;
584 void lcd_refreshline()
586 int i, work_scale;
587 byte scalebuf[160*4*MAX_SCALE], *dest;
588 static int WL = 0;
590 if (!fb.enabled) return;
592 if (!(R_LCDC & LCDC_BIT_LCD_EN))
593 return; /* should not happen... */
595 updatepatpix();
597 L = R_LY;
598 X = R_SCX;
599 Y = (R_SCY + L) & 0xff;
600 S = X >> 3;
601 T = Y >> 3;
602 U = X & 7;
603 V = Y & 7;
604 if (L == 0) { WY = R_WY; WL = 0; }
606 WX = R_WX - 7;
607 if (WY>L || WY<0 || WY>143 || WX<-7 || WX>159 || !(R_LCDC & LCDC_BIT_WIN_EN))
608 WX = 160;
609 else {
610 /* in order to have these offsets correct we need to count
611 the number of lines that displayed a window */
612 WT = WL >> 3;
613 WV = WL & 7;
614 ++WL;
617 spr_enum();
619 tilebuf();
620 if (hw.cgb)
622 bg_scan_color();
623 wnd_scan_color();
624 if (NS)
626 bg_scan_pri();
627 wnd_scan_pri();
630 else
632 bg_scan();
633 wnd_scan();
634 recolor(BUF+WX, 0x04, 160-WX);
636 spr_scan();
638 if (fb.dirty) memset(fb.ptr, 0, fb.pitch * fb.h);
639 fb.dirty = 0;
640 if (density > scale) density = scale;
641 if (scale == 1 || fb.delegate_scaling) density = 1;
643 work_scale = fb.delegate_scaling ? 1: scale;
645 dest = (density != 1) ? scalebuf : vdest;
646 if(work_scale > MAX_SCALE) {
647 die("error: no code available to scale > %d!", MAX_SCALE);
650 switch (work_scale)
652 case 0:
653 case 1:
654 switch (fb.pelsize)
656 case 1:
657 refresh_1(dest, BUF, PAL1, 160);
658 break;
659 case 2:
660 refresh_2(dest, BUF, PAL2, 160);
661 break;
662 case 3:
663 refresh_3(dest, BUF, PAL4, 160);
664 break;
665 case 4:
666 refresh_4(dest, BUF, PAL4, 160);
667 break;
669 break;
670 case 2:
671 switch (fb.pelsize)
673 case 1:
674 refresh_2(dest, BUF, PAL2, 160);
675 break;
676 case 2:
677 refresh_4(dest, BUF, PAL4, 160);
678 break;
679 case 3:
680 refresh_3_2x(dest, BUF, PAL4, 160);
681 break;
682 case 4:
683 refresh_4_2x(dest, BUF, PAL4, 160);
684 break;
686 break;
687 case 3:
688 switch (fb.pelsize)
690 case 1:
691 refresh_3(dest, BUF, PAL4, 160);
692 break;
693 case 2:
694 refresh_2_3x(dest, BUF, PAL2, 160);
695 break;
696 case 3:
697 refresh_3_3x(dest, BUF, PAL4, 160);
698 break;
699 case 4:
700 refresh_4_3x(dest, BUF, PAL4, 160);
701 break;
703 break;
704 case 4:
705 switch (fb.pelsize)
707 case 1:
708 refresh_4(dest, BUF, PAL4, 160);
709 break;
710 case 2:
711 refresh_4_2x(dest, BUF, PAL4, 160);
712 break;
713 case 3:
714 refresh_3_4x(dest, BUF, PAL4, 160);
715 break;
716 case 4:
717 refresh_4_4x(dest, BUF, PAL4, 160);
718 break;
720 break;
721 default:
722 break;
725 if (density != 1)
727 for (i = 0; i < scale; i++)
729 if ((i < density) || ((density <= 0) && !(i&1)))
730 memcpy(vdest, scalebuf, 160 * fb.pelsize * scale);
731 vdest += fb.pitch;
734 else vdest += fb.pitch * work_scale;
743 static void updatepalette(int i)
745 int c, r, g, b, y, u, v, rr, gg;
747 c = (lcd.pal[i<<1] | ((int)lcd.pal[(i<<1)|1] << 8)) & 0x7FFF;
748 r = (c & 0x001F) << 3;
749 g = (c & 0x03E0) >> 2;
750 b = (c & 0x7C00) >> 7;
751 r |= (r >> 5);
752 g |= (g >> 5);
753 b |= (b >> 5);
755 if (usefilter && (filterdmg || hw.cgb))
757 rr = ((r * filter[0][0] + g * filter[0][1] + b * filter[0][2]) >> 8) + filter[0][3];
758 gg = ((r * filter[1][0] + g * filter[1][1] + b * filter[1][2]) >> 8) + filter[1][3];
759 b = ((r * filter[2][0] + g * filter[2][1] + b * filter[2][2]) >> 8) + filter[2][3];
760 r = rr;
761 g = gg;
764 if (fb.yuv)
766 y = (((r * 263) + (g * 516) + (b * 100)) >> 10) + 16;
767 u = (((r * 450) - (g * 377) - (b * 73)) >> 10) + 128;
768 v = (((r * -152) - (g * 298) + (b * 450)) >> 10) + 128;
769 if (y < 0) y = 0;
770 if (y > 255) y = 255;
771 if (u < 0) u = 0;
772 if (u > 255) u = 255;
773 if (v < 0) v = 0;
774 if (v > 255) v = 255;
775 PAL4[i] = (y<<fb.cc[0].l) | (y<<fb.cc[3].l)
776 | (u<<fb.cc[1].l) | (v<<fb.cc[2].l);
777 return;
780 if (fb.indexed)
782 pal_release(PAL1[i]);
783 c = pal_getcolor(c, r, g, b);
784 PAL1[i] = c;
785 PAL2[i] = (c<<8) | c;
786 PAL4[i] = (c<<24) | (c<<16) | (c<<8) | c;
787 return;
790 r = (r >> fb.cc[0].r) << fb.cc[0].l;
791 g = (g >> fb.cc[1].r) << fb.cc[1].l;
792 b = (b >> fb.cc[2].r) << fb.cc[2].l;
793 c = r|g|b;
795 switch (fb.pelsize)
797 case 1:
798 PAL1[i] = c;
799 PAL2[i] = (c<<8) | c;
800 PAL4[i] = (c<<24) | (c<<16) | (c<<8) | c;
801 break;
802 case 2:
803 PAL2[i] = c;
804 PAL4[i] = (c<<16) | c;
805 break;
806 case 3:
807 case 4:
808 PAL4[i] = c;
809 break;
813 void pal_write(int i, byte b)
815 if (lcd.pal[i] == b) return;
816 lcd.pal[i] = b;
817 updatepalette(i>>1);
820 void pal_write_dmg(int i, int mapnum, byte d)
822 int j;
823 int *cmap = dmg_pal[mapnum];
824 int c, r, g, b;
826 if (hw.cgb) return;
828 /* if (mapnum >= 2) d = 0xe4; */
829 for (j = 0; j < 8; j += 2)
831 c = cmap[(d >> j) & 3];
832 r = (c & 0xf8) >> 3;
833 g = (c & 0xf800) >> 6;
834 b = (c & 0xf80000) >> 9;
835 c = r|g|b;
836 /* FIXME - handle directly without faking cgb */
837 pal_write(i+j, c & 0xff);
838 pal_write(i+j+1, c >> 8);
842 void vram_write(int a, byte b)
844 lcd.vbank[R_VBK&1][a] = b;
845 if (a >= 0x1800) return;
846 patdirty[((R_VBK&1)<<9)+(a>>4)] = 1;
847 anydirty = 1;
850 void vram_dirty()
852 anydirty = 1;
853 memset(patdirty, 1, sizeof patdirty);
856 void pal_dirty()
858 int i;
859 if (!hw.cgb)
861 pal_write_dmg(0, 0, R_BGP);
862 pal_write_dmg(8, 1, R_BGP);
863 pal_write_dmg(64, 2, R_OBP0);
864 pal_write_dmg(72, 3, R_OBP1);
866 for (i = 0; i < 64; i++)
867 updatepalette(i);
870 void lcd_reset()
872 memset(&lcd, 0, sizeof lcd);
873 lcd_begin();
874 vram_dirty();
875 pal_dirty();