sms, improve vdp (sprite collision, hcounter latch)
authorkub <derkub@gmail.com>
Mon, 18 Oct 2021 21:05:58 +0000 (23:05 +0200)
committerkub <derkub@gmail.com>
Mon, 18 Oct 2021 21:07:50 +0000 (23:07 +0200)
pico/mode4.c
pico/sms.c

index 9d9235a..9051cb3 100644 (file)
@@ -20,6 +20,30 @@ static int skip_next_line;
 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 */
 /*========*/
 
@@ -115,6 +139,7 @@ static void TileDoubleSprM4(int sx, unsigned int pack, int pal)
 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;
@@ -122,7 +147,7 @@ static void DrawSpritesM4(int scanline)
   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;
@@ -160,16 +185,16 @@ static void DrawSpritesM4(int scanline)
     }
   }
 
-  // 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
@@ -345,6 +370,7 @@ static void TileDoubleSprM2(u16 sx, unsigned int pack, int pal)
 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;
@@ -352,7 +378,7 @@ static void DrawSpritesM2(int scanline)
   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;
 
@@ -388,11 +414,8 @@ static void DrawSpritesM2(int scanline)
     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];
@@ -404,13 +427,17 @@ static void DrawSpritesM2(int scanline)
       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 */
index 7940f5f..5cb1e03 100644 (file)
@@ -22,6 +22,7 @@ extern void YM2413_dataWrite(unsigned data);
 
 static unsigned short ymflag = 0xffff;
 static u8 vdp_buffer;
+static u8 vdp_hlatch;
 
 static unsigned char vdp_data_read(void)
 {
@@ -156,12 +157,8 @@ static unsigned char z80_sms_in(unsigned short a)
         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;
 
@@ -218,6 +215,15 @@ static void z80_sms_out(unsigned short a, unsigned char d)
     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;