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)
{
PLANAR_PIXELSP(15, 7)
}
-static void DrawSpritesM4(int scanline)
+static void ParseSpritesM4(int scanline)
{
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;
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 += line_offset;
if ((Pico.m.hardware & 0x3) == 0x3)
xoff -= 48; // GG LCD, adjust to center 160 px
- scanline --;
sat = (u8 *)PicoMem.vram + ((pv->reg[5] & 0x7e) << 7);
if (pv->reg[1] & 2) {
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;
if (y + h <= scanline || scanline < y)
continue; // not on this line
if (s >= 8) {
- pv->status |= SR_SOVR;
- if (!(PicoIn.opt & POPT_DIS_SPRITE_LIM) || s >= 64)
+ if (scanline >= 0) sprites_status |= SR_SOVR;
+ if (!(PicoIn.opt & POPT_DIS_SPRITE_LIM) || s >= 32)
break;
}
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
- m = 0;
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);
- // 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
// sprites
if (!(pv->debug_p & PVD_KILL_S_LO))
- DrawSpritesM4(scanline);
+ DrawSpritesM4();
if ((pv->reg[0] & 0x20) && (Pico.m.hardware & 0x3) != 0x3) {
// first column masked with background, caculate offset to start of line
TMS_PIXELSP(15, 7)
}
-/* Draw sprites into a scanline, max 4 */
-static void DrawSpritesTMS(int scanline)
+static void ParseSpritesTMS(int scanline)
{
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;
u8 *sat;
int xoff = 8; // relative to HighCol, which is (screen - 8)
int i, s, h, m;
xoff += line_offset;
- scanline --;
sat = (u8 *)PicoMem.vramb + ((pv->reg[5] & 0x7e) << 7);
if (pv->reg[1] & 2) {
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 y;
+ int x, y;
y = sat[MEM_LE2(4*i)];
if (y == 0xd0)
break;
if (y + h <= scanline || scanline < y)
continue; // not on this line
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)
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) +
((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
- m = 0;
for (--s; s >= 0; s--) {
int x, c, w = (zoomed ? 16: 8);
- i = sprites_x[s];
- x = sat[MEM_LE2(i+1)] + xoff;
- if (sat[MEM_LE2(i+3)] & 0x80)
- x -= 32;
- c = sat[MEM_LE2(i+3)] & 0x0f;
+ x = sprites_x[s];
+ c = sprites_c[s];
// c may be 0 (transparent): sprite invisible
- if (x > 0) {
+ if (c && x > 0) {
pack = PicoMem.vramb[MEM_LE2(sprites_addr[s])];
- if (zoomed && c) TileDoubleSprTMS(x, pack, c);
- else if (c) TileNormSprTMS(x, pack, c);
- if (!m) m = CollisionDetect(mb, x, pack, zoomed);
+ if (zoomed) TileDoubleSprTMS(x, pack, c);
+ else TileNormSprTMS(x, pack, c);
}
- 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)];
- if (zoomed && c) TileDoubleSprTMS(x, pack, c);
- else if (c) TileNormSprTMS(x, pack, c);
- if (!m) m = CollisionDetect(mb, x, pack, zoomed);
+ if (zoomed) TileDoubleSprTMS(x, pack, c);
+ else TileNormSprTMS(x, pack, c);
}
}
- if (m)
- pv->status |= SR_C;
}
/* Mode 2 */
// sprites
if (!(pv->debug_p & PVD_KILL_S_LO))
- DrawSpritesTMS(scanline);
+ DrawSpritesTMS();
}
/* Mode 0 */
// sprites
if (!(pv->debug_p & PVD_KILL_S_LO))
- DrawSpritesTMS(scanline);
+ DrawSpritesTMS();
}
emu_video_mode_change(loffs, lines, coffs, columns);
rendstatus_old = Pico.est.rendstatus;
rendlines = lines;
+ sprites = 0;
}
Pico.est.HighCol = HighColBase + screen_offset * HighColIncrement;
}
}
+void PicoParseSATSMS(int line)
+{
+ if (Pico.video.reg[0] & 0x04) ParseSpritesM4(line);
+ else ParseSpritesTMS(line);
+}
+
void PicoLineSMS(int line)
{
int skip = skip_next_line;
extern void YM2413_regWrite(unsigned reg);
extern void YM2413_dataWrite(unsigned data);
+extern unsigned sprites_status; // TODO put in some hdr file!
static unsigned char vdp_data_read(void)
{
z80_map_set(z80_write_map, 0x0000, 0xbfff, xwrite, 1);
z80_map_set(z80_write_map, 0xc000, 0xdfff, PicoMem.zram, 0);
z80_map_set(z80_write_map, 0xe000, 0xffff, xwrite, 1);
-
+
// Nemesis mapper maps last 8KB rom bank #15 to adress 0
if (Pico.ms.mapper == PMS_MAP_NEMESIS && Pico.romsize > 0x1e000)
z80_map_set(z80_read_map, 0x0000, 0x1fff, Pico.rom + 0x1e000, 0);
PicoFrameStartSMS();
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++)
{
- 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) {
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 -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)
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
// 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)
+ pv->pending_ints &= ~2; // lost if not caught in the same line
if (y <= lines_vis)
{
if (--hint < 0)
elprintf(EL_INTS, "hint");
z80_int_assert(1);
}
- pv->pending_ints &= ~2; // lost if not caught immediately
}
}
else if (y == lines_vis + 1) {
- pv->pending_ints &= ~2;
pv->pending_ints |= 1;
z80_exec(Pico.t.z80c_cnt + 1);
void PicoFrameDrawOnlyMS(void)
{
+ struct PicoVideo *pv = &Pico.video;
int lines_vis = 192;
int y;
+ if ((pv->reg[0] & 6) == 6 && (pv->reg[1] & 0x18))
+ lines_vis = (pv->reg[1] & 0x08) ? 240 : 224;
PicoFrameStartSMS();
- for (y = 0; y < lines_vis; y++)
+ for (y = 0; y < lines_vis; y++) {
+ PicoParseSATSMS(y-1);
PicoLineSMS(y);
+ }
}
// vim:ts=2:sw=2:expandtab