vdp fifo: kludge for DMA fill interrupted by CPU
authorkub <derkub@gmail.com>
Sat, 8 Feb 2020 14:20:05 +0000 (15:20 +0100)
committerkub <derkub@gmail.com>
Sat, 8 Feb 2020 22:42:34 +0000 (23:42 +0100)
pico/videoport.c

index 881a74a..0a6a103 100644 (file)
@@ -26,7 +26,7 @@ int (*PicoDmaHook)(unsigned int source, int len, unsigned short **base, unsigned
  * fifo_cnt: #slots remaining for active FIFO write (#writes<<#bytep)\r
  * fifo_total: #total FIFO entries pending\r
  * fifo_data: last values transferred through fifo\r
- * fifo_queue: fifo transfer queue (#writes, VRAM_byte_p)\r
+ * fifo_queue: fifo transfer queue (#writes, flags)\r
  *\r
  * FIFO states:                empty   total=0\r
  *                     inuse   total>0 && total<4\r
@@ -95,42 +95,66 @@ const unsigned char vdpsl2cyc_40[] = { // slot # to 68k cycles/4 since HINT
 // last transferred FIFO data, ...x = index  XXX currently only CPU\r
 static short fifo_data[4], fifo_dx;\r
 // queued FIFO transfers, ...x = index, ...l = queue length\r
-// each entry has 2 values: [n]>>1=#writes, [n]&1=is VRAM byte access\r
+// each entry has 2 values: [n]>>2=#writes, [n]&3=flags:2=DMA fill 1=byte access\r
 static int fifo_queue[8], fifo_qx, fifo_ql;\r
 \r
 signed int fifo_cnt;            // pending slots for current queue entry\r
 unsigned short fifo_slot;       // last executed slot in current scanline\r
 unsigned int fifo_total;        // total# of pending FIFO entries\r
 \r
-// sync FIFO to cycles\r
-void PicoVideoFIFOSync(int cycles)\r
+// do the FIFO math\r
+static __inline int AdvanceFIFOEntry(int slots)\r
+{\r
+  int l = slots, b = fifo_queue[fifo_qx&7] & 1;\r
+\r
+  if (l > fifo_cnt)\r
+    l = fifo_cnt;\r
+  fifo_total -= ((fifo_cnt & b) + l) >> b;\r
+  fifo_cnt -= l;\r
+\r
+  if (fifo_cnt == 0) {\r
+    fifo_qx ++, fifo_ql --;\r
+    fifo_cnt= (fifo_queue[fifo_qx&7] >> 2) << (fifo_queue[fifo_qx&7] & 1);\r
+  }\r
+  return l;\r
+}\r
+\r
+static __inline int GetFIFOSlot(struct PicoVideo *pv, int cycles)\r
 {\r
-  struct PicoVideo *pv = &Pico.video;\r
   int active = !(pv->status & SR_VB) && (pv->reg[1] & 0x40);\r
   int h40 = pv->reg[12] & 1;\r
   const unsigned char *cs = h40 ? vdpcyc2sl_40 : vdpcyc2sl_32;\r
+\r
+  if (active)  return cs[cycles/4];\r
+  else         return (cycles * vdpcyc2sl_bl[h40] + cycles) >> 16;\r
+}\r
+\r
+static inline int GetFIFOCycles(struct PicoVideo *pv, int slot)\r
+{\r
+  int active = !(pv->status & SR_VB) && (pv->reg[1] & 0x40);\r
+  int h40 = pv->reg[12] & 1;\r
+  const unsigned char *sc = h40 ? vdpsl2cyc_40 : vdpsl2cyc_32;\r
+\r
+  if (active)  return sc[slot]*4;\r
+  else         return ((slot * vdpsl2cyc_bl[h40] + slot) >> 16);\r
+}\r
+\r
+// sync FIFO to cycles\r
+void PicoVideoFIFOSync(int cycles)\r
+{\r
+  struct PicoVideo *pv = &Pico.video;\r
   int slots, done;\r
 \r
   // calculate #slots since last executed slot\r
-  if (active)  slots = cs[cycles/4];\r
-  else         slots = (cycles * vdpcyc2sl_bl[h40] + cycles) >> 16;\r
+  slots = GetFIFOSlot(pv, cycles);\r
   slots -= fifo_slot;\r
 \r
   // advance FIFO queue by #done slots\r
   done = slots;\r
   while (done > 0 && fifo_ql) {\r
-    int l = done, b = fifo_queue[fifo_qx&7] & 1;\r
-    if (l > fifo_cnt)\r
-      l = fifo_cnt;\r
-    fifo_total -= ((fifo_cnt & b) + l) >> b;\r
+    int l = AdvanceFIFOEntry(done);\r
     fifo_slot += l;\r
-    fifo_cnt -= l;\r
     done -= l;\r
-\r
-    if (fifo_cnt == 0) {\r
-      fifo_qx ++, fifo_ql --;\r
-      fifo_cnt= (fifo_queue[fifo_qx&7] >> 1) << (fifo_queue[fifo_qx&7] & 1);\r
-    }\r
   }\r
 \r
   // release CPU and terminate DMA if FIFO isn't blocking the 68k anymore\r
@@ -150,7 +174,6 @@ int PicoVideoFIFODrain(int level, int cycles)
   struct PicoVideo *pv = &Pico.video;\r
   int active = !(pv->status & SR_VB) && (pv->reg[1] & 0x40);\r
   int h40 = pv->reg[12] & 1;\r
-  const unsigned char *sc = h40 ? vdpsl2cyc_40 : vdpsl2cyc_32;\r
   int maxsl = vdpslots[h40 + 2*active]; // max xfer slots in this scanline\r
   int burn = 0;\r
 \r
@@ -169,19 +192,11 @@ int PicoVideoFIFODrain(int level, int cycles)
     } else {\r
       // advance FIFO to target slot and CPU to cycles at that slot\r
       fifo_slot = slot;\r
-      if (active)      cycles = sc[slot]*4;\r
-      else             cycles = ((slot * vdpsl2cyc_bl[h40] + slot) >> 16);\r
+      cycles = GetFIFOCycles(pv, slot);\r
     }\r
     burn += cycles - ocyc;\r
 \r
-    slot -= last;\r
-    fifo_total -= ((fifo_cnt & b) + slot) >> b;\r
-    fifo_cnt -= slot;\r
-\r
-    if (fifo_cnt == 0) {\r
-      fifo_qx ++, fifo_ql --;\r
-      fifo_cnt= (fifo_queue[fifo_qx&7] >> 1) << (fifo_queue[fifo_qx&7] & 1);\r
-    }\r
+    AdvanceFIFOEntry(slot - last);\r
   }\r
 \r
   // release CPU and terminate DMA if FIFO isn't blocking the bus anymore\r
@@ -201,10 +216,6 @@ int PicoVideoFIFODrain(int level, int cycles)
 int PicoVideoFIFORead(void)\r
 {\r
   struct PicoVideo *pv = &Pico.video;\r
-  int active = !(pv->status & SR_VB) && (pv->reg[1] & 0x40);\r
-  int h40 = pv->reg[12] & 1;\r
-  const unsigned char *cs = h40 ? vdpcyc2sl_40 : vdpcyc2sl_32;\r
-  const unsigned char *sc = h40 ? vdpsl2cyc_40 : vdpsl2cyc_32;\r
   int lc = SekCyclesDone()-Pico.t.m68c_line_start+4;\r
   int burn = 0;\r
 \r
@@ -217,43 +228,33 @@ int PicoVideoFIFORead(void)
     pv->status |= PVS_CPURD; // target slot is in later scanline\r
   else {\r
     // use next VDP access slot for reading, block 68k until then\r
-    if (active) {\r
-          fifo_slot = cs[lc/4] + 1;\r
-          burn += sc[fifo_slot]*4;\r
-    } else {\r
-          fifo_slot = ((lc * vdpcyc2sl_bl[h40] + lc) >> 16) + 1;\r
-          burn += ((fifo_slot * vdpsl2cyc_bl[h40] + fifo_slot) >> 16);\r
-    }\r
-    burn -= lc;\r
+    fifo_slot = GetFIFOSlot(pv, lc) + 1;\r
+    burn += GetFIFOCycles(pv, fifo_slot) - lc;\r
   }\r
 \r
   return burn;\r
 }\r
  \r
 // write VDP data port\r
-int PicoVideoFIFOWrite(int count, int byte_p, unsigned sr_mask,unsigned sr_flags)\r
+int PicoVideoFIFOWrite(int count, int flags, unsigned sr_mask,unsigned sr_flags)\r
 {\r
   struct PicoVideo *pv = &Pico.video;\r
-  int active = !(pv->status & SR_VB) && (pv->reg[1] & 0x40);\r
-  int h40 = pv->reg[12] & 1;\r
-  const unsigned char *cs = h40 ? vdpcyc2sl_40 : vdpcyc2sl_32;\r
   int lc = SekCyclesDone()-Pico.t.m68c_line_start+4;\r
   int burn = 0;\r
 \r
   PicoVideoFIFOSync(lc);\r
   pv->status = (pv->status & ~sr_mask) | sr_flags;\r
 \r
-  if (count) {\r
+  if (count && fifo_ql < 8) {\r
     // update FIFO state if it was empty\r
     if (fifo_total == 0 && count) {\r
-      if (active)      fifo_slot = cs[lc/4];\r
-      else             fifo_slot = (lc * vdpcyc2sl_bl[h40] + lc) >> 16;\r
-      fifo_cnt = count << byte_p;\r
+      fifo_slot = GetFIFOSlot(pv, lc);\r
+      fifo_cnt = count << (flags&1);\r
     }\r
 \r
     // create xfer queue entry\r
     int x = (fifo_qx + fifo_ql) & 7;\r
-    fifo_queue[x] = (count << 1) | byte_p;\r
+    fifo_queue[x] = (count << 2) | flags;\r
     fifo_ql ++;\r
     fifo_total += count;\r
   }\r
@@ -261,6 +262,11 @@ int PicoVideoFIFOWrite(int count, int byte_p, unsigned sr_mask,unsigned sr_flags
   // if CPU is waiting for the bus, advance CPU and FIFO until bus is free\r
   if ((pv->status & (PVS_CPUWR|PVS_DMAFILL)) == PVS_CPUWR)\r
     burn = PicoVideoFIFODrain(4, lc);\r
+  else if (fifo_queue[fifo_qx&7]&2) {\r
+    // if interrupting a DMA fill terminate it\r
+    AdvanceFIFOEntry(fifo_cnt);\r
+    pv->status &= ~PVS_DMAFILL;\r
+  }\r
 \r
   return burn;\r
 }\r
@@ -515,7 +521,7 @@ static void DmaCopy(int len)
   int source;\r
   elprintf(EL_VDPDMA, "DmaCopy len %i [%u]", len, SekCyclesDone());\r
 \r
-  SekCyclesBurnRun(PicoVideoFIFOWrite(len, 1, PVS_CPUWR|PVS_DMAPEND, SR_DMA));\r
+  SekCyclesBurnRun(PicoVideoFIFOWrite(len, 1, PVS_CPUWR | PVS_DMAPEND, SR_DMA));\r
 \r
   source =Pico.video.reg[0x15];\r
   source|=Pico.video.reg[0x16]<<8;\r
@@ -544,7 +550,8 @@ static NOINLINE void DmaFill(int data)
   len = GetDmaLength();\r
   elprintf(EL_VDPDMA, "DmaFill len %i inc %i [%u]", len, inc, SekCyclesDone());\r
 \r
-  SekCyclesBurnRun(PicoVideoFIFOWrite(len, Pico.video.type == 1, PVS_CPUWR|PVS_DMAPEND, SR_DMA));\r
+  SekCyclesBurnRun(PicoVideoFIFOWrite(len, 2|(Pico.video.type == 1),\r
+                                          PVS_CPUWR | PVS_DMAPEND, SR_DMA));\r
 \r
   switch (Pico.video.type)\r
   {\r