#include "rockmacros.h" #include "defs.h" #include "regs.h" #include "hw.h" #include "mem.h" #include "lcd-gb.h" #include "fb.h" #ifdef HAVE_LCD_COLOR #include "palette-presets.h" #endif #ifdef USE_ASM #include "asm.h" #endif struct lcd lcd; struct scan scan IBSS_ATTR; #define BG (scan.bg) #define WND (scan.wnd) #if LCD_DEPTH ==16 #define BUF (scan.buf) #else #define BUF (scan.buf[scanline_ind]) #endif #define PRI (scan.pri) #define PAL (scan.pal) #define VS (scan.vs) /* vissprites */ #define NS (scan.ns) #define L (scan.l) /* line */ #define X (scan.x) /* screen position */ #define Y (scan.y) #define S (scan.s) /* tilemap position */ #define T (scan.t) #define U (scan.u) /* position within tile */ #define V (scan.v) #define WX (scan.wx) #define WY (scan.wy) #define WT (scan.wt) #define WV (scan.wv) byte patpix[4096][8][8] #if defined(CPU_COLDFIRE) __attribute__ ((aligned(16))) /* to profit from burst mode */ #endif ; byte patdirty[1024]; byte anydirty; #if LCD_DEPTH < 16 static int scanline_ind=0; #endif static int dmg_pal[4][4]; #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256) unsigned char *vdest; #else fb_data *vdest; #endif #ifndef ASM_UPDATEPATPIX static void updatepatpix(void) ICODE_ATTR; static void updatepatpix(void) { int i, j; #if ((CONFIG_CPU != SH7034) && !defined(CPU_COLDFIRE)) int k, a, c; #endif byte *vram = lcd.vbank[0]; if (!anydirty) return; for (i = 0; i < 1024; i++) { if (i == 384) i = 512; if (i == 896) break; if (!patdirty[i]) continue; patdirty[i] = 0; for (j = 0; j < 8; j++) { #if CONFIG_CPU == SH7034 asm volatile ( "mov.w @%2,r1 \n" "swap.b r1,r2 \n" "mov #0,r0 \n" "shlr r1 \n" "rotcl r0 \n" "shlr r2 \n" "rotcl r0 \n" "mov.b r0,@%0 \n" "mov.b r0,@(7,%1) \n" "mov #0,r0 \n" "shlr r1 \n" "rotcl r0 \n" "shlr r2 \n" "rotcl r0 \n" "mov.b r0,@(1,%0) \n" "mov.b r0,@(6,%1) \n" "mov #0,r0 \n" "shlr r1 \n" "rotcl r0 \n" "shlr r2 \n" "rotcl r0 \n" "mov.b r0,@(2,%0) \n" "mov.b r0,@(5,%1) \n" "mov #0,r0 \n" "shlr r1 \n" "rotcl r0 \n" "shlr r2 \n" "rotcl r0 \n" "mov.b r0,@(3,%0) \n" "mov.b r0,@(4,%1) \n" "mov #0,r0 \n" "shlr r1 \n" "rotcl r0 \n" "shlr r2 \n" "rotcl r0 \n" "mov.b r0,@(4,%0) \n" "mov.b r0,@(3,%1) \n" "mov #0,r0 \n" "shlr r1 \n" "rotcl r0 \n" "shlr r2 \n" "rotcl r0 \n" "mov.b r0,@(5,%0) \n" "mov.b r0,@(2,%1) \n" "mov #0,r0 \n" "shlr r1 \n" "rotcl r0 \n" "shlr r2 \n" "rotcl r0 \n" "mov.b r0,@(6,%0) \n" "mov.b r0,@(1,%1) \n" "mov #0,r0 \n" "shlr r1 \n" "rotcl r0 \n" "shlr r2 \n" "rotcl r0 \n" "mov.b r0,@(7,%0) \n" "mov.b r0,@%1 \n" : /* outputs */ : /* inputs */ /* %0 */ "r"(patpix[i+1024][j]), /* %1 */ "r"(patpix[i][j]), /* %2 */ "r"(&vram[(i<<4)|(j<<1)]) : /* clobbers */ "r0", "r1", "r2" ); #elif defined(CPU_COLDFIRE) asm volatile ( "move.b (%2),%%d2 \n" "move.b (1,%2),%%d1 \n" "addq.l #8,%1 \n" "clr.l %%d0 \n" "lsr.l #1,%%d1 \n" "addx.l %%d0,%%d0 \n" "lsr.l #1,%%d2 \n" "addx.l %%d0,%%d0 \n" "move.b %%d0,-(%1) \n" "lsl.l #6,%%d0 \n" "lsr.l #1,%%d1 \n" "addx.l %%d0,%%d0 \n" "lsr.l #1,%%d2 \n" "addx.l %%d0,%%d0 \n" "move.b %%d0,-(%1) \n" "lsl.l #6,%%d0 \n" "lsr.l #1,%%d1 \n" "addx.l %%d0,%%d0 \n" "lsr.l #1,%%d2 \n" "addx.l %%d0,%%d0 \n" "move.b %%d0,-(%1) \n" "lsl.l #6,%%d0 \n" "lsr.l #1,%%d1 \n" "addx.l %%d0,%%d0 \n" "lsr.l #1,%%d2 \n" "addx.l %%d0,%%d0 \n" "move.l %%d0,(%0) \n" "move.b %%d0,-(%1) \n" "clr.l %%d0 \n" "lsr.l #1,%%d1 \n" "addx.l %%d0,%%d0 \n" "lsr.l #1,%%d2 \n" "addx.l %%d0,%%d0 \n" "move.b %%d0,-(%1) \n" "lsl.l #6,%%d0 \n" "lsr.l #1,%%d1 \n" "addx.l %%d0,%%d0 \n" "lsr.l #1,%%d2 \n" "addx.l %%d0,%%d0 \n" "move.b %%d0,-(%1) \n" "lsl.l #6,%%d0 \n" "lsr.l #1,%%d1 \n" "addx.l %%d0,%%d0 \n" "lsr.l #1,%%d2 \n" "addx.l %%d0,%%d0 \n" "move.b %%d0,-(%1) \n" "lsl.l #6,%%d0 \n" "lsr.l #1,%%d1 \n" "addx.l %%d0,%%d0 \n" "lsr.l #1,%%d2 \n" "addx.l %%d0,%%d0 \n" "move.l %%d0,(4,%0) \n" "move.b %%d0,-(%1) \n" : /* outputs */ : /* inputs */ /* %0 */ "a"(patpix[i+1024][j]), /* %1 */ "a"(patpix[i][j]), /* %2 */ "a"(&vram[(i<<4)|(j<<1)]) : /* clobbers */ "d0", "d1", "d2" ); #else a = ((i<<4) | (j<<1)); for (k = 0; k < 8; k++) { c = vram[a] & BIT_N(k) ? 1 : 0; c |= vram[a+1] & BIT_N(k) ? 2 : 0; patpix[i+1024][j][k] = c; } for (k = 0; k < 8; k++) patpix[i][j][k] = patpix[i+1024][j][7-k]; #endif } #if CONFIG_CPU == SH7034 asm volatile ( "mov.l @%0,r0 \n" "mov.l @(4,%0),r1 \n" "mov.l r0,@(56,%1) \n" "mov.l r1,@(60,%1) \n" "mov.l @(8,%0),r0 \n" "mov.l @(12,%0),r1 \n" "mov.l r0,@(48,%1) \n" "mov.l r1,@(52,%1) \n" "mov.l @(16,%0),r0 \n" "mov.l @(20,%0),r1 \n" "mov.l r0,@(40,%1) \n" "mov.l r1,@(44,%1) \n" "mov.l @(24,%0),r0 \n" "mov.l @(28,%0),r1 \n" "mov.l r0,@(32,%1) \n" "mov.l r1,@(36,%1) \n" "mov.l @(32,%0),r0 \n" "mov.l @(36,%0),r1 \n" "mov.l r0,@(24,%1) \n" "mov.l r1,@(28,%1) \n" "mov.l @(40,%0),r0 \n" "mov.l @(44,%0),r1 \n" "mov.l r0,@(16,%1) \n" "mov.l r1,@(20,%1) \n" "mov.l @(48,%0),r0 \n" "mov.l @(52,%0),r1 \n" "mov.l r0,@(8,%1) \n" "mov.l r1,@(12,%1) \n" "mov.l @(56,%0),r0 \n" "mov.l @(60,%0),r1 \n" "mov.l r0,@%1 \n" "mov.l r1,@(4,%1) \n" "add %2,%0 \n" "add %2,%1 \n" "mov.l @%0,r0 \n" "mov.l @(4,%0),r1 \n" "mov.l r0,@(56,%1) \n" "mov.l r1,@(60,%1) \n" "mov.l @(8,%0),r0 \n" "mov.l @(12,%0),r1 \n" "mov.l r0,@(48,%1) \n" "mov.l r1,@(52,%1) \n" "mov.l @(16,%0),r0 \n" "mov.l @(20,%0),r1 \n" "mov.l r0,@(40,%1) \n" "mov.l r1,@(44,%1) \n" "mov.l @(24,%0),r0 \n" "mov.l @(28,%0),r1 \n" "mov.l r0,@(32,%1) \n" "mov.l r1,@(36,%1) \n" "mov.l @(32,%0),r0 \n" "mov.l @(36,%0),r1 \n" "mov.l r0,@(24,%1) \n" "mov.l r1,@(28,%1) \n" "mov.l @(40,%0),r0 \n" "mov.l @(44,%0),r1 \n" "mov.l r0,@(16,%1) \n" "mov.l r1,@(20,%1) \n" "mov.l @(48,%0),r0 \n" "mov.l @(52,%0),r1 \n" "mov.l r0,@(8,%1) \n" "mov.l r1,@(12,%1) \n" "mov.l @(56,%0),r0 \n" "mov.l @(60,%0),r1 \n" "mov.l r0,@%1 \n" "mov.l r1,@(4,%1) \n" : /* outputs */ : /* inputs */ /* %0 */ "r"(patpix[i][0]), /* %1 */ "r"(patpix[i+2048][0]), /* %2 */ "r"(1024*64) : /* clobbers */ "r0", "r1" ); #elif defined(CPU_COLDFIRE) asm volatile ( "movem.l (%0),%%d0-%%d3 \n" "move.l %%d0,%%d4 \n" "move.l %%d1,%%d5 \n" "movem.l %%d2-%%d5,(48,%1) \n" "movem.l (16,%0),%%d0-%%d3 \n" "move.l %%d0,%%d4 \n" "move.l %%d1,%%d5 \n" "movem.l %%d2-%%d5,(32,%1) \n" "movem.l (32,%0),%%d0-%%d3 \n" "move.l %%d0,%%d4 \n" "move.l %%d1,%%d5 \n" "movem.l %%d2-%%d5,(16,%1) \n" "movem.l (48,%0),%%d0-%%d3 \n" "move.l %%d0,%%d4 \n" "move.l %%d1,%%d5 \n" "movem.l %%d2-%%d5,(%1) \n" "move.l %2,%%d0 \n" "add.l %%d0,%0 \n" "add.l %%d0,%1 \n" "movem.l (%0),%%d0-%%d3 \n" "move.l %%d0,%%d4 \n" "move.l %%d1,%%d5 \n" "movem.l %%d2-%%d5,(48,%1) \n" "movem.l (16,%0),%%d0-%%d3 \n" "move.l %%d0,%%d4 \n" "move.l %%d1,%%d5 \n" "movem.l %%d2-%%d5,(32,%1) \n" "movem.l (32,%0),%%d0-%%d3 \n" "move.l %%d0,%%d4 \n" "move.l %%d1,%%d5 \n" "movem.l %%d2-%%d5,(16,%1) \n" "movem.l (48,%0),%%d0-%%d3 \n" "move.l %%d0,%%d4 \n" "move.l %%d1,%%d5 \n" "movem.l %%d2-%%d5,(%1) \n" : /* outputs */ : /* inputs */ /* %0 */ "a"(patpix[i][0]), /* %1 */ "a"(patpix[i+2048][0]), /* %2 */ "i"(1024*64) : /* clobbers */ "d0", "d1", "d2", "d3", "d4", "d5" ); #else for (j = 0; j < 8; j++) { for (k = 0; k < 8; k++) { patpix[i+2048][j][k] = patpix[i][7-j][k]; patpix[i+3072][j][k] = patpix[i+1024][7-j][k]; } } #endif } anydirty = 0; } #endif /* ASM_UPDATEPATPIX */ static void tilebuf(void) ICODE_ATTR; static void tilebuf(void) { int i, cnt; int base; byte *tilemap, *attrmap; int *tilebuf; int *wrap; static int wraptable[64] = { 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,-32 }; base = ((R_LCDC&0x08)?0x1C00:0x1800) + (T<<5) + S; tilemap = lcd.vbank[0] + base; attrmap = lcd.vbank[1] + base; tilebuf = BG; wrap = wraptable + S; cnt = ((WX + 7) >> 3) + 1; if (hw.cgb) { if (R_LCDC & 0x10) for (i = cnt; i > 0; i--) { *(tilebuf++) = *tilemap | (((int)*attrmap & 0x08) << 6) | (((int)*attrmap & 0x60) << 5); *(tilebuf++) = (((int)*attrmap & 0x07) << 2); attrmap += *wrap + 1; tilemap += *(wrap++) + 1; } else for (i = cnt; i > 0; i--) { *(tilebuf++) = (256 + ((n8)*tilemap)) | (((int)*attrmap & 0x08) << 6) | (((int)*attrmap & 0x60) << 5); *(tilebuf++) = (((int)*attrmap & 0x07) << 2); attrmap += *wrap + 1; tilemap += *(wrap++) + 1; } } else { if (R_LCDC & 0x10) for (i = cnt; i > 0; i--) { *(tilebuf++) = *(tilemap++); tilemap += *(wrap++); } else for (i = cnt; i > 0; i--) { *(tilebuf++) = (256 + ((n8)*(tilemap++))); tilemap += *(wrap++); } } if (WX >= 160) return; base = ((R_LCDC&0x40)?0x1C00:0x1800) + (WT<<5); tilemap = lcd.vbank[0] + base; attrmap = lcd.vbank[1] + base; tilebuf = WND; cnt = ((160 - WX) >> 3) + 1; if (hw.cgb) { if (R_LCDC & 0x10) for (i = cnt; i > 0; i--) { *(tilebuf++) = *(tilemap++) | (((int)*attrmap & 0x08) << 6) | (((int)*attrmap & 0x60) << 5); *(tilebuf++) = (((int)*(attrmap++)&7) << 2); } else for (i = cnt; i > 0; i--) { *(tilebuf++) = (256 + ((n8)*(tilemap++))) | (((int)*attrmap & 0x08) << 6) | (((int)*attrmap & 0x60) << 5); *(tilebuf++) = (((int)*(attrmap++)&7) << 2); } } else { if (R_LCDC & 0x10) for (i = cnt; i > 0; i--) *(tilebuf++) = *(tilemap++); else for (i = cnt; i > 0; i--) *(tilebuf++) = (256 + ((n8)*(tilemap++))); } } /* V = vertical line * WX = WND start (if 0, no need to do anything) -> WY * U = start...something...thingy... 7 at most */ static void bg_scan(void) ICODE_ATTR; static void bg_scan(void) { int cnt; byte *src, *dest; int *tile; if (WX <= 0) return; cnt = WX; tile = BG; dest = BUF; src = patpix[*(tile++)][V] + U; memcpy(dest, src, 8-U); dest += 8-U; cnt -= 8-U; if (cnt <= 0) return; while (cnt >= 8) { #if defined(CPU_COLDFIRE) asm volatile ( "move.l (%1)+,(%0)+ \n" "move.l (%1)+,(%0)+ \n" : /*outputs*/ : /*inputs*/ /* %0 */ "a" (dest), /* %1 */ "a" (patpix[*(tile++)][V]) //: /* clobbers */ ); #else src = patpix[*(tile++)][V]; memcpy(dest,src,8); dest += 8; #endif cnt -= 8; } src = patpix[*tile][V]; while (cnt--) *(dest++) = *(src++); } static void wnd_scan(void) ICODE_ATTR; static void wnd_scan(void) { int cnt; byte *src, *dest; int *tile; if (WX >= 160) return; cnt = 160 - WX; tile = WND; dest = BUF + WX; while (cnt >= 8) { #if defined(CPU_COLDFIRE) asm volatile ( "move.l (%1)+,(%0)+ \n" "move.l (%1)+,(%0)+ \n" : /*outputs*/ : /*inputs*/ /* %0 */ "a" (dest), /* %1 */ "a" (patpix[*(tile++)][WV]) //: /* clobbers */ ); #else src = patpix[*(tile++)][WV]; memcpy(dest,src,8); dest += 8; #endif cnt -= 8; } src = patpix[*tile][WV]; while (cnt--) *(dest++) = *(src++); } static void blendcpy(byte *dest, byte *src, byte b, int cnt) { while (cnt--) *(dest++) = *(src++) | b; } static int priused(void *attr) { un32 *a = attr; return (int)((a[0]|a[1]|a[2]|a[3]|a[4]|a[5]|a[6]|a[7])&0x80808080); } static void bg_scan_pri(void) ICODE_ATTR; static void bg_scan_pri(void) { int cnt, i; byte *src, *dest; if (WX <= 0) return; i = S; cnt = WX; dest = PRI; src = lcd.vbank[1] + ((R_LCDC&0x08)?0x1C00:0x1800) + (T<<5); if (!priused(src)) { memset(dest, 0, cnt); return; } memset(dest, src[i++&31]&128, 8-U); dest += 8-U; cnt -= 8-U; if (cnt <= 0) return; while (cnt >= 8) { memset(dest, src[i++&31]&128, 8); dest += 8; cnt -= 8; } memset(dest, src[i&31]&128, cnt); } static void wnd_scan_pri(void) ICODE_ATTR; static void wnd_scan_pri(void) { int cnt, i; byte *src, *dest; if (WX >= 160) return; i = 0; cnt = 160 - WX; dest = PRI + WX; src = lcd.vbank[1] + ((R_LCDC&0x40)?0x1C00:0x1800) + (WT<<5); if (!priused(src)) { memset(dest, 0, cnt); return; } while (cnt >= 8) { memset(dest, src[i++]&128, 8); dest += 8; cnt -= 8; } memset(dest, src[i]&128, cnt); } static void bg_scan_color(void) { int cnt; byte *src, *dest; int *tile; if (WX <= 0) return; cnt = WX; tile = BG; dest = BUF; src = patpix[*(tile++)][V] + U; blendcpy(dest, src, *(tile++), 8-U); dest += 8-U; cnt -= 8-U; if (cnt <= 0) return; while (cnt >= 8) { src = patpix[*(tile++)][V]; #if defined(CPU_COLDFIRE) asm volatile ( "move.l (%2)+,%%d1 \n" "move.b %%d1,%%d2 \n" "move.b (%1)+,%%d0 \n" "or.l %%d2,%%d0 \n" "move.b %%d0,(%0)+ \n" "move.b (%1)+,%%d0 \n" "or.l %%d1,%%d0 \n" "move.b %%d0,(%0)+ \n" "move.b (%1)+,%%d0 \n" "or.l %%d2,%%d0 \n" "move.b %%d0,(%0)+ \n" "move.b (%1)+,%%d0 \n" "or.l %%d2,%%d0 \n" "move.b %%d0,(%0)+ \n" "move.b (%1)+,%%d0 \n" "or.l %%d2,%%d0 \n" "move.b %%d0,(%0)+ \n" "move.b (%1)+,%%d0 \n" "or.l %%d2,%%d0 \n" "move.b %%d0,(%0)+ \n" "move.b (%1)+,%%d0 \n" "or.l %%d2,%%d0 \n" "move.b %%d0,(%0)+ \n" "move.b (%1)+,%%d0 \n" "or.l %%d2,%%d0 \n" "move.b %%d0,(%0)+ \n" : /*outputs*/ : /*inputs*/ /* %0 */ "a" (dest), /* %1 */ "a" (src), /* %2 */ "a" (tile) : /* clobbers */ "d0", "d1", "d2" ); #else blendcpy(dest, src, *(tile++), 8); dest += 8; #endif cnt -= 8; } src = patpix[*(tile++)][V]; blendcpy(dest, src, *(tile++), cnt); } static void wnd_scan_color(void) { int cnt; byte *src, *dest; int *tile; if (WX >= 160) return; cnt = 160 - WX; tile = WND; dest = BUF + WX; while (cnt >= 8) { src = patpix[*(tile++)][WV]; blendcpy(dest, src, *(tile++), 8); dest += 8; cnt -= 8; } src = patpix[*(tile++)][WV]; blendcpy(dest, src, *(tile++), cnt); } static void spr_enum(void) ICODE_ATTR; static void spr_enum(void) { int i, j; struct obj *o; struct vissprite ts; int v, pat; NS = 0; if (!(R_LCDC & 0x02)) return; o = lcd.oam.obj; for (i = 40; i; i--, o++) { if (L >= o->y || L + 16 < o->y) continue; if (L + 8 >= o->y && !(R_LCDC & 0x04)) continue; VS[NS].x = (int)o->x - 8; v = L - (int)o->y + 16; if (hw.cgb) { pat = o->pat | (((int)o->flags & 0x60) << 5) | (((int)o->flags & 0x08) << 6); VS[NS].pal = 32 + ((o->flags & 0x07) << 2); } else { pat = o->pat | (((int)o->flags & 0x60) << 5); VS[NS].pal = 32 + ((o->flags & 0x10) >> 2); } VS[NS].pri = (o->flags & 0x80) >> 7; if ((R_LCDC & 0x04)) { pat &= ~1; if (v >= 8) { v -= 8; pat++; } if (o->flags & 0x40) pat ^= 1; } VS[NS].buf = patpix[pat][v]; if (++NS == 10) break; } if (hw.cgb) return; for (i = 0; i < NS; i++) { for (j = i + 1; j < NS; j++) { if (VS[i].x > VS[j].x) { ts = VS[i]; VS[i] = VS[j]; VS[j] = ts; } } } } static void spr_scan(void) ICODE_ATTR; static void spr_scan(void) { int i, x; byte pal, b, ns = NS; byte *src, *dest, *bg, *pri; struct vissprite *vs; static byte bgdup[256]; if (!ns) return; memcpy(bgdup, BUF, 256); vs = &VS[ns-1]; for (; ns; ns--, vs--) { x = vs->x; if (x > 159) continue; if (x < -7) continue; if (x < 0) { src = vs->buf - x; dest = BUF; i = 8 + x; } else { src = vs->buf; dest = BUF + x; if (x > 152) i = 160 - x; else i = 8; } pal = vs->pal; if (vs->pri) { bg = bgdup + (dest - BUF); while (i--) { b = src[i]; if (b && !(bg[i]&3)) dest[i] = pal|b; } } else if (hw.cgb) { bg = bgdup + (dest - BUF); pri = PRI + (dest - BUF); while (i--) { b = src[i]; if (b && (!pri[i] || !(bg[i]&3))) dest[i] = pal|b; } } else while (i--) if (src[i]) dest[i] = pal|src[i]; } } /* Scaling defines */ #define DX ((LCD_WIDTH<<16) / 160) #define DXI ((160<<16) / LCD_WIDTH) #define DY ((LCD_HEIGHT<<16) / 144) #define DYI ((144<<16) / LCD_HEIGHT) #define DXR ((LCD_WIDTH<<16) / 144) #define DXIR ((144<<16) / LCD_WIDTH) #define DYR ((LCD_HEIGHT<<16) / 160) #define DYIR ((160<<16) / LCD_HEIGHT) /* Defines for scale offsets: * S2 is for scaled * S3 if scaled and maintain ratio * S1 is unscaled * R's are the rotated defines */ #if (LCD_WIDTH>=160) && (LCD_HEIGHT>=144) #define S1 ((LCD_HEIGHT-144)/2)*LCD_WIDTH + ((LCD_WIDTH-160)/2) #define S2 0 #define S1R ((LCD_HEIGHT-160)/2)*LCD_WIDTH + ((LCD_WIDTH-144)/2)+144 #define S2R (LCD_WIDTH-1) #elif (LCD_WIDTH>=160) && (LCD_HEIGHT<=144) #define S1 0 #define S2 0 #define S1R LCD_WIDTH-1 #define S2R LCD_WIDTH-1 #elif (LCD_WIDTH<=160) && (LCD_HEIGHT>=144) #define S1 ((LCD_HEIGHT-144)/2)*LCD_WIDTH #define S2 ((LCD_HEIGHT-144)/2)*LCD_WIDTH #define S1R LCD_WIDTH-1 #define S2R LCD_WIDTH-1 #else #define S1 0 #define S2 0 #define S1R LCD_WIDTH-1 #define S2R LCD_WIDTH-1 #endif #if (LCD_WIDTH>LCD_HEIGHT) #define S3 ((LCD_WIDTH-((160*DY)>>16))/2) #define S3R LCD_WIDTH-1 #else #define S3 ((LCD_HEIGHT-((144*DX)>>16))/2)*LCD_WIDTH #define S3R ((LCD_HEIGHT-((160*DXR)>>16))/2)*LCD_WIDTH+LCD_WIDTH-1 #endif void lcd_begin(void) { #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256) vdest=(unsigned char*)rb->lcd_framebuffer; #else vdest=rb->lcd_framebuffer; #endif #ifdef HAVE_LCD_COLOR if(options.rotate==1) { if(options.scaling == 0) vdest+=S2R; else if (options.scaling == 1) vdest+=S3R; else vdest+=S1R; } else if(options.rotate==2) { if(options.scaling == 0) vdest+=(LCD_WIDTH*LCD_HEIGHT)-S2R; else if (options.scaling == 1) vdest+=(LCD_WIDTH*LCD_HEIGHT)-S3R; else vdest+=(LCD_WIDTH*LCD_HEIGHT)-S1R-144; } else { if(options.scaling == 0) vdest+=S2; else if (options.scaling == 1) vdest+=S3; else vdest+=S1; } #endif WY = R_WY; } #ifdef HAVE_LCD_COLOR int SCALEWL IDATA_ATTR=1<<16; int SCALEWS IDATA_ATTR=1<<16; int SCALEHL IDATA_ATTR=1<<16; int SCALEHS IDATA_ATTR=1<<16; int swidth IDATA_ATTR=160; int sremain IDATA_ATTR=LCD_WIDTH-160; #endif void setvidmode(void) { #ifdef HAVE_LCD_COLOR switch(options.scaling) { case 0: if(options.rotate) { SCALEWL=DYR; SCALEWS=DYIR; SCALEHL=DXR; SCALEHS=DXIR; } else { SCALEWL=DX; SCALEWS=DXI; SCALEHL=DY; SCALEHS=DYI; } break; case 1: /* Maintain Ratio */ if(options.rotate) { if (DYR>16); if(options.rotate==1) { sremain=-(((160*SCALEWL)>>16)*LCD_WIDTH+1); } else if(options.rotate==2) { sremain=(((160*SCALEWL)>>16)*LCD_WIDTH+1); } else { sremain=LCD_WIDTH-swidth; } #endif } void lcd_refreshline(void) { if (!(R_LCDC & 0x80)) return; /* should not happen... */ #if (LCD_HEIGHT <= 128) && !defined(HAVE_LCD_COLOR) if ( (fb.mode==0&&(R_LY >= 128)) || (fb.mode==1&&(R_LY < 16)) || (fb.mode==2&&(R_LY<8||R_LY>=136)) || (fb.mode==3&&((R_LY%9)==8)) #if LCD_HEIGHT == 64 || (R_LY & 1) /* calculate only even lines */ #endif ) return; #endif updatepatpix(); L = R_LY; X = R_SCX; Y = (R_SCY + L) & 0xff; S = X >> 3; T = Y >> 3; U = X & 7; V = Y & 7; WX = R_WX - 7; if (WY>L || WY<0 || WY>143 || WX<-7 || WX>159 || !(R_LCDC&0x20)) WX = 160; WT = (L - WY) >> 3; WV = (L - WY) & 7; spr_enum(); tilebuf(); if (hw.cgb) { bg_scan_color(); wnd_scan_color(); if (NS) { bg_scan_pri(); wnd_scan_pri(); } } else { bg_scan(); wnd_scan(); } spr_scan(); #if !defined(HAVE_LCD_COLOR) #if LCD_DEPTH == 1 if (scanline_ind == 7) #elif LCD_DEPTH == 2 if (scanline_ind == 3) #endif { if(fb.mode!=3) vid_update(L); else vid_update(L-((int)(L/9))); #else { /* Universal Scaling pulled from PrBoom and modified for rockboy */ static int hpt IDATA_ATTR=0x8000; while((hpt>>16)>16]; #else *vdest = PAL[BUF[srcpt>>16]]; #endif if (options.rotate == 1) { vdest+=LCD_WIDTH; } else if (options.rotate == 2) { vdest-=LCD_WIDTH; } else { vdest++; } srcpt+=SCALEWS; } vdest+=sremain; } if(L==143) { if(options.showstats) { rb->lcd_putsxyf(0,LCD_HEIGHT-10,"FPS: %d Frameskip: %d ", options.fps, options.frameskip); rb->lcd_update_rect(0,LCD_HEIGHT-10, LCD_WIDTH, 10); } hpt=0x8000; #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256) if(options.scaling==3) { rb->lcd_blit_pal256((unsigned char*)rb->lcd_framebuffer,(LCD_WIDTH-160)/2, (LCD_HEIGHT-144)/2, (LCD_WIDTH-160)/2, (LCD_HEIGHT-144)/2, 160, 144); } else { rb->lcd_blit_pal256((unsigned char*)rb->lcd_framebuffer,0,0,0,0,LCD_WIDTH,LCD_HEIGHT); } #else if(options.scaling==3) { rb->lcd_update_rect( (LCD_WIDTH-160)/2, (LCD_HEIGHT-144)/2, 160, 144); } else { rb->lcd_update(); } #endif } #endif } #if LCD_DEPTH == 1 scanline_ind = (scanline_ind+1) % 8; #elif LCD_DEPTH == 2 scanline_ind = (scanline_ind+1) % 4; #endif } #ifdef HAVE_LCD_COLOR void set_pal(void) { memcpy(dmg_pal,palettes[options.pal], sizeof(dmg_pal)); pal_dirty(); } static void updatepalette(int i) { int c, r, g, b; c = (lcd.pal[i<<1] | ((int)lcd.pal[(i<<1)|1] << 8)) & 0x7FFF; r = (c & 0x001F) << 3; g = (c & 0x03E0) >> 2; b = (c & 0x7C00) >> 7; r |= (r >> 5); g |= (g >> 5); b |= (b >> 5); r = (r >> fb.cc[0].r) << fb.cc[0].l; g = (g >> fb.cc[1].r) << fb.cc[1].l; b = (b >> fb.cc[2].r) << fb.cc[2].l; #if LCD_PIXELFORMAT == RGB565 c = r|g|b; #elif LCD_PIXELFORMAT == RGB565SWAPPED c = swap16(r|g|b); #endif /* updatepalette might get called, but the pallete does not necessarily * need to be updated. */ if(PAL[i]!=c) { PAL[i] = c; #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256) rb->lcd_pal256_update_pal(PAL); #endif } } #endif /* HAVE_LCD_COLOR */ void pal_write(int i, byte b) { if (lcd.pal[i] == b) return; lcd.pal[i] = b; #ifdef HAVE_LCD_COLOR updatepalette(i>>1); #endif } void pal_write_dmg(int i, int mapnum, byte d) { int j; int *cmap = dmg_pal[mapnum]; int c, r, g, b; if (hw.cgb) return; for (j = 0; j < 8; j += 2) { c = cmap[(d >> j) & 3]; r = (c & 0xf8) >> 3; g = (c & 0xf800) >> 6; b = (c & 0xf80000) >> 9; c = r|g|b; /* FIXME - handle directly without faking cgb */ pal_write(i+j, c & 0xff); pal_write(i+j+1, c >> 8); } } void vram_write(addr a, byte b) { lcd.vbank[R_VBK&1][a] = b; if (a >= 0x1800) return; patdirty[((R_VBK&1)<<9)+(a>>4)] = 1; anydirty = 1; pal_dirty(); } void vram_dirty(void) { anydirty = 1; memset(patdirty, 1, sizeof patdirty); } void pal_dirty(void) { #ifdef HAVE_LCD_COLOR int i; #endif if (!hw.cgb) { pal_write_dmg(0, 0, R_BGP); pal_write_dmg(8, 1, R_BGP); pal_write_dmg(64, 2, R_OBP0); pal_write_dmg(72, 3, R_OBP1); } #ifdef HAVE_LCD_COLOR for (i = 0; i < 64; i++) updatepalette(i); #endif } void lcd_reset(void) { memset(&lcd, 0, sizeof lcd); lcd_begin(); vram_dirty(); } ='#n872'>872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889
/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2007 by Jonathan Gordon
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/

