/* * SMS renderer * (C) notaz, 2009-2010 * (C) kub, 2021 * * currently supports VDP mode 4 (SMS and GG) and mode 2+0 (TMS) * * This work is licensed under the terms of MAME license. * See COPYING file in the top-level directory. */ /* * TODO: * - other TMS9918 modes? */ #include "pico_int.h" #include static void (*FinalizeLineSMS)(int line); static int skip_next_line; static int screen_offset, line_offset; static u8 mode; static unsigned int sprites_addr[32]; // bitmap address static unsigned char sprites_c[32]; // TMS sprites color static int sprites_x[32]; // x position static int sprites; // count static unsigned char sprites_map[2+256/8+2]; // collision detection map unsigned int sprites_status; /* sprite collision detection */ static int CollisionDetect(u8 *mb, u16 sx, unsigned int pack, int zoomed) { static u8 morton[16] = { 0x00,0x03,0x0c,0x0f,0x30,0x33,0x3c,0x3f, 0xc0,0xc3,0xcc,0xcf,0xf0,0xf3,0xfc,0xff }; u8 *mp = mb + (sx>>3); unsigned col, m; // check sprite map for collision and update map with current sprite if (!zoomed) { // 8 sprite pixels m = mp[0] | (mp[1]<<8); col = m & (pack<<(sx&7)); // collision if current sprite overlaps sprite map m |= pack<<(sx&7); mp[0] = m, mp[1] = m>>8; } else { // 16 sprite pixels in zoom mode pack = morton[pack&0x0f] | (morton[(pack>>4)&0x0f] << 8); m = mp[0] | (mp[1]<<8) | (mp[2]<<16); col = m & (pack<<(sx&7)); m |= pack<<(sx&7); mp[0] = m, mp[1] = m>>8, mp[2] = m>>16; } // invisible overscan area, not tested for collision mb[0] = mb[33] = mb[34] = 0; return col; } /* Mode 4 */ /*========*/ static void TileBGM4(u16 sx, int pal) { if (sx & 3) { u8 *pd = (u8 *)(Pico.est.HighCol + sx); pd[0] = pd[1] = pd[2] = pd[3] = pal; pd[4] = pd[5] = pd[6] = pd[7] = pal; } else { u32 *pd = (u32 *)(Pico.est.HighCol + sx); pd[0] = pd[1] = pal * 0x01010101; } } // 8 pixels are arranged in 4 bitplane bytes in a 32 bit word. To pull the // 4 bitplanes together multiply with each bit distance (multiples of 1<<7) #define PLANAR_PIXELBG(x,p) \ t = (pack>>(7-p)) & 0x01010101; \ t = (t*0x10204080) >> 28; \ pd[x] = pal|t; static void TileNormBGM4(u16 sx, unsigned int pack, int pal) { u8 *pd = Pico.est.HighCol + sx; u32 t; PLANAR_PIXELBG(0, 0) PLANAR_PIXELBG(1, 1) PLANAR_PIXELBG(2, 2) PLANAR_PIXELBG(3, 3) PLANAR_PIXELBG(4, 4) PLANAR_PIXELBG(5, 5) PLANAR_PIXELBG(6, 6) PLANAR_PIXELBG(7, 7) } static void TileFlipBGM4(u16 sx, unsigned int pack, int pal) { u8 *pd = Pico.est.HighCol + sx; u32 t; PLANAR_PIXELBG(0, 7) PLANAR_PIXELBG(1, 6) PLANAR_PIXELBG(2, 5) PLANAR_PIXELBG(3, 4) PLANAR_PIXELBG(4, 3) PLANAR_PIXELBG(5, 2) PLANAR_PIXELBG(6, 1) PLANAR_PIXELBG(7, 0) } // non-transparent sprite pixels apply if no higher prio pixel is already there #define PLANAR_PIXELSP(x,p) \ t = (pack>>(7-p)) & 0x01010101; \ if (t && (pd[x] & 0x2f) <= 0x20) { \ t = (t*0x10204080) >> 28; \ pd[x] = pal|t; \ } static void TileNormSprM4(u16 sx, unsigned int pack, int pal) { u8 *pd = Pico.est.HighCol + sx; u32 t; PLANAR_PIXELSP(0, 0) PLANAR_PIXELSP(1, 1) PLANAR_PIXELSP(2, 2) PLANAR_PIXELSP(3, 3) PLANAR_PIXELSP(4, 4) PLANAR_PIXELSP(5, 5) PLANAR_PIXELSP(6, 6) PLANAR_PIXELSP(7, 7) } static void TileDoubleSprM4(int sx, unsigned int pack, int pal) { u8 *pd = Pico.est.HighCol + sx; u32 t; PLANAR_PIXELSP(0, 0) PLANAR_PIXELSP(1, 0) PLANAR_PIXELSP(2, 1) PLANAR_PIXELSP(3, 1) PLANAR_PIXELSP(4, 2) PLANAR_PIXELSP(5, 2) PLANAR_PIXELSP(6, 3) PLANAR_PIXELSP(7, 3) PLANAR_PIXELSP(8, 4) PLANAR_PIXELSP(9, 4) PLANAR_PIXELSP(10, 5) PLANAR_PIXELSP(11, 5) PLANAR_PIXELSP(12, 6) PLANAR_PIXELSP(13, 6) PLANAR_PIXELSP(14, 7) PLANAR_PIXELSP(15, 7) } static void ParseSpritesM4(int scanline) { struct PicoVideo *pv = &Pico.video; u8 *sat; int xoff = 8; // relative to HighCol, which is (screen - 8) int sprite_base, addr_mask; int zoomed = pv->reg[1] & 0x1; // zoomed sprites, e.g. Earthworm Jim unsigned int pack; int i, s, h, m; if (pv->reg[0] & 8) xoff = 0; xoff += line_offset; if ((Pico.m.hardware & 0x3) == 0x3) xoff -= 48; // GG LCD, adjust to center 160 px sat = (u8 *)PicoMem.vram + ((pv->reg[5] & 0x7e) << 7); if (pv->reg[1] & 2) { addr_mask = 0xfe; h = 16; } else { addr_mask = 0xff; h = 8; } if (zoomed) h *= 2; sprite_base = (pv->reg[6] & 4) << (13-2-1); m = 0; memset(sprites_map, 0, sizeof(sprites_map)); for (i = s = 0; i < 64; i++) { int y; y = sat[MEM_LE2(i)]; if (y == 0xd0 && !((pv->reg[0] & 6) == 6 && (pv->reg[1] & 0x18))) break; if (y >= 0xe0) y -= 256; y &= ~zoomed; // zoomed sprites apparently only on even lines, see GG Tarzan if (y + h <= scanline || scanline < y) continue; // not on this line if (s >= 8) { if (scanline >= 0) sprites_status |= SR_SOVR; if (!(PicoIn.opt & POPT_DIS_SPRITE_LIM) || s >= 32) break; } if (xoff + sat[MEM_LE2(0x80 + i*2)] >= 0) { sprites_x[s] = xoff + sat[MEM_LE2(0x80 + i*2)]; sprites_addr[s] = sprite_base + ((sat[MEM_LE2(0x80 + i*2 + 1)] & addr_mask) << (5-1)) + ((scanline - y) >> zoomed << (2-1)); if (Pico.video.reg[1] & 0x40) { // collision detection. Do it here since off-screen lines aren't drawn pack = CPU_LE2(*(u32 *)(PicoMem.vram + sprites_addr[s])); // make sprite pixel map by merging the 4 bitplanes pack = ((pack | (pack>>16)) | ((pack | (pack>>16))>>8)) & 0xff; if (!m) m = CollisionDetect(sprites_map, sprites_x[s], pack, zoomed); } s++; } } if (m) sprites_status |= SR_C; sprites = s; } static void DrawSpritesM4(void) { struct PicoVideo *pv = &Pico.video; unsigned int pack; int zoomed = pv->reg[1] & 0x1; // zoomed sprites, e.g. Earthworm Jim int s = sprites; // now draw all sprites backwards for (--s; s >= 0; s--) { pack = CPU_LE2(*(u32 *)(PicoMem.vram + sprites_addr[s])); if (zoomed) TileDoubleSprM4(sprites_x[s], pack, 0x10); else TileNormSprM4(sprites_x[s], pack, 0x10); } } // cells_dx, tilex_ty merged to reduce register pressure static void DrawStripM4(const u16 *nametab, int cells_dx, int tilex_ty) { int oldcode = -1; int addr = 0, pal = 0; // Draw tiles across screen: for (; cells_dx > 0; cells_dx += 8, tilex_ty++, cells_dx -= 0x10000) { unsigned int pack; unsigned code; code = nametab[tilex_ty & 0x1f]; if (code != oldcode) { oldcode = code; // Get tile address/2: addr = (code & 0x1ff) << 4; addr += tilex_ty >> 16; if (code & 0x0400) addr ^= 0xe; // Y-flip pal = (code>>7) & 0x30; // prio | palette select } pack = CPU_LE2(*(u32 *)(PicoMem.vram + addr)); // Get 4 bitplanes / 8 pixels if (pack == 0) TileBGM4(cells_dx, pal); else if (code & 0x0200) TileFlipBGM4(cells_dx, pack, pal); else TileNormBGM4(cells_dx, pack, pal); } } static void DrawDisplayM4(int scanline) { struct PicoVideo *pv = &Pico.video; u16 *nametab, *nametab2; int line, tilex, dx, ty, cells; int cellskip = 0; // XXX int maxcells = 32; // Find the line in the name table line = pv->reg[9] + scanline; // vscroll + scanline // Find name table: nametab = PicoMem.vram; if ((pv->reg[0] & 6) == 6 && (pv->reg[1] & 0x18)) { // 224/240 line mode line &= 0xff; nametab += ((pv->reg[2] & 0x0c) << (10-1)) + (0x700 >> 1); } else { while (line >= 224) line -= 224; nametab += (pv->reg[2] & 0x0e) << (10-1); // old SMS only, masks line:7 with reg[2]:0 for address calculation //if ((pv->reg[2] & 0x01) == 0) line &= 0x7f; } nametab2 = nametab + ((scanline>>3) << (6-1)); nametab = nametab + ((line>>3) << (6-1)); dx = pv->reg[8]; // hscroll if (scanline < 16 && (pv->reg[0] & 0x40)) dx = 0; // hscroll disabled for top 2 rows (e.g. Fantasy Zone II) tilex = ((-dx >> 3) + cellskip) & 0x1f; ty = (line & 7) << 1; // Y-Offset into tile cells = maxcells - cellskip; dx = ((dx - 1) & 7) + 1; if (dx != 8) cells++; // have hscroll, need to draw 1 cell more dx += cellskip << 3; dx += line_offset; // tiles if (!(pv->debug_p & PVD_KILL_B)) { if ((Pico.m.hardware & 0x3) == 0x3) { // on GG render only the center 160 px DrawStripM4(nametab , dx | ((cells-12)<< 16),(tilex+6) | (ty << 16)); } else if (pv->reg[0] & 0x80) { // vscroll disabled for rightmost 8 columns (e.g. Gauntlet) int dx2 = dx + (cells-8)*8, tilex2 = tilex + (cells-8), ty2 = scanline&7; DrawStripM4(nametab, dx | ((cells-8) << 16), tilex | (ty << 16)); DrawStripM4(nametab2, dx2 | (8 << 16), tilex2 | (ty2 << 17)); } else DrawStripM4(nametab , dx | ( cells << 16), tilex | (ty << 16)); } // sprites if (!(pv->debug_p & PVD_KILL_S_LO)) DrawSpritesM4(); if ((pv->reg[0] & 0x20) && (Pico.m.hardware & 0x3) != 0x3) { // first column masked with background, caculate offset to start of line dx = (dx&~0x1f) / 4; ty = ((pv->reg[7]&0x0f)|0x10) * 0x01010101; ((u32 *)Pico.est.HighCol)[dx+2] = ((u32 *)Pico.est.HighCol)[dx+3] = ty; } } /* TMS Modes */ /*===========*/ /* Background, Graphics modes */ #define TMS_PIXELBG(x,p) \ t = (pack>>(7-p)) & 0x01; \ t = (pal >> (t << 2)) & 0x0f; \ pd[x] = t; static void TileNormBgGr(u16 sx, unsigned int pack, int pal) { u8 *pd = Pico.est.HighCol + sx; unsigned int t; TMS_PIXELBG(0, 0) TMS_PIXELBG(1, 1) TMS_PIXELBG(2, 2) TMS_PIXELBG(3, 3) TMS_PIXELBG(4, 4) TMS_PIXELBG(5, 5) TMS_PIXELBG(6, 6) TMS_PIXELBG(7, 7) } /* Sprites */ #define TMS_PIXELSP(x,p) \ t = (pack>>(7-p)) & 0x01; \ if (t) \ pd[x] = pal; static void TileNormSprTMS(u16 sx, unsigned int pack, int pal) { u8 *pd = Pico.est.HighCol + sx; unsigned int t; TMS_PIXELSP(0, 0) TMS_PIXELSP(1, 1) TMS_PIXELSP(2, 2) TMS_PIXELSP(3, 3) TMS_PIXELSP(4, 4) TMS_PIXELSP(5, 5) TMS_PIXELSP(6, 6) TMS_PIXELSP(7, 7) } static void TileDoubleSprTMS(u16 sx, unsigned int pack, int pal) { u8 *pd = Pico.est.HighCol + sx; unsigned int t; TMS_PIXELSP(0, 0) TMS_PIXELSP(1, 0) TMS_PIXELSP(2, 1) TMS_PIXELSP(3, 1) TMS_PIXELSP(4, 2) TMS_PIXELSP(5, 2) TMS_PIXELSP(6, 3) TMS_PIXELSP(7, 3) TMS_PIXELSP(8, 4) TMS_PIXELSP(9, 4) TMS_PIXELSP(10, 5) TMS_PIXELSP(11, 5) TMS_PIXELSP(12, 6) TMS_PIXELSP(13, 6) TMS_PIXELSP(14, 7) TMS_PIXELSP(15, 7) } static void ParseSpritesTMS(int scanline) { struct PicoVideo *pv = &Pico.video; unsigned int pack; u8 *sat; int xoff = 8; // relative to HighCol, which is (screen - 8) int sprite_base, addr_mask; int zoomed = pv->reg[1] & 0x1; // zoomed sprites int i, s, h, m; xoff += line_offset; sat = (u8 *)PicoMem.vramb + ((pv->reg[5] & 0x7e) << 7); if (pv->reg[1] & 2) { addr_mask = 0xfc; h = 16; } else { addr_mask = 0xff; h = 8; } if (zoomed) h *= 2; sprite_base = (pv->reg[6] & 0x7) << 11; m = 0; memset(sprites_map, 0, sizeof(sprites_map)); /* find sprites on this scanline */ for (i = s = 0; i < 32; i++) { int x, y; y = sat[MEM_LE2(4*i)]; if (y == 0xd0) break; if (y >= 0xe0) y -= 256; y &= ~zoomed; if (y + h <= scanline || scanline < y) continue; // not on this line if (s >= 4) { if (scanline >= 0) sprites_status |= SR_SOVR | i; if (!(PicoIn.opt & POPT_DIS_SPRITE_LIM) || s >= 32) break; } x = sat[MEM_LE2(4*i+1)] + xoff; if (sat[MEM_LE2(4*i+3)] & 0x80) x -= 32; sprites_c[s] = sat[MEM_LE2(4*i+3)] & 0x0f; sprites_x[s] = x; sprites_addr[s] = sprite_base + ((sat[MEM_LE2(4*i + 2)] & addr_mask) << 3) + ((scanline - y) >> zoomed); if (Pico.video.reg[1] & 0x40) { // collision detection. Do it here since off-screen lines aren't drawn if (sprites_c[s] && x > 0) { pack = PicoMem.vramb[MEM_LE2(sprites_addr[s])]; if (!m) m = CollisionDetect(sprites_map, x, pack, zoomed); } x += (zoomed ? 16:8); if (sprites_c[s] && (pv->reg[1] & 0x2) && x > 0 && x < 8+256) { pack = PicoMem.vramb[MEM_LE2(sprites_addr[s]+0x10)]; if (!m) m = CollisionDetect(sprites_map, x, pack, zoomed); } } s++; } if (m) sprites_status |= SR_C; sprites = s; } /* Draw sprites into a scanline, max 4 */ static void DrawSpritesTMS(void) { struct PicoVideo *pv = &Pico.video; unsigned int pack; int zoomed = pv->reg[1] & 0x1; // zoomed sprites int s = sprites; // now draw all sprites backwards for (--s; s >= 0; s--) { int x, c, w = (zoomed ? 16: 8); x = sprites_x[s]; c = sprites_c[s]; // c may be 0 (transparent): sprite invisible if (c && x > 0) { pack = PicoMem.vramb[MEM_LE2(sprites_addr[s])]; if (zoomed) TileDoubleSprTMS(x, pack, c); else TileNormSprTMS(x, pack, c); } if (c && (pv->reg[1] & 0x2) && (x+=w) > 0 && x < 8+256) { pack = PicoMem.vramb[MEM_LE2(sprites_addr[s]+0x10)]; if (zoomed) TileDoubleSprTMS(x, pack, c); else TileNormSprTMS(x, pack, c); } } } /* Mode 2 */ /*========*/ /* Draw the background into a scanline; cells, dx, tilex, ty merged to reduce registers */ static void DrawStripM2(const u8 *nametab, const u8 *coltab, const u8 *pattab, int cells_dx, int tilex_ty) { // Draw tiles across screen: for (; cells_dx > 0; cells_dx += 8, tilex_ty++, cells_dx -= 0x10000) { unsigned int pack, pal; unsigned code; code = nametab[tilex_ty & 0x1f] << 3; pal = coltab[code]; pack = pattab[code]; TileNormBgGr(cells_dx, pack, pal); } } /* Draw a scanline */ static void DrawDisplayM2(int scanline) { struct PicoVideo *pv = &Pico.video; u8 *nametab, *coltab, *pattab; int tilex, dx, cells; int cellskip = 0; // XXX int maxcells = 32; // name, color, pattern table: nametab = PicoMem.vramb + ((pv->reg[2]<<10) & 0x3c00); coltab = PicoMem.vramb + ((pv->reg[3]<< 6) & 0x2000); pattab = PicoMem.vramb + ((pv->reg[4]<<11) & 0x2000); nametab += ((scanline>>3) << 5); coltab += ((scanline>>6) <<11) + (scanline & 0x7); pattab += ((scanline>>6) <<11) + (scanline & 0x7); tilex = cellskip & 0x1f; cells = maxcells - cellskip; dx = (cellskip << 3) + line_offset + 8; // tiles if (!(pv->debug_p & PVD_KILL_B)) DrawStripM2(nametab, coltab, pattab, dx | (cells << 16), tilex | (scanline << 16)); // sprites if (!(pv->debug_p & PVD_KILL_S_LO)) DrawSpritesTMS(); } /* Mode 0 */ /*========*/ /* Draw the background into a scanline; cells, dx, tilex, ty merged to reduce registers */ static void DrawStripM0(const u8 *nametab, const u8 *coltab, const u8 *pattab, int cells_dx, int tilex_ty) { // Draw tiles across screen: for (; cells_dx > 0; cells_dx += 8, tilex_ty++, cells_dx -= 0x10000) { unsigned int pack, pal; unsigned code; code = nametab[tilex_ty & 0x1f]; pal = coltab[code >> 3]; pack = pattab[code << 3]; TileNormBgGr(cells_dx, pack, pal); } } /* Draw a scanline */ static void DrawDisplayM0(int scanline) { struct PicoVideo *pv = &Pico.video; u8 *nametab, *coltab, *pattab; int tilex, dx, cells; int cellskip = 0; // XXX int maxcells = 32; // name, color, pattern table: nametab = PicoMem.vramb + ((pv->reg[2]<<10) & 0x3c00); coltab = PicoMem.vramb + ((pv->reg[3]<< 6) & 0x3fc0); pattab = PicoMem.vramb + ((pv->reg[4]<<11) & 0x3800); nametab += (scanline>>3) << 5; pattab += (scanline & 0x7); tilex = cellskip & 0x1f; cells = maxcells - cellskip; dx = (cellskip << 3) + line_offset + 8; // tiles if (!(pv->debug_p & PVD_KILL_B)) DrawStripM0(nametab, coltab, pattab, dx | (cells << 16), tilex | (scanline << 16)); // sprites if (!(pv->debug_p & PVD_KILL_S_LO)) DrawSpritesTMS(); } /* Common/global */ /*===============*/ static void FinalizeLineRGB555SMS(int line); static void FinalizeLine8bitSMS(int line); void PicoFrameStartSMS(void) { int lines = 192, columns = 256, loffs, coffs; skip_next_line = 0; loffs = screen_offset = 24; // 192 lines is really 224 with top/bottom bars Pico.est.rendstatus = PDRAW_32_COLS; // if mode changes make palette dirty since some modes switch to a fixed one if (mode != ((Pico.video.reg[0]&0x06) | (Pico.video.reg[1]&0x18))) { mode = (Pico.video.reg[0]&0x06) | (Pico.video.reg[1]&0x18); Pico.m.dirtyPal = 1; } // Copy LCD enable flag for easier handling Pico.m.hardware &= ~0x2; if (PicoIn.opt & POPT_EN_GG_LCD) Pico.m.hardware |= 0x2; if ((Pico.m.hardware & 0x3) == 0x3) { // GG LCD always has 160x144 regardless of settings screen_offset = 24; // nonetheless the vdp timing has 224 lines loffs = 48; lines = 144; columns = 160; } else switch (mode) { // SMS2 only 224/240 line modes, e.g. Micro Machines case 0x06|0x08: loffs = screen_offset = 0; lines = 240; break; case 0x06|0x10: loffs = screen_offset = 8; lines = 224; break; } if (PicoIn.opt & POPT_EN_SOFTSCALE) { coffs = 0; columns = 320; } else coffs = PicoIn.opt & POPT_DIS_32C_BORDER ? 0:(320-columns)/2; line_offset = (FinalizeLineSMS == NULL ? coffs : 0); if (FinalizeLineSMS == FinalizeLineRGB555SMS) line_offset = 0 /* done in FinalizeLine */; if (Pico.est.rendstatus != rendstatus_old || lines != rendlines) { emu_video_mode_change(loffs, lines, coffs, columns); rendstatus_old = Pico.est.rendstatus; rendlines = lines; sprites = 0; } Pico.est.HighCol = HighColBase + screen_offset * HighColIncrement; Pico.est.DrawLineDest = (char *)DrawLineDestBase + screen_offset * DrawLineDestIncrement; if (FinalizeLineSMS == FinalizeLine8bitSMS) { Pico.m.dirtyPal = (Pico.m.dirtyPal || Pico.est.SonicPalCount ? 2 : 0); memcpy(Pico.est.SonicPal, PicoMem.cram, 0x40*2); } Pico.est.SonicPalCount = 0; } void PicoParseSATSMS(int line) { if (Pico.video.reg[0] & 0x04) ParseSpritesM4(line); else ParseSpritesTMS(line); } void PicoLineSMS(int line) { int skip = skip_next_line; unsigned bgcolor; // GG LCD, render only visible part of screen if ((Pico.m.hardware & 0x3) == 0x3 && (line < 24 || line >= 24+144)) goto norender; if (PicoScanBegin != NULL && skip == 0) skip = PicoScanBegin(line + screen_offset); if (skip) { skip_next_line = skip - 1; return; } // Draw screen: bgcolor = (Pico.video.reg[7] & 0x0f) | ((Pico.video.reg[0] & 0x04) << 2); BackFill(bgcolor, 0, &Pico.est); // bgcolor is from 2nd palette in mode 4 if (Pico.video.reg[1] & 0x40) { if (Pico.video.reg[0] & 0x04) DrawDisplayM4(line); else if (Pico.video.reg[0] & 0x02) DrawDisplayM2(line); else DrawDisplayM0(line); } if (FinalizeLineSMS != NULL) FinalizeLineSMS(line); if (PicoScanEnd != NULL) skip_next_line = PicoScanEnd(line + screen_offset); norender: Pico.est.HighCol += HighColIncrement; Pico.est.DrawLineDest = (char *)Pico.est.DrawLineDest + DrawLineDestIncrement; } /* Fixed palette for TMS9918 modes */ static u16 tmspal[32] = { // SMS palette 0x0000, 0x0000, 0x00a0, 0x00f0, 0x0500, 0x0f00, 0x0005, 0x0ff0, 0x000a, 0x000f, 0x0055, 0x00ff, 0x0050, 0x0f0f, 0x0555, 0x0fff, // TMS palette 0x0000, 0x0000, 0x04c2, 0x07d5, 0x0e55, 0x0f77, 0x045d, 0x0fe4, 0x055f, 0x077f, 0x05cd, 0x08ce, 0x03b2, 0x0b5c, 0x0ccc, 0x0fff, }; void PicoDoHighPal555SMS(void) { u32 *spal = (void *)Pico.est.SonicPal; u32 *dpal = (void *)Pico.est.HighPal; unsigned int cnt = Pico.est.SonicPalCount+1; unsigned int t; int i, j; if (FinalizeLineSMS != FinalizeLine8bitSMS || Pico.m.dirtyPal == 2) Pico.m.dirtyPal = 0; // use hardware palette for 16bit accurate mode if (FinalizeLineSMS == FinalizeLineRGB555SMS) spal = (void *)PicoMem.cram; /* SMS 6 bit cram data was already converted to MD/GG format by vdp write, * hence GG/SMS/TMS can all be handled the same here */ for (j = cnt; j > 0; j--) { if (!(Pico.video.reg[0] & 0x4)) spal = (u32 *)tmspal; // fixed palette in TMS modes for (i = 0x20/2; i > 0; i--, spal++, dpal++) { t = *spal; #if defined(USE_BGR555) t = ((t & 0x000f000f)<<1) | ((t & 0x00f000f0)<<2) | ((t & 0x0f000f00)<<3); t |= (t >> 4) & 0x04210421; #elif defined(USE_BGR565) t = ((t & 0x000f000f)<<1) | ((t & 0x00f000f0)<<3) | ((t & 0x0f000f00)<<4); t |= (t >> 4) & 0x08610861; #else t = ((t & 0x000f000f)<<12)| ((t & 0x00f000f0)<<3) | ((t & 0x0f000f00)>>7); t |= (t >> 4) & 0x08610861; #endif *dpal = t; } memcpy(dpal, dpal-0x20/2, 0x20*2); // for prio bit spal += 0x20/2, dpal += 0x20/2; } Pico.est.HighPal[0xe0] = 0; } static void FinalizeLineRGB555SMS(int line) { if (Pico.m.dirtyPal) PicoDoHighPal555SMS(); // standard FinalizeLine can finish it for us, // with features like scaling and such FinalizeLine555(0, line, &Pico.est); } static void FinalizeLine8bitSMS(int line) { FinalizeLine8bit(0, line, &Pico.est); } void PicoDrawSetOutputSMS(pdso_t which) { switch (which) { case PDF_8BIT: FinalizeLineSMS = FinalizeLine8bitSMS; break; case PDF_RGB555: FinalizeLineSMS = FinalizeLineRGB555SMS; break; // there's no fast renderer yet, just treat it like PDF_8BIT default: FinalizeLineSMS = FinalizeLine8bitSMS; PicoDrawSetInternalBuf(Pico.est.Draw2FB, 328); break; } rendstatus_old = -1; mode = -1; } // vim:shiftwidth=2:ts=2:expandtab