diff --git a/cpu/cz80/cz80_opED.c b/cpu/cz80/cz80_opED.c index eb9f6eec..71f7dbce 100644 --- a/cpu/cz80/cz80_opED.c +++ b/cpu/cz80/cz80_opED.c @@ -491,27 +491,23 @@ OP_LDX: -----------------------------------------*/ OPED(0xb0): // LDIR - if (zBC != 1) - PC -= 2; do { val = READ_MEM8(zHL++); WRITE_MEM8(zDE++, val); zBC--; USE_CYCLES(21) - } while (zBC > 1 && (CPU->ICount > 0) && !CPU->Status); + } while (zBC && (CPU->ICount > -4) && !CPU->Status); goto OP_LDXR; OPED(0xb8): // LDDR - if (zBC != 1) - PC -= 2; do { val = READ_MEM8(zHL--); WRITE_MEM8(zDE--, val); zBC--; USE_CYCLES(21) - } while (zBC > 1 && (CPU->ICount > 0) && !CPU->Status); + } while (zBC && (CPU->ICount > -4) && !CPU->Status); OP_LDXR: F = zF & (SF | ZF | CF); @@ -520,6 +516,7 @@ OP_LDXR: if (zBC) { zF = F | VF; + PC -= 2; #if CZ80_EMULATE_R_EXACTLY zR--; #endif @@ -556,8 +553,6 @@ OP_CPX: -----------------------------------------*/ OPED(0xb1): // CPIR - if (zBC != 1) - PC -= 2; do { val = READ_MEM8(zHL++); @@ -570,12 +565,10 @@ OP_CPX: if (zBC) F |= VF; zF = F; USE_CYCLES(21) - } while (zBC > 1 && !(F & ZF) && (CPU->ICount > 0) && !CPU->Status); + } while (zBC && !(F & ZF) && (CPU->ICount > -4) && !CPU->Status); goto OP_CPXR; OPED(0xb9): // CPDR - if (zBC != 1) - PC -= 2; do { val = READ_MEM8(zHL--); @@ -588,11 +581,12 @@ OP_CPX: if (zBC) F |= VF; zF = F; USE_CYCLES(21) - } while (zBC > 1 && !(F & ZF) && (CPU->ICount > 0) && !CPU->Status); + } while (zBC && !(F & ZF) && (CPU->ICount > -4) && !CPU->Status); OP_CPXR: if (zBC && !(F & ZF)) { + PC -= 2; #if CZ80_EMULATE_R_EXACTLY zR--; #endif @@ -631,27 +625,23 @@ OP_INX: -----------------------------------------*/ OPED(0xb2): // INIR - if (zB != 1) - PC -= 2; do { val = IN(zBC); zB--; WRITE_MEM8(zHL++, val); USE_CYCLES(21) - } while (zB > 1 && (CPU->ICount > 0) && !CPU->Status); + } while (zB && (CPU->ICount > -4) && !CPU->Status); goto OP_INXR; OPED(0xba): // INDR - if (zB != 1) - PC -= 2; do { val = IN(zBC); zB--; WRITE_MEM8(zHL--, val); USE_CYCLES(21) - } while (zB > 1 && (CPU->ICount > 0) && !CPU->Status); + } while (zB && (CPU->ICount > -4) && !CPU->Status); OP_INXR: F = SZ[zB]; @@ -662,6 +652,7 @@ OP_INXR: zF = F; if (zB) { + PC -= 2; #if CZ80_EMULATE_R_EXACTLY zR--; #endif @@ -700,27 +691,23 @@ OP_OUTX: -----------------------------------------*/ OPED(0xb3): // OTIR - if (zB != 1) - PC -= 2; do { val = READ_MEM8(zHL++); zB--; OUT(zBC, val); USE_CYCLES(21) - } while (zB > 1 && (CPU->ICount > 0) && !CPU->Status); + } while (zB && (CPU->ICount > -4) && !CPU->Status); goto OP_OTXR; OPED(0xbb): // OTDR - if (zB != 1) - PC -= 2; do { val = READ_MEM8(zHL--); zB--; OUT(zBC, val); USE_CYCLES(21) - } while (zB > 1 && (CPU->ICount > 0) && !CPU->Status); + } while (zB && (CPU->ICount > -4) && !CPU->Status); OP_OTXR: F = SZ[zB]; @@ -731,6 +718,7 @@ OP_OTXR: zF = F; if (zB) { + PC -= 2; #if CZ80_EMULATE_R_EXACTLY zR--; #endif diff --git a/package b/package index 65d2fe2a..7192b6e1 100755 --- a/package +++ b/package @@ -32,7 +32,7 @@ Comment=MS/MD/MCD/32X emulator Exec=picodrive.sh %f Icon=picodrive Categories=emulators; -SelectorFilter=zip,md,smd,sms,32x,cue,cso,chd' > opk/picodrive.funkey-s.desktop +SelectorFilter=zip,bin,md,smd,sms,32x,cue,cso,chd' > opk/picodrive.funkey-s.desktop rm -f *.opk mksquashfs opk/ picodrive_v1.98_$(md5sum opk/PicoDrive | cut -c1-7)_funkey-s.opk -all-root -noappend -no-exports -no-xattrs diff --git a/pico/debug.c b/pico/debug.c index f565a3bb..e8810894 100644 --- a/pico/debug.c +++ b/pico/debug.c @@ -212,7 +212,7 @@ void PDebugShowPalette(unsigned short *screen, int stride) Pico.m.dirtyPal = 1; if (PicoIn.AHW & PAHW_SMS) - PicoDoHighPal555M4(); + PicoDoHighPal555SMS(); else PicoDoHighPal555(1, 0, est); Pico.m.dirtyPal = 1; diff --git a/pico/draw.c b/pico/draw.c index 54e48512..3ae05c54 100644 --- a/pico/draw.c +++ b/pico/draw.c @@ -1988,7 +1988,7 @@ void PicoDrawUpdateHighPal(void) sh = 0; // no s/h support if (PicoIn.AHW & PAHW_SMS) - PicoDoHighPal555M4(); + PicoDoHighPal555SMS(); else if (FinalizeLine == FinalizeLine8bit) PicoDoHighPal555_8bit(sh, 0, est); else @@ -2027,7 +2027,7 @@ void PicoDrawSetOutFormat(pdso_t which, int use_32x_line_mode) } if (PicoIn.AHW & PAHW_32X) PicoDrawSetOutFormat32x(which, use_32x_line_mode); - PicoDrawSetOutputMode4(which); + PicoDrawSetOutputSMS(which); rendstatus_old = -1; Pico.m.dirtyPal = 1; } @@ -2038,7 +2038,7 @@ void PicoDrawSetOutBufMD(void *dest, int increment) // kludge for no-copy mode, using ALT_RENDERER layout PicoDrawSetInternalBuf(dest, increment); } else if (FinalizeLine == NULL) { - PicoDrawSetInternalBuf(dest, increment); // needed for Mode4 + PicoDrawSetInternalBuf(dest, increment); // needed for SMS PicoDraw2SetOutBuf(dest, increment); } else if (dest != NULL) { DrawLineDestBase = dest; diff --git a/pico/media.c b/pico/media.c index 1294243a..f4ad8cde 100644 --- a/pico/media.c +++ b/pico/media.c @@ -35,8 +35,8 @@ static int detect_media(const char *fname) { static const short sms_offsets[] = { 0x7ff0, 0x3ff0, 0x1ff0 }; static const char *sms_exts[] = { "sms", "gg", "sg" }; - static const char *md_exts[] = { "gen", "bin", "smd" }; - char buff0[32], buff[32]; + static const char *md_exts[] = { "gen", "smd" }; + char buff0[512], buff[32]; unsigned short *d16; pm_file *pmf; char ext[5]; @@ -56,7 +56,7 @@ static int detect_media(const char *fname) if (pmf == NULL) return PM_BAD_DETECT; - if (pm_read(buff0, 32, pmf) != 32) { + if (pm_read(buff0, 512, pmf) != 512) { pm_close(pmf); return PM_BAD_DETECT; } @@ -111,6 +111,11 @@ extension_check: lprintf("bad MD reset vector, assuming SMS\n"); goto looks_like_sms; } + d16 = (unsigned short *)(buff0 + 0x1a0); + if ((((d16[0] << 16) | d16[1]) & 0xffffff) != 0) { + lprintf("bad MD rom start, assuming SMS\n"); + goto looks_like_sms; + } looks_like_md: pm_close(pmf); diff --git a/pico/memory.c b/pico/memory.c index 4d2cb7a1..9c35a181 100644 --- a/pico/memory.c +++ b/pico/memory.c @@ -57,6 +57,10 @@ void z80_map_set(uptr *map, int start_addr, int end_addr, const void *func_or_mh, int is_func) { xmap_set(map, Z80_MEM_SHIFT, start_addr, end_addr, func_or_mh, is_func); +#ifdef _USE_CZ80 + if (!is_func) + Cz80_Set_Fetch(&CZ80, start_addr, end_addr, (FPTR)func_or_mh); +#endif } void cpu68k_map_set(uptr *map, int start_addr, int end_addr, @@ -1290,8 +1294,6 @@ static void z80_mem_setup(void) drZ80.z80_out = z80_md_out; #endif #ifdef _USE_CZ80 - Cz80_Set_Fetch(&CZ80, 0x0000, 0x1fff, (FPTR)PicoMem.zram); // main RAM - Cz80_Set_Fetch(&CZ80, 0x2000, 0x3fff, (FPTR)PicoMem.zram); // mirror Cz80_Set_INPort(&CZ80, z80_md_in); Cz80_Set_OUTPort(&CZ80, z80_md_out); #endif diff --git a/pico/mode4.c b/pico/mode4.c index 756e26a4..889a43e0 100644 --- a/pico/mode4.c +++ b/pico/mode4.c @@ -1,40 +1,45 @@ /* - * mode4/SMS renderer + * SMS renderer * (C) notaz, 2009-2010 + * (C) kub, 2021 + * + * currently supports VDP mode 4 (SMS and GG) and mode 2 * * This work is licensed under the terms of MAME license. * See COPYING file in the top-level directory. */ /* * TODO: - * - TMS9918 modes? - * - gg mode? - * - column scroll (reg 0 bit7) - * - 224/240 line modes - * - doubled sprites + * - other TMS9918 modes? */ #include "pico_int.h" #include -static void (*FinalizeLineM4)(int line); +static void (*FinalizeLineSMS)(int line); static int skip_next_line; static int screen_offset, line_offset; -static void TileBGM4(int sx, int pal) + +/* Mode 4 */ +/*========*/ + +static void TileBGM4(u16 sx, int pal) { u32 *pd = (u32 *)(Pico.est.HighCol + sx); pd[0] = pd[1] = pal ? 0x10101010 : 0; } +// 8 pixels are arranged to have 1 bit in each byte of a 32 bit word. To pull +// the 4 bitplanes together multiply with each bit distance (multiples of 1<<7) #define PLANAR_PIXELL(x,p) \ - t = pack & (0x80808080 >> p); \ - t = ((t >> (7-p)) | (t >> (14-p)) | (t >> (21-p)) | (t >> (28-p))) & 0x0f; \ + t = (pack>>(7-p)) & 0x01010101; \ + t = (t*0x10204080) >> 28; \ pd[x] = pal|t; -static void TileNormM4Low(int sx, unsigned int pack, int pal) +static void TileNormLowM4(u16 sx, unsigned int pack, int pal) { - unsigned char *pd = Pico.est.HighCol + sx; - unsigned int t; + u8 *pd = Pico.est.HighCol + sx; + u32 t; PLANAR_PIXELL(0, 0) PLANAR_PIXELL(1, 1) @@ -46,10 +51,10 @@ static void TileNormM4Low(int sx, unsigned int pack, int pal) PLANAR_PIXELL(7, 7) } -static void TileFlipM4Low(int sx, unsigned int pack, int pal) +static void TileFlipLowM4(u16 sx, unsigned int pack, int pal) { - unsigned char *pd = Pico.est.HighCol + sx; - unsigned int t; + u8 *pd = Pico.est.HighCol + sx; + u32 t; PLANAR_PIXELL(0, 7) PLANAR_PIXELL(1, 6) @@ -62,16 +67,16 @@ static void TileFlipM4Low(int sx, unsigned int pack, int pal) } #define PLANAR_PIXEL(x,p) \ - t = pack & (0x80808080 >> p); \ + t = (pack>>(7-p)) & 0x01010101; \ if (t) { \ - t = ((t >> (7-p)) | (t >> (14-p)) | (t >> (21-p)) | (t >> (28-p))) & 0x0f; \ + t = (t*0x10204080) >> 28; \ pd[x] = pal|t; \ } -static void TileNormM4(int sx, unsigned int pack, int pal) +static void TileNormM4(u16 sx, unsigned int pack, int pal) { - unsigned char *pd = Pico.est.HighCol + sx; - unsigned int t; + u8 *pd = Pico.est.HighCol + sx; + u32 t; PLANAR_PIXEL(0, 0) PLANAR_PIXEL(1, 1) @@ -83,10 +88,10 @@ static void TileNormM4(int sx, unsigned int pack, int pal) PLANAR_PIXEL(7, 7) } -static void TileFlipM4(int sx, unsigned int pack, int pal) +static void TileFlipM4(u16 sx, unsigned int pack, int pal) { - unsigned char *pd = Pico.est.HighCol + sx; - unsigned int t; + u8 *pd = Pico.est.HighCol + sx; + u32 t; PLANAR_PIXEL(0, 7) PLANAR_PIXEL(1, 6) @@ -98,34 +103,59 @@ static void TileFlipM4(int sx, unsigned int pack, int pal) PLANAR_PIXEL(7, 0) } -static void draw_sprites(int scanline) +static void TileDoubleM4(int sx, unsigned int pack, int pal) +{ + u8 *pd = Pico.est.HighCol + sx; + u32 t; + + PLANAR_PIXEL(0, 0) + PLANAR_PIXEL(1, 0) + PLANAR_PIXEL(2, 1) + PLANAR_PIXEL(3, 1) + PLANAR_PIXEL(4, 2) + PLANAR_PIXEL(5, 2) + PLANAR_PIXEL(6, 3) + PLANAR_PIXEL(7, 3) + PLANAR_PIXEL(8, 4) + PLANAR_PIXEL(9, 4) + PLANAR_PIXEL(10, 5) + PLANAR_PIXEL(11, 5) + PLANAR_PIXEL(12, 6) + PLANAR_PIXEL(13, 6) + PLANAR_PIXEL(14, 7) + PLANAR_PIXEL(15, 7) +} + +static void DrawSpritesM4(int scanline) { struct PicoVideo *pv = &Pico.video; unsigned int sprites_addr[8]; unsigned int sprites_x[8]; unsigned int pack; - unsigned char *sat; + 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 int i, s, h; if (pv->reg[0] & 8) xoff = 0; xoff += line_offset; - sat = (unsigned char *)PicoMem.vram + ((pv->reg[5] & 0x7e) << 7); + 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); for (i = s = 0; i < 64; i++) { int y; - y = sat[MEM_LE2(i)] + 1; - if (y == 0xd1) + y = (sat[MEM_LE2(i)] + 1) & 0xff; + if (y == 0xd1 && !((pv->reg[0] & 6) == 6 && (pv->reg[1] & 0x18))) break; if (y + h <= scanline || scanline < y) continue; // not on this line @@ -136,7 +166,7 @@ static void draw_sprites(int scanline) 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) << (2-1)); + ((scanline - y) >> zoomed << (2-1)); s++; } @@ -147,30 +177,32 @@ static void draw_sprites(int scanline) // now draw all sprites backwards for (--s; s >= 0; s--) { pack = CPU_LE2(*(u32 *)(PicoMem.vram + sprites_addr[s])); - TileNormM4(sprites_x[s], pack, 0x10); + if (zoomed) TileDoubleM4(sprites_x[s], pack, 0x10); + else TileNormM4(sprites_x[s], pack, 0x10); } } - -// tilex_ty_prio merged to reduce register pressure -static void draw_strip_low(const unsigned short *nametab, int dx, int cells, int tilex_ty_prio) +// cells_dx, tilex_ty merged to reduce register pressure +static void DrawStripLowM4(const u16 *nametab, int cells_dx, int tilex_ty) { int oldcode = -1; int addr = 0, pal = 0; // Draw tiles across screen: - for (; cells > 0; dx += 8, tilex_ty_prio++, cells--) + for (; cells_dx > 0; cells_dx += 8, tilex_ty++, cells_dx -= 0x10000) { unsigned int pack; unsigned code; - code = nametab[tilex_ty_prio & 0x1f]; + code = nametab[tilex_ty& 0x1f]; + if (code & 0x1000) // priority high? + continue; if (code != oldcode) { oldcode = code; // Get tile address/2: addr = (code & 0x1ff) << 4; - addr += tilex_ty_prio >> 16; + addr += tilex_ty>> 16; if (code & 0x0400) addr ^= 0xe; // Y-flip @@ -178,34 +210,34 @@ static void draw_strip_low(const unsigned short *nametab, int dx, int cells, int } pack = CPU_LE2(*(u32 *)(PicoMem.vram + addr)); /* Get 4 bitplanes / 8 pixels */ - if (pack == 0) TileBGM4(dx, pal); - else if (code & 0x0200) TileFlipM4Low(dx, pack, pal); - else TileNormM4Low(dx, pack, pal); + if (pack == 0) TileBGM4(cells_dx, pal); + else if (code & 0x0200) TileFlipLowM4(cells_dx, pack, pal); + else TileNormLowM4(cells_dx, pack, pal); } } -// tilex_ty_prio merged to reduce register pressure -static void draw_strip_high(const unsigned short *nametab, int dx, int cells, int tilex_ty_prio) + +static void DrawStripHighM4(const u16 *nametab, int cells_dx, int tilex_ty) { int oldcode = -1, blank = -1; // The tile we know is blank int addr = 0, pal = 0; // Draw tiles across screen: - for (; cells > 0; dx += 8, tilex_ty_prio++, cells--) + for (; cells_dx > 0; cells_dx += 8, tilex_ty++, cells_dx -= 0x10000) { unsigned int pack; unsigned code; - code = nametab[tilex_ty_prio & 0x1f]; + code = nametab[tilex_ty& 0x1f]; if (code == blank) continue; - if ((code ^ tilex_ty_prio) & 0x1000) // priority differs? + if (!(code & 0x1000)) // priority low? continue; if (code != oldcode) { oldcode = code; // Get tile address/2: addr = (code & 0x1ff) << 4; - addr += tilex_ty_prio >> 16; + addr += tilex_ty>> 16; if (code & 0x0400) addr ^= 0xe; // Y-flip @@ -217,15 +249,15 @@ static void draw_strip_high(const unsigned short *nametab, int dx, int cells, in blank = code; continue; } - if (code & 0x0200) TileFlipM4(dx, pack, pal); - else TileNormM4(dx, pack, pal); + if (code & 0x0200) TileFlipM4(cells_dx, pack, pal); + else TileNormM4(cells_dx, pack, pal); } } static void DrawDisplayM4(int scanline) { struct PicoVideo *pv = &Pico.video; - unsigned short *nametab; + u16 *nametab, *nametab2; int line, tilex, dx, ty, cells; int cellskip = 0; // XXX int maxcells = 32; @@ -236,6 +268,7 @@ static void DrawDisplayM4(int 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 { @@ -244,11 +277,12 @@ static void DrawDisplayM4(int scanline) // old SMS only, masks line:7 with reg[2]:0 for address calculation //if ((pv->reg[2] & 0x01) == 0) line &= 0x7f; } - nametab += (line>>3) << (6-1); + 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 + 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 @@ -261,41 +295,250 @@ static void DrawDisplayM4(int scanline) dx += line_offset; // low priority tiles - if (!(pv->debug_p & PVD_KILL_B)) - draw_strip_low(nametab, dx, cells, tilex | 0x0000 | (ty << 16)); + if (!(pv->debug_p & PVD_KILL_B)) { + 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; + DrawStripLowM4(nametab, dx | ((cells-8) << 16), tilex | (ty << 16)); + DrawStripLowM4(nametab2, dx2 | (8 << 16), tilex2 | (ty2 << 17)); + } else + DrawStripLowM4(nametab , dx | ( cells << 16), tilex | (ty << 16)); + } // sprites if (!(pv->debug_p & PVD_KILL_S_LO)) - draw_sprites(scanline); + DrawSpritesM4(scanline); // high priority tiles (use virtual layer switch just for fun) - if (!(pv->debug_p & PVD_KILL_A)) - draw_strip_high(nametab, dx, cells, tilex | 0x1000 | (ty << 16)); + if (!(pv->debug_p & PVD_KILL_A)) { + if (pv->reg[0] & 0x80) { + int dx2 = dx + (cells-8)*8, tilex2 = tilex + (cells-8), ty2 = scanline & 7; + DrawStripHighM4(nametab, dx | ((cells-8) << 16), tilex | (ty << 16)); + DrawStripHighM4(nametab2, dx2 | (8 << 16), tilex2 | (ty2 << 17)); + } else + DrawStripHighM4(nametab , dx | ( cells << 16), tilex | (ty << 16)); + } if (pv->reg[0] & 0x20) { - // first column masked, caculate offset to start of line + // first column masked with background, caculate offset to start of line dx = (dx&~0x1f) / 4; - ((u32 *)Pico.est.HighCol)[dx+2] = ((u32 *)Pico.est.HighCol)[dx+3] = 0xe0e0e0e0; + ty = 0xe0e0e0e0; // really (pv->reg[7]&0x3f) * 0x01010101, but the looks... + ((u32 *)Pico.est.HighCol)[dx+2] = ((u32 *)Pico.est.HighCol)[dx+3] = ty; } } -static void FinalizeLineRGB555M4(int line); -void PicoFrameStartMode4(void) + +/* Mode 2 */ +/*========*/ + +/* Background */ + +#define TMS_PIXELBG(x,p) \ + t = (pack>>(7-p)) & 0x01; \ + t = (pal >> (t << 2)) & 0x0f; \ + pd[x] = t; + +static void TileNormBgM2(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 TileNormSprM2(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 TileDoubleSprM2(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) +} + +/* Draw sprites into a scanline, max 4 */ +static void DrawSpritesM2(int scanline) +{ + struct PicoVideo *pv = &Pico.video; + unsigned int sprites_addr[4]; + unsigned int sprites_x[4]; + 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; + + 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; + + /* find sprites on this scanline */ + for (i = s = 0; i < 32; i++) + { + int y; + y = (sat[MEM_LE2(4*i)] + 1) & 0xff; + if (y == 0xd1) + break; + if (y > 0xe0) + y -= 256; + if (y + h <= scanline || scanline < y) + continue; // not on this line + if (s >= 4) { + pv->status |= SR_SOVR | i; + break; + } + + sprites_x[s] = 4*i; + sprites_addr[s] = sprite_base + ((sat[MEM_LE2(4*i + 2)] & addr_mask) << 3) + + ((scanline - y) >> zoomed); + s++; + } + + // really half-assed but better than nothing + if (s > 1) + pv->status |= SR_C; + + // now draw all sprites backwards + for (--s; s >= 0; s--) { + int x, w = (zoomed ? 16: 8); + i = sprites_x[s]; + x = sat[MEM_LE2(i+1)] + xoff; + if (sat[MEM_LE2(i+3)] & 0x80) + x -= 32; + if (x > 0) { + pack = PicoMem.vramb[MEM_LE2(sprites_addr[s])]; + if (zoomed) TileDoubleSprM2(x, pack, sat[MEM_LE2(i+3)] & 0xf); + else TileNormSprM2(x, pack, sat[MEM_LE2(i+3)] & 0xf); + } + if((pv->reg[1] & 0x2) && (x+=w) > 0) { + pack = PicoMem.vramb[MEM_LE2(sprites_addr[s]+0x10)]; + if (zoomed) TileDoubleSprM2(x, pack, sat[MEM_LE2(i+3)] & 0xf); + else TileNormSprM2(x, pack, sat[MEM_LE2(i+3)] & 0xf); + } + } +} + +/* 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]; + TileNormBgM2(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)) + DrawSpritesM2(scanline); +} + + +/* Common/global */ +/*===============*/ + +static void FinalizeLineRGB555SMS(int line); + +void PicoFrameStartSMS(void) { int lines = 192, columns = 256, coffs; skip_next_line = 0; screen_offset = 24; Pico.est.rendstatus = PDRAW_32_COLS; - if ((Pico.video.reg[0] & 6) == 6 && (Pico.video.reg[1] & 0x18)) { - if (Pico.video.reg[1] & 0x08) { + switch ((Pico.video.reg[0]&0x06) | (Pico.video.reg[1]&0x18)) { + // SMS2 only 224/240 line modes, e.g. Micro Machines + case 0x06|0x08: screen_offset = 0; lines = 240; - } - else { + break; + case 0x06|0x10: screen_offset = 8; lines = 224; - } + break; } if (PicoIn.opt & POPT_EN_SOFTSCALE) { line_offset = 0; @@ -304,7 +547,7 @@ void PicoFrameStartMode4(void) line_offset = PicoIn.opt & POPT_DIS_32C_BORDER ? 0 : 32; coffs = line_offset; - if (FinalizeLineM4 == FinalizeLineRGB555M4) + if (FinalizeLineSMS == FinalizeLineRGB555SMS) line_offset = 0 /* done in FinalizeLine */; if (Pico.est.rendstatus != rendstatus_old || lines != rendlines) { @@ -317,7 +560,7 @@ void PicoFrameStartMode4(void) Pico.est.DrawLineDest = (char *)DrawLineDestBase + screen_offset * DrawLineDestIncrement; } -void PicoLineMode4(int line) +void PicoLineSMS(int line) { if (skip_next_line > 0) { skip_next_line--; @@ -329,11 +572,13 @@ void PicoLineMode4(int line) // Draw screen: BackFill(Pico.video.reg[7] & 0x0f, 0, &Pico.est); - if (Pico.video.reg[1] & 0x40) - DrawDisplayM4(line); + if (Pico.video.reg[1] & 0x40) { + if (Pico.video.reg[0] & 0x04) DrawDisplayM4(line); + else DrawDisplayM2(line); + } - if (FinalizeLineM4 != NULL) - FinalizeLineM4(line); + if (FinalizeLineSMS != NULL) + FinalizeLineSMS(line); if (PicoScanEnd != NULL) skip_next_line = PicoScanEnd(line + screen_offset); @@ -342,7 +587,12 @@ void PicoLineMode4(int line) Pico.est.DrawLineDest = (char *)Pico.est.DrawLineDest + DrawLineDestIncrement; } -void PicoDoHighPal555M4(void) +/* Fixed palette for TMS9918 modes */ +static u16 tmspal[32] = { + 0x00,0x00,0x08,0x0c,0x10,0x30,0x01,0x3c,0x02,0x03,0x05,0x0f,0x04,0x33,0x15,0x3f +}; + +void PicoDoHighPal555SMS(void) { unsigned int *spal=(void *)PicoMem.cram; unsigned int *dpal=(void *)Pico.est.HighPal; @@ -350,9 +600,11 @@ void PicoDoHighPal555M4(void) int i; Pico.m.dirtyPal = 0; + if (!(Pico.video.reg[0] & 0x4)) + spal = (u32 *)tmspal; /* cram is always stored as shorts, even though real hardware probably uses bytes */ - for (i = 0x20/2; i > 0; i--, spal++, dpal++) { + if (PicoIn.AHW & PAHW_SMS) for (i = 0x20/2; i > 0; i--, spal++, dpal++) { t = *spal; #if defined(USE_BGR555) t = ((t & 0x00030003)<< 3) | ((t & 0x000c000c)<<6) | ((t & 0x00300030)<<9); @@ -363,42 +615,55 @@ void PicoDoHighPal555M4(void) #else t = ((t & 0x00030003)<<14) | ((t & 0x000c000c)<<7) | ((t & 0x00300030)>>1); t |= (t >> 2) | ((t >> 4) & 0x08610861); +#endif + *dpal = t; + } else for (i = 0x20/2; i > 0; i--, spal++, dpal++) { // GG palette 4 bit/col + 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; } Pico.est.HighPal[0xe0] = 0; } -static void FinalizeLineRGB555M4(int line) +static void FinalizeLineRGB555SMS(int line) { if (Pico.m.dirtyPal) - PicoDoHighPal555M4(); + PicoDoHighPal555SMS(); // standard FinalizeLine can finish it for us, // with features like scaling and such FinalizeLine555(0, line, &Pico.est); } -static void FinalizeLine8bitM4(int line) +static void FinalizeLine8bitSMS(int line) { - unsigned char *pd = Pico.est.DrawLineDest + line_offset; - unsigned char *ps = Pico.est.HighCol + line_offset + 8; + u8 *pd = Pico.est.DrawLineDest + line_offset; + u8 *ps = Pico.est.HighCol + line_offset + 8; if (DrawLineDestIncrement) { if (PicoIn.opt & POPT_EN_SOFTSCALE) rh_upscale_nn_4_5(pd, 320, ps, 256, 256, f_nop); - else + else if (pd != ps) memcpy(pd, ps, 256); } } -void PicoDrawSetOutputMode4(pdso_t which) +void PicoDrawSetOutputSMS(pdso_t which) { switch (which) { - case PDF_8BIT: FinalizeLineM4 = FinalizeLine8bitM4; break; - case PDF_RGB555: FinalizeLineM4 = FinalizeLineRGB555M4; break; - default: FinalizeLineM4 = NULL; + case PDF_8BIT: FinalizeLineSMS = FinalizeLine8bitSMS; break; + case PDF_RGB555: FinalizeLineSMS = FinalizeLineRGB555SMS; break; + default: FinalizeLineSMS = NULL; PicoDrawSetInternalBuf(Pico.est.Draw2FB, 328); break; } rendstatus_old = -1; diff --git a/pico/pico_int.h b/pico/pico_int.h index f4dcef3c..9a66344c 100644 --- a/pico/pico_int.h +++ b/pico/pico_int.h @@ -347,7 +347,8 @@ struct PicoMS unsigned char carthw[0x10]; unsigned char io_ctl; unsigned char nmi_state; - unsigned char pad[0x4e]; + unsigned char mapper; + unsigned char pad[0x4d]; }; // emu state and data for the asm code @@ -686,10 +687,10 @@ void PicoDraw2Init(void); PICO_INTERNAL void PicoFrameFull(); // mode4.c -void PicoFrameStartMode4(void); -void PicoLineMode4(int line); -void PicoDoHighPal555M4(void); -void PicoDrawSetOutputMode4(pdso_t which); +void PicoFrameStartSMS(void); +void PicoLineSMS(int line); +void PicoDoHighPal555SMS(void); +void PicoDrawSetOutputSMS(pdso_t which); // memory.c PICO_INTERNAL void PicoMemSetup(void); diff --git a/pico/sms.c b/pico/sms.c index 4901e089..5f175ca6 100644 --- a/pico/sms.c +++ b/pico/sms.c @@ -8,7 +8,6 @@ /* * TODO: * - start in a state as if BIOS ran - * - RAM support in mapper * - region support * - H counter */ @@ -231,7 +230,8 @@ static void write_sram(unsigned short a, unsigned char d) Pico.sv.data[a] = d; } -static void write_bank(unsigned short a, unsigned char d) +// 16KB bank mapping for Sega mapper +static void write_bank_sega(unsigned short a, unsigned char d) { elprintf(EL_Z80BNK, "bank %04x %02x @ %04x", a, d, z80_pc()); Pico.ms.carthw[a & 0x0f] = d; @@ -240,16 +240,10 @@ static void write_bank(unsigned short a, unsigned char d) case 0x0d: d &= bank_mask; z80_map_set(z80_read_map, 0x0400, 0x3fff, Pico.rom+0x400 + (d << 14), 0); -#ifdef _USE_CZ80 - Cz80_Set_Fetch(&CZ80, 0x0400, 0x3fff, (FPTR)Pico.rom+0x400 + (d << 14)); -#endif break; case 0x0e: d &= bank_mask; z80_map_set(z80_read_map, 0x4000, 0x7fff, Pico.rom + (d << 14), 0); -#ifdef _USE_CZ80 - Cz80_Set_Fetch(&CZ80, 0x4000, 0x7fff, (FPTR)Pico.rom + (d << 14)); -#endif break; case 0x0c: @@ -260,39 +254,50 @@ static void write_bank(unsigned short a, unsigned char d) if (Pico.ms.carthw[0xc] & 0x08) { d = (Pico.ms.carthw[0xc] & 0x04) >> 2; z80_map_set(z80_read_map, 0x8000, 0xbfff, Pico.sv.data + d*0x4000, 0); -#ifdef _USE_CZ80 - Cz80_Set_Fetch(&CZ80, 0x8000, 0xbfff, (FPTR)Pico.sv.data + d*0x4000); -#endif z80_map_set(z80_write_map, 0x8000, 0xbfff, write_sram, 1); } else { d = Pico.ms.carthw[0xf] & bank_mask; z80_map_set(z80_read_map, 0x8000, 0xbfff, Pico.rom + (d << 14), 0); -#ifdef _USE_CZ80 - Cz80_Set_Fetch(&CZ80, 0x8000, 0xbfff, (FPTR)Pico.rom + (d << 14)); -#endif z80_map_set(z80_write_map, 0x8000, 0xbfff, xwrite, 1); } break; } } +// 8KB ROM mapping for MSX mapper +static void write_bank_msx(unsigned short a, unsigned char d) +{ + Pico.ms.mapper = 1; // TODO define (more) mapper types + Pico.ms.carthw[a] = d; + + a = (a^2)*0x2000 + 0x4000; + d &= 2*bank_mask + 1; + z80_map_set(z80_read_map, a, a+0x1fff, Pico.rom + (d << 13), 0); +} + +// TODO mapping is currently auto-selecting, but that's not very reliable. static void xwrite(unsigned int a, unsigned char d) { elprintf(EL_IO, "z80 write [%04x] %02x", a, d); if (a >= 0xc000) PicoMem.zram[a & 0x1fff] = d; - if (a >= 0xfff8) - write_bank(a, d); - // codemasters - if (a == 0x0000) - write_bank(0xfffd, d); + + // Sega. Maps 4 bank 16KB each + if (a >= 0xfff8 /*&& !Pico.ms.mapper*/) + write_bank_sega(a, d); + // Codemasters. Similar to Sega, but different addresses + if (a == 0x0000 && !Pico.ms.mapper) + write_bank_sega(0xfffd, d); if (a == 0x4000) - write_bank(0xfffe, d); + write_bank_sega(0xfffe, d); if (a == 0x8000) - write_bank(0xffff, d); - // korean + write_bank_sega(0xffff, d); + // Korean. 1 selectable 16KB bank at the top if (a == 0xa000) - write_bank(0xffff, d); + write_bank_sega(0xffff, d); + // MSX. 4 selectable 8KB banks at the top + if (a <= 0x0003 && (a || Pico.ms.mapper)) + write_bank_msx(a, d); } void PicoResetMS(void) @@ -300,6 +305,10 @@ void PicoResetMS(void) z80_reset(); PsndReset(); // pal must be known here ymflag = 0xffff; + Pico.m.dirtyPal = 1; + + // reset memory mapping + PicoMemSetupMS(); } void PicoPowerMS(void) @@ -321,11 +330,6 @@ void PicoPowerMS(void) tmp = 1 << s; bank_mask = (tmp - 1) >> 14; - Pico.ms.carthw[0x0c] = 0; - Pico.ms.carthw[0x0d] = 0; - Pico.ms.carthw[0x0e] = 1; - Pico.ms.carthw[0x0f] = 2; - PicoReset(); } @@ -344,20 +348,36 @@ void PicoMemSetupMS(void) drZ80.z80_out = z80_sms_out; #endif #ifdef _USE_CZ80 - Cz80_Set_Fetch(&CZ80, 0x0000, 0xbfff, (FPTR)Pico.rom); - Cz80_Set_Fetch(&CZ80, 0xc000, 0xdfff, (FPTR)PicoMem.zram); - Cz80_Set_Fetch(&CZ80, 0xe000, 0xffff, (FPTR)PicoMem.zram); Cz80_Set_INPort(&CZ80, z80_sms_in); Cz80_Set_OUTPort(&CZ80, z80_sms_out); #endif + + // memory mapper setup, linearly mapped, default is Sega mapper + Pico.ms.carthw[0x00] = 4; + Pico.ms.carthw[0x01] = 5; + Pico.ms.carthw[0x02] = 2; + Pico.ms.carthw[0x03] = 3; + + Pico.ms.carthw[0x0c] = 0; + Pico.ms.carthw[0x0d] = 0; + Pico.ms.carthw[0x0e] = 1; + Pico.ms.carthw[0x0f] = 2; + Pico.ms.mapper = 0; } void PicoStateLoadedMS(void) { - write_bank(0xfffc, Pico.ms.carthw[0x0c]); - write_bank(0xfffd, Pico.ms.carthw[0x0d]); - write_bank(0xfffe, Pico.ms.carthw[0x0e]); - write_bank(0xffff, Pico.ms.carthw[0x0f]); + if (Pico.ms.mapper) { + xwrite(0x0000, Pico.ms.carthw[0]); + xwrite(0x0001, Pico.ms.carthw[1]); + xwrite(0x0002, Pico.ms.carthw[2]); + xwrite(0x0003, Pico.ms.carthw[3]); + } else { + xwrite(0xfffc, Pico.ms.carthw[0x0c]); + xwrite(0xfffd, Pico.ms.carthw[0x0d]); + xwrite(0xfffe, Pico.ms.carthw[0x0e]); + xwrite(0xffff, Pico.ms.carthw[0x0f]); + } } void PicoFrameMS(void) @@ -382,7 +402,7 @@ void PicoFrameMS(void) if ((pv->reg[0] & 6) == 6 && (pv->reg[1] & 0x18)) lines_vis = (pv->reg[1] & 0x08) ? 240 : 224; - PicoFrameStartMode4(); + PicoFrameStartSMS(); hint = pv->reg[0x0a]; for (y = 0; y < lines; y++) @@ -392,7 +412,7 @@ void PicoFrameMS(void) pv->v_counter = y - 6; if (y < lines_vis && !skip) - PicoLineMode4(y); + PicoLineSMS(y); if (y <= lines_vis) { @@ -428,10 +448,10 @@ void PicoFrameDrawOnlyMS(void) int lines_vis = 192; int y; - PicoFrameStartMode4(); + PicoFrameStartSMS(); for (y = 0; y < lines_vis; y++) - PicoLineMode4(y); + PicoLineSMS(y); } // vim:ts=2:sw=2:expandtab