new z80 scheduling method, timers are still wip
[picodrive.git] / Pico / Memory.c
index 0304e27..45d9fdf 100644 (file)
@@ -687,6 +687,168 @@ static void m68k_mem_setup(void)
 #endif // EMU_M68K\r
 \r
 \r
+// -----------------------------------------------------------------\r
+\r
+extern const unsigned short vcounts[];\r
+\r
+static int get_scanline(int is_from_z80)\r
+{\r
+  if (is_from_z80) {\r
+    int cycles = z80_cyclesDone();\r
+    while (cycles - z80_scanline_cycles >= 228)\r
+      z80_scanline++, z80_scanline_cycles += 228;\r
+    return z80_scanline;\r
+  }\r
+\r
+  if (Pico.m.scanline != -1)\r
+    return Pico.m.scanline;\r
+\r
+  return vcounts[SekCyclesDone()>>8];\r
+}\r
+\r
+// ym2612 DAC and timer I/O handlers for z80\r
+int ym2612_write_local(u32 a, u32 d, int is_from_z80)\r
+{\r
+  int addr;\r
+\r
+  a &= 3;\r
+  if (a == 1 && ym2612.OPN.ST.address == 0x2a) /* DAC data */\r
+  {\r
+    int scanline = get_scanline(is_from_z80);\r
+    //elprintf(EL_STATUS, "%03i -> %03i dac w %08x z80 %i", PsndDacLine, scanline, d, is_from_z80);\r
+    ym2612.dacout = ((int)d - 0x80) << 6;\r
+    if (PsndOut && ym2612.dacen && scanline >= PsndDacLine)\r
+      PsndDoDAC(scanline);\r
+    return 0;\r
+  }\r
+\r
+  switch (a)\r
+  {\r
+    case 0: /* address port 0 */\r
+      ym2612.OPN.ST.address = d;\r
+      ym2612.addr_A1 = 0;\r
+#ifdef __GP2X__\r
+      if (PicoOpt & POPT_EXT_FM) YM2612Write_940(a, d, -1);\r
+#endif\r
+      return 0;\r
+\r
+    case 1: /* data port 0    */\r
+      if (ym2612.addr_A1 != 0)\r
+        return 0;\r
+\r
+      addr = ym2612.OPN.ST.address;\r
+      ym2612.REGS[addr] = d;\r
+\r
+      switch (addr)\r
+      {\r
+        case 0x24: // timer A High 8\r
+        case 0x25: { // timer A Low 2\r
+          int TAnew = (addr == 0x24) ? ((ym2612.OPN.ST.TA & 0x03)|(((int)d)<<2))\r
+                                     : ((ym2612.OPN.ST.TA & 0x3fc)|(d&3));\r
+          if (ym2612.OPN.ST.TA != TAnew)\r
+          {\r
+            //elprintf(EL_STATUS, "timer a set %i", TAnew);\r
+            ym2612.OPN.ST.TA = TAnew;\r
+            //ym2612.OPN.ST.TAC = (1024-TAnew)*18;\r
+            //ym2612.OPN.ST.TAT = 0;\r
+            //\r
+            timer_a_step = 16495 * (1024 - TAnew);\r
+            if ((ym2612.OPN.ST.mode & 5) == 5) {\r
+              int cycles = is_from_z80 ? z80_cyclesDone() : cycles_68k_to_z80(SekCyclesDone());\r
+              timer_a_next_oflow = (cycles << 8) + timer_a_step;\r
+              //elprintf(EL_STATUS, "set to %i @ %i", timer_a_next_oflow>>8, cycles);\r
+            }\r
+          }\r
+          return 0;\r
+        }\r
+        case 0x26: // timer B\r
+          if (ym2612.OPN.ST.TB != d) {\r
+            //elprintf(EL_STATUS, "timer b set %i", d);\r
+            ym2612.OPN.ST.TB = d;\r
+            //ym2612.OPN.ST.TBC  = (256-d)<<4;\r
+            //ym2612.OPN.ST.TBC *= 18;\r
+            //ym2612.OPN.ST.TBT  = 0;\r
+          }\r
+          return 0;\r
+        case 0x27: { /* mode, timer control */\r
+          int old_mode = ym2612.OPN.ST.mode;\r
+          int xcycles = is_from_z80 ? z80_cyclesDone() : cycles_68k_to_z80(SekCyclesDone());\r
+          xcycles <<= 8;\r
+\r
+          //elprintf(EL_STATUS, "st mode %02x", d);\r
+\r
+          if ((ym2612.OPN.ST.mode & 5) != 5 && (d & 5) == 5) {\r
+            timer_a_next_oflow = xcycles + timer_a_step;\r
+            //elprintf(EL_STATUS, "set to %i @ %i st", timer_a_next_oflow>>8, xcycles >> 8);\r
+          }\r
+\r
+          /* reset Timer b flag */\r
+          if (d & 0x20)\r
+            ym2612.OPN.ST.status &= ~2;\r
+\r
+          /* reset Timer a flag */\r
+          if (d & 0x10) {\r
+            if (ym2612.OPN.ST.status & 1)\r
+              while (xcycles > timer_a_next_oflow)\r
+                timer_a_next_oflow += timer_a_step;\r
+            ym2612.OPN.ST.status &= ~1;\r
+          }\r
+          if (!(d & 5)) timer_a_next_oflow = 0x80000000;\r
+          ym2612.OPN.ST.mode = d;\r
+#ifdef __GP2X__\r
+          if (PicoOpt & POPT_EXT_FM) YM2612Write_940(a, d, get_scanline(is_from_z80));\r
+#endif\r
+          return 0;\r
+        }\r
+        case 0x2b: { /* DAC Sel  (YM2612) */\r
+          int scanline = get_scanline(is_from_z80);\r
+          ym2612.dacen = d & 0x80;\r
+          if (d & 0x80) PsndDacLine = scanline;\r
+#ifdef __GP2X__\r
+          if (PicoOpt & POPT_EXT_FM) YM2612Write_940(a, d, scanline);\r
+#endif\r
+          return 0;\r
+        }\r
+      }\r
+      break;\r
+\r
+    case 2: /* address port 1 */\r
+      ym2612.OPN.ST.address = d;\r
+      ym2612.addr_A1 = 1;\r
+#ifdef __GP2X__\r
+      if (PicoOpt & POPT_EXT_FM) YM2612Write_940(a, d, -1);\r
+#endif\r
+      return 0;\r
+\r
+    case 3: /* data port 1    */\r
+      if (ym2612.addr_A1 != 1)\r
+        return 0;\r
+\r
+      addr = ym2612.OPN.ST.address | 0x100;\r
+      ym2612.REGS[addr] = d;\r
+      break;\r
+  }\r
+\r
+#ifdef __GP2X__\r
+  if (PicoOpt & POPT_EXT_FM)\r
+    return YM2612Write_940(a, d, get_scanline(is_from_z80));\r
+#endif\r
+  return YM2612Write_(a, d);\r
+}\r
+\r
+// TODO: timer b, 68k side + asm, savestates\r
+u32 ym2612_read_local_z80(void)\r
+{\r
+  int xcycles = z80_cyclesDone() << 8;\r
+  if (timer_a_next_oflow != 0x80000000 && xcycles >= timer_a_next_oflow) {\r
+    ym2612.OPN.ST.status |= 1;\r
+  }\r
+\r
+  //elprintf(EL_STATUS, "timer %i, sched %i, @ %i|%i", ym2612.OPN.ST.status, timer_a_next_oflow>>8,\r
+  //     xcycles >> 8, (xcycles >> 8) / 228);\r
+  return ym2612.OPN.ST.status;\r
+}\r
+\r
 // -----------------------------------------------------------------\r
 //                        z80 memhandlers\r
 \r