/* This file contains the code to draw the list widget on BITMAP LCDs. */

#include "config.h"
#include "system.h"
#include "lcd.h"
#include "font.h"
#include "button.h"
#include "string.h"
#include "settings.h"
#include "kernel.h"
#include "file.h"

#include "action.h"
#include "screen_access.h"
#include "list.h"
#include "scrollbar.h"
#include "lang.h"
#include "sound.h"
#include "misc.h"
#include "viewport.h"
#include "statusbar-skinned.h"
#include "debug.h"

#define ICON_PADDING 1

/* these are static to make scrolling work */
static struct viewport list_text[NB_SCREENS], title_text[NB_SCREENS];

#ifdef HAVE_TOUCHSCREEN
static int y_offset;
static bool hide_selection;
#endif

int gui_list_get_item_offset(struct gui_synclist * gui_list, int item_width,
                             int text_pos, struct screen * display,
                             struct viewport *vp);
bool list_display_title(struct gui_synclist *list, enum screen_type screen);

void gui_synclist_scroll_stop(struct gui_synclist *lists)
{
    FOR_NB_SCREENS(i)
    {
        screens[i].scroll_stop(&list_text[i]);
        screens[i].scroll_stop(&title_text[i]);
        screens[i].scroll_stop(lists->parent[i]);
    }
}

