sms vdp, separate SAT parsing
authorkub <derkub@gmail.com>
Sat, 6 Nov 2021 20:00:55 +0000 (21:00 +0100)
committerkub <derkub@gmail.com>
Sat, 6 Nov 2021 20:00:55 +0000 (21:00 +0100)
pico/mode4.c
pico/pico_int.h
pico/sms.c

index 5fda0e7..3827875 100644 (file)
@@ -20,6 +20,14 @@ static int skip_next_line;
 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)
 {
@@ -139,17 +147,14 @@ static void TileDoubleSprM4(int sx, unsigned int pack, int pal)
   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)
@@ -157,7 +162,6 @@ static void DrawSpritesM4(int scanline)
   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) {
@@ -168,6 +172,8 @@ static void DrawSpritesM4(int scanline)
   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;
@@ -180,8 +186,8 @@ static void DrawSpritesM4(int scanline)
     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;
     }
 
@@ -189,22 +195,34 @@ static void DrawSpritesM4(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) >> 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
@@ -295,7 +313,7 @@ static void DrawDisplayM4(int scanline)
 
   // 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
@@ -376,13 +394,9 @@ static void TileDoubleSprTMS(u16 sx, unsigned int pack, int pal)
   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)
@@ -391,7 +405,6 @@ static void DrawSpritesTMS(int scanline)
   int i, s, h, m;
 
   xoff += line_offset;
-  scanline --;
 
   sat = (u8 *)PicoMem.vramb + ((pv->reg[5] & 0x7e) << 7);
   if (pv->reg[1] & 2) {
@@ -400,13 +413,14 @@ static void DrawSpritesTMS(int scanline)
     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;
@@ -416,42 +430,62 @@ static void DrawSpritesTMS(int scanline)
     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 */
@@ -501,7 +535,7 @@ static void DrawDisplayM2(int scanline)
 
   // sprites
   if (!(pv->debug_p & PVD_KILL_S_LO))
-    DrawSpritesTMS(scanline);
+    DrawSpritesTMS();
 }
 
 /* Mode 0 */
@@ -550,7 +584,7 @@ static void DrawDisplayM0(int scanline)
 
   // sprites
   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);
     rendstatus_old = Pico.est.rendstatus;
     rendlines = lines;
+    sprites = 0;
   }
 
   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)
 {
   int skip = skip_next_line;
index e6da518..a199029 100644 (file)
@@ -704,6 +704,7 @@ PICO_INTERNAL void PicoFrameFull();
 \r
 // mode4.c\r
 void PicoFrameStartSMS(void);\r
+void PicoParseSATSMS(int line);\r
 void PicoLineSMS(int line);\r
 void PicoDoHighPal555SMS(void);\r
 void PicoDrawSetOutputSMS(pdso_t which);\r
index a65fe41..fd29fa3 100644 (file)
@@ -20,6 +20,7 @@
 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)
 {
@@ -583,7 +584,7 @@ void PicoMemSetupMS(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);
@@ -682,9 +683,15 @@ void PicoFrameMS(void)
   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;
@@ -693,14 +700,24 @@ void PicoFrameMS(void)
     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)
@@ -713,11 +730,9 @@ void PicoFrameMS(void)
           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);
 
@@ -736,13 +751,18 @@ void PicoFrameMS(void)
 
 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