@@ -696,7 +858,7 @@ PICO_INTERNAL unsigned char z80_read(unsigned short a)
 \r
   if ((a>>13)==2) // 0x4000-0x5fff (Charles MacDonald)\r
   {\r
-    if (PicoOpt&POPT_EN_FM) ret = (u8) YM2612Read();\r
+    if (PicoOpt&POPT_EN_FM) ret = ym2612_read_local_z80();\r
     return ret;\r
   }\r
 \r
@@ -710,7 +872,10 @@ PICO_INTERNAL unsigned char z80_read(unsigned short a)
     if (PicoAHW & PAHW_MCD)\r
          ret = PicoReadM68k8(addr68k);\r
     else ret = PicoRead8(addr68k);\r
-    elprintf(EL_Z80BNK, "z80->68k r8 [%06x] %02x", addr68k, ret);\r
+    if (addr68k >= 0x400000) // not many games do this\r
+      { elprintf(EL_ANOMALY, "z80->68k upper read [%06x] %02x", addr68k, ret); }\r
+    else\r
+      { elprintf(EL_Z80BNK, "z80->68k r8 [%06x] %02x", addr68k, ret); }\r
     return ret;\r
   }\r
 \r
@@ -729,7 +894,7 @@ PICO_INTERNAL_ASM void z80_write(unsigned int a, unsigned char data)
 {\r
   if ((a>>13)==2) // 0x4000-0x5fff (Charles MacDonald)\r
   {\r
-    if(PicoOpt&POPT_EN_FM) emustatus|=YM2612Write(a, data) & 1;\r
+    if(PicoOpt&POPT_EN_FM) emustatus|=ym2612_write_local(a, data, 1) & 1;\r
     return;\r
   }\r
 \r