static int screen_offset, line_offset;
+/* 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;
+ // create a pixel bitmap of the sprite pixels from the 4 bitplanes in pack
+ pack = ((pack | (pack>>16)) | ((pack | (pack>>16))>>8)) & 0xff;
+ if (zoomed) pack = morton[pack&0x0f] | (morton[(pack>>4)&0x0f] << 8);
+ // get the corresponding data from the sprite map
+ m = mp[0] | (mp[1]<<8);
+ if (zoomed) m |= (mp[2]<<16);
+ // collision if bits in pixel bitmap overlap bits in sprite map
+ col = m & (pack<<(sx&7));
+ // update sprite map data with our pixel bitmap
+ m |= pack<<(sx&7);
+ mp[0] = m, mp[1] = m>>8;
+ if (zoomed) mp[2] = m>>16;
+ // invisible overscan area, not tested for collision
+ mb[0] = mb[33] = 0;
+ return col;
+}
+
/* Mode 4 */
/*========*/
static void DrawSpritesM4(int scanline)
{
struct PicoVideo *pv = &Pico.video;
+ unsigned char mb[256/8+2] = {0};
unsigned int sprites_addr[8];
unsigned int sprites_x[8];
unsigned int pack;
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;
+ int i, s, h, m;
if (pv->reg[0] & 8)
xoff = 0;
}
}
- // really half-assed but better than nothing
- if (s > 1)
- pv->status |= SR_C;
-
// 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);
+ 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
static void DrawSpritesM2(int scanline)
{
struct PicoVideo *pv = &Pico.video;
+ unsigned char mb[256/8+2] = {0};
unsigned int sprites_addr[4];
unsigned int sprites_x[4];
unsigned int pack;
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;
+ int i, s, h, m;
xoff += line_offset;
s++;
}
- // really half-assed but better than nothing
- if (s > 1)
- pv->status |= SR_C;
-
// now draw all sprites backwards
+ m = 0;
for (--s; s >= 0; s--) {
int x, c, w = (zoomed ? 16: 8);
i = sprites_x[s];
pack = PicoMem.vramb[MEM_LE2(sprites_addr[s])];
if (zoomed) TileDoubleSprM2(x, pack, c);
else TileNormSprM2(x, pack, c);
+ if (!m) m = CollisionDetect(mb, x, pack, zoomed);
}
if((pv->reg[1] & 0x2) && (x+=w) > 0) {
pack = PicoMem.vramb[MEM_LE2(sprites_addr[s]+0x10)];
if (zoomed) TileDoubleSprM2(x, pack, c);
else TileNormSprM2(x, pack, c);
+ if (!m) m = CollisionDetect(mb, x, pack, zoomed);
}
}
+ if (m)
+ pv->status |= SR_C;
}
/* Draw the background into a scanline; cells, dx, tilex, ty merged to reduce registers */
static unsigned short ymflag = 0xffff;
static u8 vdp_buffer;
+static u8 vdp_hlatch;
static unsigned char vdp_data_read(void)
{
elprintf(EL_HVCNT, "V counter read: %02x", d);
break;
- case 0x41: /* H counter, TODO: latched by toggle of pad TH line */
- // 171 slots per scanline of 228 clocks, runs from 0x85-0x93,0xe9-0x84
-#define CYC2SLOT (256 * 171/228) // cycles to slot factor in Q8
- d = 228-z80_cyclesLeft;
- if (d <= 19) d = (( d * CYC2SLOT)>>8) + 0x85;
- else d = (((d-20) * CYC2SLOT)>>8) + 0xe9;
+ case 0x41: /* H counter */
+ d = vdp_hlatch;
elprintf(EL_HVCNT, "H counter read: %02x", d);
break;
switch (a)
{
case 0x01:
+ // latch hcounter if one of the TH lines is switched to 1
+ if ((Pico.ms.io_ctl ^ d) & d & 0xa0) {
+ unsigned c = 228-z80_cyclesLeft;
+ // 171 slots per scanline of 228 clocks, runs from 0xf4-0x93,0xe9-0xf3
+ // this matches h counter tables in SMSVDPTest
+ c = (((c+2) * ((171<<8)/228))>>8)-1 + 0xf4; // Q8 to avoid dividing
+ if (c > 0x193) c += 0xe9-0x93-1;
+ vdp_hlatch = (u8)c;
+ }
Pico.ms.io_ctl = d;
break;