From 134092feb7ea612db0a74c547667978aab980595 Mon Sep 17 00:00:00 2001
From: kub <derkub@gmail.com>
Date: Wed, 5 Apr 2023 20:02:47 +0200
Subject: [PATCH] md, implement z80 cycle stealing when accessing 68k bus

this fixes sound issues in Overdrive 2
---
 pico/memory.c   | 10 ++++++++++
 pico/pico_cmn.c | 30 ++++++++++++++++++++++++------
 pico/pico_int.h |  4 +++-
 3 files changed, 37 insertions(+), 7 deletions(-)

diff --git a/pico/memory.c b/pico/memory.c
index 03450191..b268e85e 100644
--- a/pico/memory.c
+++ b/pico/memory.c
@@ -1294,7 +1294,11 @@ static unsigned char z80_md_bank_read(unsigned short a)
   unsigned int addr68k;
   unsigned char ret;
 
+  // account for 68K bus access on both CPUs.
+  // don't use SekCyclesBurn(7) here since the Z80 doesn't run in cycle lock to
+  // the 68K. Count the stolen cycles to be accounted later in the 68k CPU runs
   z80_subCLeft(3);
+  Pico.t.z80_buscycles += 7;
 
   addr68k = Pico.m.z80_bank68k << 15;
   addr68k |= a & 0x7fff;
@@ -1335,6 +1339,12 @@ static void z80_md_bank_write(unsigned int a, unsigned char data)
 {
   unsigned int addr68k;
 
+  // account for 68K bus access on both CPUs.
+  // don't use SekCyclesBurn(7) here since the Z80 doesn't run in cycle lock to
+  // the 68K. Count the stolen cycles to be accounted later in the 68K CPU runs
+  z80_subCLeft(3);
+  Pico.t.z80_buscycles += 7;
+
   addr68k = Pico.m.z80_bank68k << 15;
   addr68k += a & 0x7fff;
 
diff --git a/pico/pico_cmn.c b/pico/pico_cmn.c
index 8cad1dd7..4cffc55f 100644
--- a/pico/pico_cmn.c
+++ b/pico/pico_cmn.c
@@ -41,11 +41,22 @@ static void SekExecM68k(int cyc_do)
 static void SekSyncM68k(void)
 {
   int cyc_do;
+
   pprof_start(m68k);
   pevt_log_m68k_o(EVT_RUN_START);
 
-  while ((cyc_do = Pico.t.m68c_aim - Pico.t.m68c_cnt) > 0)
-    SekExecM68k(cyc_do);
+  while ((cyc_do = Pico.t.m68c_aim - Pico.t.m68c_cnt) > 0) {
+    // the Z80 CPU is stealing some bus cycles from the 68K main CPU when 
+    // accessing the 68K RAM or ROM. Account for these by shortening the time
+    // the 68K CPU runs.
+    int z80_buscyc = Pico.t.z80_buscycles;
+    if (z80_buscyc <= cyc_do)
+      SekExecM68k(cyc_do - z80_buscyc);
+    else
+      z80_buscyc = cyc_do;
+    Pico.t.m68c_cnt += z80_buscyc;
+    Pico.t.z80_buscycles -= z80_buscyc;
+  }
 
   SekTrace(0);
   pevt_log_m68k_o(EVT_RUN_END);
@@ -56,9 +67,7 @@ static __inline void SekRunM68k(int cyc)
 {
   Pico.t.m68c_aim += cyc;
   Pico.t.m68c_cnt += cyc >> 6; // refresh slowdowns
-  cyc = Pico.t.m68c_aim - Pico.t.m68c_cnt;
-  if (cyc <= 0)
-    return;
+
   SekSyncM68k();
 }
 
@@ -90,11 +99,20 @@ static void do_hint(struct PicoVideo *pv)
 static void do_timing_hacks_end(struct PicoVideo *pv)
 {
   PicoVideoFIFOSync(CYCLES_M68K_LINE);
+
+  // need rather tight Z80 sync for emulation of main bus cycle stealing
+  if (Pico.m.z80Run && !Pico.m.z80_reset && (PicoIn.opt&POPT_EN_Z80) && (Pico.m.scanline&1))
+    PicoSyncZ80(Pico.t.m68c_aim);
 }
 
 static void do_timing_hacks_start(struct PicoVideo *pv)
 {
-  SekCyclesBurn(PicoVideoFIFOHint()); // prolong cpu HOLD if necessary
+  int cycles = PicoVideoFIFOHint();
+
+  SekCyclesBurn(cycles); // prolong cpu HOLD if necessary
+  // XXX how to handle Z80 bus cycle stealing during DMA correctly?
+  if ((Pico.t.z80_buscycles -= cycles) < 0)
+    Pico.t.z80_buscycles = 0;
 }
 
 static int PicoFrameHints(void)
diff --git a/pico/pico_int.h b/pico/pico_int.h
index 6b245c33..57b6c544 100644
--- a/pico/pico_int.h
+++ b/pico/pico_int.h
@@ -204,7 +204,8 @@ extern struct DrZ80 drZ80;
 #define z80_cyclesDone() \
   (Pico.t.z80c_aim - z80_cyclesLeft)
 
-#define cycles_68k_to_z80(x) ((x) * 3822 >> 13)
+// one line has 488 68K cycles and 228 Z80 cycles, 228/488*8192=3827
+#define cycles_68k_to_z80(x) ((x) * 3857 >> 13)
 
 // ----------------------- SH2 CPU -----------------------
 
@@ -447,6 +448,7 @@ struct PicoTiming
   unsigned int z80c_aim;
   unsigned int z80c_line_start;
   int z80_scanline;
+  int z80_buscycles;
 
   int timer_a_next_oflow, timer_a_step; // in z80 cycles
   int timer_b_next_oflow, timer_b_step;
-- 
2.39.5