/* Draw the list...
    internal screen layout:
        -----------------
        |TI|  title     |   TI is title icon
        -----------------
        | | |            |
        |S|I|            |   S - scrollbar
        | | | items      |   I - icons
        | | |            |
        ------------------

        Note: This image is flipped horizontally when the language is a
        right-to-left one (Hebrew, Arabic)
*/

static int list_icon_width(enum screen_type screen)
{
    return get_icon_width(screen) + ICON_PADDING * 2;
}

static int list_icon_height(enum screen_type screen)
{
    return get_icon_height(screen);
}

static bool draw_title(struct screen *display, struct gui_synclist *list)
{
    const int screen = display->screen_type;
    int style = STYLE_DEFAULT;
    struct viewport *title_text_vp = &title_text[screen];

    if (sb_set_title_text(list->title, list->title_icon, screen))
        return false; /* the sbs is handling the title */
    display->scroll_stop(title_text_vp);
    if (!list_display_title(list, screen))
        return false;
    *title_text_vp = *(list->parent[screen]);
    title_text_vp->height = title_text_vp->line_height;

    if (list->title_icon != Icon_NOICON && global_settings.show_icons)
    {
        struct viewport title_icon = *title_text_vp;

        title_icon.width = list_icon_width(screen);
        title_icon.y += (title_icon.height - list_icon_height(screen)) / 2;
        title_icon.height = list_icon_height(screen);
        if (VP_IS_RTL(&title_icon))
        {
            title_icon.x += title_text_vp->width - title_icon.width;
        }
        else
        {
            title_text_vp->x += title_icon.width;
        }
        title_text_vp->width -= title_icon.width;

        display->set_viewport(&title_icon);
        screen_put_iconxy(display, 0, 0, list->title_icon);
    }
#ifdef HAVE_LCD_COLOR
    if (list->title_color >= 0)
    {
        style |= (STYLE_COLORED|list->title_color);
    }
#endif
    display->set_viewport(title_text_vp);
    display->puts_scroll_style(0, 0, list->title, style);
    return true;
}

