sms vdp, separate SAT parsing

This commit is contained in:
kub 2021-11-06 21:00:55 +01:00
parent f55ce7bf47
commit 1ce2b0923c
3 changed files with 111 additions and 49 deletions

View file

@ -20,6 +20,14 @@ static int skip_next_line;
static int screen_offset, line_offset; static int screen_offset, line_offset;
static u8 mode; 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 */ /* sprite collision detection */
static int CollisionDetect(u8 *mb, u16 sx, unsigned int pack, int zoomed) static int CollisionDetect(u8 *mb, u16 sx, unsigned int pack, int zoomed)
{ {
@ -139,17 +147,14 @@ static void TileDoubleSprM4(int sx, unsigned int pack, int pal)
PLANAR_PIXELSP(15, 7) PLANAR_PIXELSP(15, 7)
} }
static void DrawSpritesM4(int scanline) static void ParseSpritesM4(int scanline)
{ {
struct PicoVideo *pv = &Pico.video; struct PicoVideo *pv = &Pico.video;
unsigned char mb[1+256/8+2] = {0}; // zoomed
unsigned int sprites_addr[64];
unsigned int sprites_x[64];
unsigned int pack;
u8 *sat; u8 *sat;
int xoff = 8; // relative to HighCol, which is (screen - 8) int xoff = 8; // relative to HighCol, which is (screen - 8)
int sprite_base, addr_mask; int sprite_base, addr_mask;
int zoomed = pv->reg[1] & 0x1; // zoomed sprites, e.g. Earthworm Jim int zoomed = pv->reg[1] & 0x1; // zoomed sprites, e.g. Earthworm Jim
unsigned int pack;
int i, s, h, m; int i, s, h, m;
if (pv->reg[0] & 8) if (pv->reg[0] & 8)
@ -157,7 +162,6 @@ static void DrawSpritesM4(int scanline)
xoff += line_offset; xoff += line_offset;
if ((Pico.m.hardware & 0x3) == 0x3) if ((Pico.m.hardware & 0x3) == 0x3)
xoff -= 48; // GG LCD, adjust to center 160 px xoff -= 48; // GG LCD, adjust to center 160 px
scanline --;
sat = (u8 *)PicoMem.vram + ((pv->reg[5] & 0x7e) << 7); sat = (u8 *)PicoMem.vram + ((pv->reg[5] & 0x7e) << 7);
if (pv->reg[1] & 2) { if (pv->reg[1] & 2) {
@ -168,6 +172,8 @@ static void DrawSpritesM4(int scanline)
if (zoomed) h *= 2; if (zoomed) h *= 2;
sprite_base = (pv->reg[6] & 4) << (13-2-1); 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++) for (i = s = 0; i < 64; i++)
{ {
int y; int y;
@ -180,8 +186,8 @@ static void DrawSpritesM4(int scanline)
if (y + h <= scanline || scanline < y) if (y + h <= scanline || scanline < y)
continue; // not on this line continue; // not on this line
if (s >= 8) { if (s >= 8) {
pv->status |= SR_SOVR; if (scanline >= 0) sprites_status |= SR_SOVR;
if (!(PicoIn.opt & POPT_DIS_SPRITE_LIM) || s >= 64) if (!(PicoIn.opt & POPT_DIS_SPRITE_LIM) || s >= 32)
break; break;
} }
@ -189,22 +195,34 @@ static void DrawSpritesM4(int scanline)
sprites_x[s] = xoff + sat[MEM_LE2(0x80 + i*2)]; 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)) + sprites_addr[s] = sprite_base + ((sat[MEM_LE2(0x80 + i*2 + 1)] & addr_mask) << (5-1)) +
((scanline - y) >> zoomed << (2-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++; 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 // now draw all sprites backwards
m = 0;
for (--s; s >= 0; s--) { for (--s; s >= 0; s--) {
pack = CPU_LE2(*(u32 *)(PicoMem.vram + sprites_addr[s])); pack = CPU_LE2(*(u32 *)(PicoMem.vram + sprites_addr[s]));
if (zoomed) TileDoubleSprM4(sprites_x[s], pack, 0x10); if (zoomed) TileDoubleSprM4(sprites_x[s], pack, 0x10);
else TileNormSprM4(sprites_x[s], pack, 0x10); else TileNormSprM4(sprites_x[s], pack, 0x10);
// make sprite pixel map by merging the 4 bitplanes
pack = ((pack | (pack>>16)) | ((pack | (pack>>16))>>8)) & 0xff;
if (!m) m = CollisionDetect(mb, sprites_x[s], pack, zoomed);
} }
if (m)
pv->status |= SR_C;
} }
// cells_dx, tilex_ty merged to reduce register pressure // cells_dx, tilex_ty merged to reduce register pressure
@ -295,7 +313,7 @@ static void DrawDisplayM4(int scanline)
// sprites // sprites
if (!(pv->debug_p & PVD_KILL_S_LO)) if (!(pv->debug_p & PVD_KILL_S_LO))
DrawSpritesM4(scanline); DrawSpritesM4();
if ((pv->reg[0] & 0x20) && (Pico.m.hardware & 0x3) != 0x3) { if ((pv->reg[0] & 0x20) && (Pico.m.hardware & 0x3) != 0x3) {
// first column masked with background, caculate offset to start of line // first column masked with background, caculate offset to start of line
@ -376,13 +394,9 @@ static void TileDoubleSprTMS(u16 sx, unsigned int pack, int pal)
TMS_PIXELSP(15, 7) TMS_PIXELSP(15, 7)
} }
/* Draw sprites into a scanline, max 4 */ static void ParseSpritesTMS(int scanline)
static void DrawSpritesTMS(int scanline)
{ {
struct PicoVideo *pv = &Pico.video; struct PicoVideo *pv = &Pico.video;
unsigned char mb[1+256/8+4] = {0}; // zoomed+doublesize
unsigned int sprites_addr[32];
unsigned int sprites_x[32];
unsigned int pack; unsigned int pack;
u8 *sat; u8 *sat;
int xoff = 8; // relative to HighCol, which is (screen - 8) int xoff = 8; // relative to HighCol, which is (screen - 8)
@ -391,7 +405,6 @@ static void DrawSpritesTMS(int scanline)
int i, s, h, m; int i, s, h, m;
xoff += line_offset; xoff += line_offset;
scanline --;
sat = (u8 *)PicoMem.vramb + ((pv->reg[5] & 0x7e) << 7); sat = (u8 *)PicoMem.vramb + ((pv->reg[5] & 0x7e) << 7);
if (pv->reg[1] & 2) { if (pv->reg[1] & 2) {
@ -400,13 +413,14 @@ static void DrawSpritesTMS(int scanline)
addr_mask = 0xff; h = 8; addr_mask = 0xff; h = 8;
} }
if (zoomed) h *= 2; if (zoomed) h *= 2;
sprite_base = (pv->reg[6] & 0x7) << 11; sprite_base = (pv->reg[6] & 0x7) << 11;
m = 0;
memset(sprites_map, 0, sizeof(sprites_map));
/* find sprites on this scanline */ /* find sprites on this scanline */
for (i = s = 0; i < 32; i++) for (i = s = 0; i < 32; i++)
{ {
int y; int x, y;
y = sat[MEM_LE2(4*i)]; y = sat[MEM_LE2(4*i)];
if (y == 0xd0) if (y == 0xd0)
break; break;
@ -416,42 +430,62 @@ static void DrawSpritesTMS(int scanline)
if (y + h <= scanline || scanline < y) if (y + h <= scanline || scanline < y)
continue; // not on this line continue; // not on this line
if (s >= 4) { if (s >= 4) {
pv->status |= SR_SOVR | i; if (scanline >= 0) sprites_status |= SR_SOVR | i;
if (!(PicoIn.opt & POPT_DIS_SPRITE_LIM) || s >= 32) if (!(PicoIn.opt & POPT_DIS_SPRITE_LIM) || s >= 32)
break; break;
} }
x = sat[MEM_LE2(4*i+1)] + xoff;
if (sat[MEM_LE2(4*i+3)] & 0x80)
x -= 32;
sprites_x[s] = 4*i; 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) + sprites_addr[s] = sprite_base + ((sat[MEM_LE2(4*i + 2)] & addr_mask) << 3) +
((scanline - y) >> zoomed); ((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++; 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 // now draw all sprites backwards
m = 0;
for (--s; s >= 0; s--) { for (--s; s >= 0; s--) {
int x, c, w = (zoomed ? 16: 8); int x, c, w = (zoomed ? 16: 8);
i = sprites_x[s]; x = sprites_x[s];
x = sat[MEM_LE2(i+1)] + xoff; c = sprites_c[s];
if (sat[MEM_LE2(i+3)] & 0x80)
x -= 32;
c = sat[MEM_LE2(i+3)] & 0x0f;
// c may be 0 (transparent): sprite invisible // c may be 0 (transparent): sprite invisible
if (x > 0) { if (c && x > 0) {
pack = PicoMem.vramb[MEM_LE2(sprites_addr[s])]; pack = PicoMem.vramb[MEM_LE2(sprites_addr[s])];
if (zoomed && c) TileDoubleSprTMS(x, pack, c); if (zoomed) TileDoubleSprTMS(x, pack, c);
else if (c) TileNormSprTMS(x, pack, c); else TileNormSprTMS(x, pack, c);
if (!m) m = CollisionDetect(mb, x, pack, zoomed);
} }
if((pv->reg[1] & 0x2) && (x+=w) > 0 && x < 8+256) { if (c && (pv->reg[1] & 0x2) && (x+=w) > 0 && x < 8+256) {
pack = PicoMem.vramb[MEM_LE2(sprites_addr[s]+0x10)]; pack = PicoMem.vramb[MEM_LE2(sprites_addr[s]+0x10)];
if (zoomed && c) TileDoubleSprTMS(x, pack, c); if (zoomed) TileDoubleSprTMS(x, pack, c);
else if (c) TileNormSprTMS(x, pack, c); else TileNormSprTMS(x, pack, c);
if (!m) m = CollisionDetect(mb, x, pack, zoomed);
} }
} }
if (m)
pv->status |= SR_C;
} }
/* Mode 2 */ /* Mode 2 */
@ -501,7 +535,7 @@ static void DrawDisplayM2(int scanline)
// sprites // sprites
if (!(pv->debug_p & PVD_KILL_S_LO)) if (!(pv->debug_p & PVD_KILL_S_LO))
DrawSpritesTMS(scanline); DrawSpritesTMS();
} }
/* Mode 0 */ /* Mode 0 */
@ -550,7 +584,7 @@ static void DrawDisplayM0(int scanline)
// sprites // sprites
if (!(pv->debug_p & PVD_KILL_S_LO)) if (!(pv->debug_p & PVD_KILL_S_LO))
DrawSpritesTMS(scanline); DrawSpritesTMS();
} }
@ -609,6 +643,7 @@ void PicoFrameStartSMS(void)
emu_video_mode_change(loffs, lines, coffs, columns); emu_video_mode_change(loffs, lines, coffs, columns);
rendstatus_old = Pico.est.rendstatus; rendstatus_old = Pico.est.rendstatus;
rendlines = lines; rendlines = lines;
sprites = 0;
} }
Pico.est.HighCol = HighColBase + screen_offset * HighColIncrement; Pico.est.HighCol = HighColBase + screen_offset * HighColIncrement;
@ -621,6 +656,12 @@ void PicoFrameStartSMS(void)
} }
} }
void PicoParseSATSMS(int line)
{
if (Pico.video.reg[0] & 0x04) ParseSpritesM4(line);
else ParseSpritesTMS(line);
}
void PicoLineSMS(int line) void PicoLineSMS(int line)
{ {
int skip = skip_next_line; int skip = skip_next_line;

View file

@ -704,6 +704,7 @@ PICO_INTERNAL void PicoFrameFull();
// mode4.c // mode4.c
void PicoFrameStartSMS(void); void PicoFrameStartSMS(void);
void PicoParseSATSMS(int line);
void PicoLineSMS(int line); void PicoLineSMS(int line);
void PicoDoHighPal555SMS(void); void PicoDoHighPal555SMS(void);
void PicoDrawSetOutputSMS(pdso_t which); void PicoDrawSetOutputSMS(pdso_t which);

View file

@ -20,6 +20,7 @@
extern void YM2413_regWrite(unsigned reg); extern void YM2413_regWrite(unsigned reg);
extern void YM2413_dataWrite(unsigned data); extern void YM2413_dataWrite(unsigned data);
extern unsigned sprites_status; // TODO put in some hdr file!
static unsigned char vdp_data_read(void) static unsigned char vdp_data_read(void)
{ {
@ -682,9 +683,15 @@ void PicoFrameMS(void)
PicoFrameStartSMS(); PicoFrameStartSMS();
hint = pv->reg[0x0a]; hint = pv->reg[0x0a];
// SMS: xscroll:f3 sprovr,vint, vcount:fc, hint:fd
// GG: xscroll:f5 sprovr,vint:fd vcount:fe, hint:ff
for (y = 0; y < lines; y++) for (y = 0; y < lines; y++)
{ {
pv->v_counter = Pico.m.scanline = y; Pico.t.z80c_line_start = Pico.t.z80c_aim;
// advance the line counter. It is set back at some point in the VBLANK so
// that the line count in the active area (-32..lines+1) is contiguous.
pv->v_counter = Pico.m.scanline = (u8)y;
switch (is_pal ? -lines_vis : lines_vis) { switch (is_pal ? -lines_vis : lines_vis) {
case 192: if (y > 218) pv->v_counter = y - (lines-256); break; case 192: if (y > 218) pv->v_counter = y - (lines-256); break;
case 224: if (y > 234) pv->v_counter = y - (lines-256); break; case 224: if (y > 234) pv->v_counter = y - (lines-256); break;
@ -693,14 +700,24 @@ void PicoFrameMS(void)
case -240: if (y > 266) pv->v_counter = y - (lines-256); break; case -240: if (y > 266) pv->v_counter = y - (lines-256); break;
} }
// Parse sprites for the next line
if (y < lines_vis)
PicoParseSATSMS(y-1);
else if (y > lines-32)
PicoParseSATSMS(y-1-lines);
// render next line
if (y < lines_vis && !skip) if (y < lines_vis && !skip)
PicoLineSMS(y); PicoLineSMS(y);
Pico.t.z80c_line_start = Pico.t.z80c_aim; // take over status bits from previously rendered line TODO: cycle exact?
pv->status |= sprites_status;
sprites_status = 0;
// Interrupt handling. Simulate interrupt flagged and immediately reset in // Interrupt handling. Simulate interrupt flagged and immediately reset in
// same insn by flagging the irq, execute for 1 insn, then checking if the // same insn by flagging the irq, execute for 1 insn, then checking if the
// irq is still pending. (GG Chicago, SMS Back to the Future III) // irq is still pending. (GG Chicago, SMS Back to the Future III)
pv->pending_ints &= ~2; // lost if not caught in the same line
if (y <= lines_vis) if (y <= lines_vis)
{ {
if (--hint < 0) if (--hint < 0)
@ -713,11 +730,9 @@ void PicoFrameMS(void)
elprintf(EL_INTS, "hint"); elprintf(EL_INTS, "hint");
z80_int_assert(1); z80_int_assert(1);
} }
pv->pending_ints &= ~2; // lost if not caught immediately
} }
} }
else if (y == lines_vis + 1) { else if (y == lines_vis + 1) {
pv->pending_ints &= ~2;
pv->pending_ints |= 1; pv->pending_ints |= 1;
z80_exec(Pico.t.z80c_cnt + 1); z80_exec(Pico.t.z80c_cnt + 1);
@ -736,13 +751,18 @@ void PicoFrameMS(void)
void PicoFrameDrawOnlyMS(void) void PicoFrameDrawOnlyMS(void)
{ {
struct PicoVideo *pv = &Pico.video;
int lines_vis = 192; int lines_vis = 192;
int y; int y;
if ((pv->reg[0] & 6) == 6 && (pv->reg[1] & 0x18))
lines_vis = (pv->reg[1] & 0x08) ? 240 : 224;
PicoFrameStartSMS(); PicoFrameStartSMS();
for (y = 0; y < lines_vis; y++) for (y = 0; y < lines_vis; y++) {
PicoParseSATSMS(y-1);
PicoLineSMS(y); PicoLineSMS(y);
} }
}
// vim:ts=2:sw=2:expandtab // vim:ts=2:sw=2:expandtab