void list_draw(struct screen *display, struct gui_synclist *list)
{
    struct viewport list_icons;
    int start, end, line_height, style, item_offset, i;
    const int screen = display->screen_type;
    const int list_start_item = list->start_item[screen];
    const int icon_width = list_icon_width(screen);
    const bool scrollbar_in_left = (global_settings.scrollbar == SCROLLBAR_LEFT);
    const bool show_cursor = !global_settings.cursor_style &&
                        list->show_selection_marker;
    struct viewport *parent = (list->parent[screen]);
#ifdef HAVE_LCD_COLOR
    unsigned char cur_line = 0;
#endif
    int icon_yoffset = 0; /* to center the icon */
    bool show_title;
    struct viewport *list_text_vp = &list_text[screen];
    int indent = 0;

    line_height = parent->line_height;
    display->set_viewport(parent);
    display->clear_viewport();
    display->scroll_stop(list_text_vp);
    *list_text_vp = *parent;
    list_text_vp->line_height = line_height;
    if ((show_title = draw_title(display, list)))
    {
        int title_height = title_text[screen].height;
        list_text_vp->y += title_height;
        list_text_vp->height -= title_height;
    }

    const int nb_lines = viewport_get_nb_lines(list_text_vp);

    start = list_start_item;
    end = start + nb_lines;

#ifdef HAVE_TOUCHSCREEN
    if (list->selected_item == 0 || (list->nb_items < nb_lines))
        y_offset = 0; /* reset in case it's a new list */

    int draw_offset = y_offset;
    /* draw some extra items to not have empty lines at the top and bottom */
    if (y_offset > 0)
    {
        /* make it negative for more consistent apparence when switching
         * directions */
        draw_offset -= line_height;
        if (start > 0)
            start--;
    }
    else if (y_offset < 0)
        end++;
#else
    #define draw_offset 0
#endif

    /* draw the scrollbar if its needed */
    if (global_settings.scrollbar && nb_lines < list->nb_items)
    {
        struct viewport vp = *list_text_vp;
        vp.width = SCROLLBAR_WIDTH;
        vp.height = line_height * nb_lines;
        vp.x = parent->x;
        list_text_vp->width -= SCROLLBAR_WIDTH;
        if (scrollbar_in_left)
            list_text_vp->x += SCROLLBAR_WIDTH;
        else
            vp.x += list_text_vp->width;
        display->set_viewport(&vp);
        gui_scrollbar_draw(display,
                (scrollbar_in_left? 0: 1), 0, SCROLLBAR_WIDTH-1, vp.height,
                list->nb_items, list_start_item, list_start_item + nb_lines,
                VERTICAL);
    }
    else if (show_title)
    {
        /* shift everything a bit in relation to the title... */
        if (!VP_IS_RTL(list_text_vp) && scrollbar_in_left)
        {
            list_text_vp->width -= SCROLLBAR_WIDTH;
            list_text_vp->x += SCROLLBAR_WIDTH;
        }
        else if (VP_IS_RTL(list_text_vp) && !scrollbar_in_left)
        {
            list_text_vp->width -= SCROLLBAR_WIDTH;
        }
    }

    /* setup icon placement */
    list_icons = *list_text_vp;
    int icon_count = (list->callback_get_item_icon != NULL) ? 1 : 0;
    if (show_cursor)
        icon_count++;
    if (icon_count)
    {
        list_icons.width = icon_width * icon_count;
        list_text_vp->width -= list_icons.width + ICON_PADDING;
        if (VP_IS_RTL(&list_icons))
            list_icons.x += list_text_vp->width + ICON_PADDING;
        else
            list_text_vp->x += list_icons.width + ICON_PADDING;
        icon_yoffset = (line_height - list_icon_height(screen)) / 2;
    }

    for (i=start; i<end && i<list->nb_items; i++)
    {
        /* do the text */
        unsigned const char *s;
        char entry_buffer[MAX_PATH];
        unsigned char *entry_name;
        int text_pos = 0;
        int line = i - start;
        indent = 0;
        s = list->callback_get_item_name(i, list->data, entry_buffer,
                                         sizeof(entry_buffer));
        entry_name = P2STR(s);

        while (*entry_name == '\t')
        {
            indent++;
            entry_name++;
        }
        if (indent)
        {
            if (icon_width)
                indent *= icon_width;
            else
                indent *= display->getcharwidth();

            list_icons.x += indent;
            list_text_vp->x += indent;
            list_text_vp->width -= indent;
        }

        display->set_viewport(list_text_vp);
        style = STYLE_DEFAULT;
        /* position the string at the correct offset place */
        int item_width,h;
        display->getstringsize(entry_name, &item_width, &h);
        item_offset = gui_list_get_item_offset(list, item_width, text_pos,
                display, list_text_vp);

#ifdef HAVE_LCD_COLOR
        /* if the list has a color callback */
        if (list->callback_get_item_color)
        {
            int color = list->callback_get_item_color(i, list->data);
            /* if color selected */
            if (color >= 0)
            {
                style |= STYLE_COLORED|color;
            }
        }
#endif
        /* draw the selected line */
        if(
#ifdef HAVE_TOUCHSCREEN
            /* don't draw it during scrolling */
            !hide_selection &&
#endif
                i >= list->selected_item
                && i <  list->selected_item + list->selected_size
                && list->show_selection_marker)
        {/* The selected item must be displayed scrolling */
            if (global_settings.cursor_style == 1
#ifdef HAVE_REMOTE_LCD
                    /* the global_settings.cursor_style check is here to make
                    * sure if they want the cursor instead of bar it will work
                    */
                    || (display->depth < 16 && global_settings.cursor_style)
#endif
            )
            {
                /* Display inverted-line-style */
                style = STYLE_INVERT;
            }
#ifdef HAVE_LCD_COLOR
            else if (global_settings.cursor_style == 2)
            {
                /* Display colour line selector */
                style = STYLE_COLORBAR;
            }
            else if (global_settings.cursor_style == 3)
            {
                /* Display gradient line selector */
                style = STYLE_GRADIENT;

                /* Make the lcd driver know how many lines the gradient should
                   cover and current line number */
                /* number of selected lines */
                style |= NUMLN_PACK(list->selected_size);
                /* current line number, zero based */
                style |= CURLN_PACK(cur_line);
                cur_line++;
            }
#endif
            /* if the text is smaller than the viewport size */
            if (item_offset> item_width - (list_text_vp->width - text_pos))
            {
                /* don't scroll */
                display->puts_style_xyoffset(0, line, entry_name,
                        style, item_offset, draw_offset);
            }
            else
            {
                display->puts_scroll_style_xyoffset(0, line, entry_name,
                        style, item_offset, draw_offset);
            }
        }
        else
        {
            if (list->scroll_all)
                display->puts_scroll_style_xyoffset(0, line, entry_name,
                        style, item_offset, draw_offset);
            else
                display->puts_style_xyoffset(0, line, entry_name,
                        style, item_offset, draw_offset);
        }
        /* do the icon */
        display->set_viewport(&list_icons);
        if (list->callback_get_item_icon != NULL)
        {
            int xoff = show_cursor ? list_icon_width(screen) : 0;
            screen_put_iconxy(display, xoff,
                            line*line_height + draw_offset + icon_yoffset,
                            list->callback_get_item_icon(i, list->data));
        }
        /* do the cursor */
        if (show_cursor && i >= list->selected_item &&
                i <  list->selected_item + list->selected_size)
        {
            screen_put_iconxy(display, 0,
                            line*line_height + draw_offset + icon_yoffset,
                            Icon_Cursor);
        }
        if (indent)
        {
            list_icons.x -= indent;
            list_text_vp->x -= indent;
            list_text_vp->width += indent;
        }
    }
    display->set_viewport(parent);
    display->update_viewport();
    display->set_viewport(NULL);
}

#if defined(HAVE_TOUCHSCREEN)
/* This needs to be fixed if we ever get more than 1 touchscreen on a target. */

/* difference in pixels between draws, above it means enough to start scrolling */