core, basic md+ support
authorkub <derkub@gmail.com>
Sat, 21 Sep 2024 20:28:02 +0000 (22:28 +0200)
committerkub <derkub@gmail.com>
Mon, 23 Sep 2024 20:18:20 +0000 (22:18 +0200)
like msu-md. loading a .cue file will look for a cartridge image with
the same basename and an extension of "gen", "smd", "md", "32x".

pico/cd/cdd.c
pico/cd/mcd.c
pico/cd/megasd.c [new file with mode: 0644]
pico/cd/megasd.h [new file with mode: 0644]
pico/cd/memory.c
pico/memory.c
platform/common/common.mak
platform/common/emu.c

index 6f121f4..9079f74 100644 (file)
@@ -155,6 +155,9 @@ static void ogg_free(int i)
 
 void cdd_reset(void)
 {
+  /* stop audio streaming */
+  Pico_mcd->cdda_stream = NULL;
+
   /* reset cycle counter */
   cdd.cycles = 0;
   
@@ -715,13 +718,13 @@ void cdd_read_audio(unsigned int samples)
 
 
 void cdd_update(void)
-{  
+{
 #ifdef LOG_CDD
   error("LBA = %d (track n°%d)(latency=%d)\n", cdd.lba, cdd.index, cdd.latency);
 #endif
   
   /* update decoder, depending on track type */
-  if (cdd.status == CD_PLAY && !is_audio(cdd.index))
+  if (cdd.status == CD_PLAY && !is_audio(cdd.index) && !cdd.latency)
   {
     /* DATA sector header (CD-ROM Mode 1) */
     uint8 header[4];
@@ -1043,12 +1046,12 @@ void cdd_process(void)
         cdd.latency += (((cdd.lba - lba) * 120) / 270000);
       }
 
-      /* block transfer always starts 3 blocks earlier */
-      lba -= 3;
-
       /* get track index */
       while ((cdd.toc.tracks[index].end <= lba) && (index < cdd.toc.last)) index++;
 
+      /* block transfer always starts 3 blocks earlier */
+      lba -= 3;
+
       /* seek to block */
       cdd_seek(index, lba);
 
@@ -1131,7 +1134,7 @@ void cdd_process(void)
 
     case 0x07:  /* Resume */
     {
-      int lba = (cdd.lba < 0 ? 0 : cdd.lba);
+      int lba = (cdd.lba < 4 ? 4 : cdd.lba);
 
       /* CD drive latency */
       if (!cdd.latency)
index 7086a17..368181b 100644 (file)
@@ -9,6 +9,7 @@
 
 #include "../pico_int.h"
 #include "../sound/ym2612.h"
+#include "megasd.h"
 
 extern unsigned char formatted_bram[4*0x10];
 
@@ -96,6 +97,7 @@ PICO_INTERNAL int PicoResetMCD(void)
   }
   Pico.sv.start = Pico.sv.end = 0; // unused
 
+  msd_reset();
   return 0;
 }
 
@@ -151,6 +153,8 @@ static void pcd_cdc_event(unsigned int now)
     Pico_mcd->s68k_regs[0x4b] = 0xf0;
   }
 
+  msd_update();
+
   if ((Pico_mcd->s68k_regs[0x33] & PCDS_IEN4) && (Pico_mcd->s68k_regs[0x37] & 4)) {
     elprintf(EL_INTS|EL_CD, "s68k: cdd irq 4");
     pcd_irq_s68k(4, 1);
diff --git a/pico/cd/megasd.c b/pico/cd/megasd.c
new file mode 100644 (file)
index 0000000..d6324ef
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * PicoDrive
+ * (C) irixxxx, 2024
+ *
+ * MEGASD enhancement support
+ */
+
+#include "../pico_int.h"
+#include "../memory.h"
+
+#include "genplus_macros.h"
+#include "cdd.h"
+#include "megasd.h"
+
+// modifiable fields visible through the interface
+u16 msd_command, msd_result;
+u16 msd_data[0x800/2];
+
+// internal state
+static int msd_initialized;
+static s32 msd_startlba, msd_endlba, msd_looplba;
+static s32 msd_readlba = -1; // >= 0 if sector read is running
+static int msd_loop, msd_index = -1; // >= 0 if audio track is playing
+
+static u16 verser[] = // mimick version 1.04 R7, serial 0x01234567
+    { 0x4d45, 0x4741, 0x5344, 0x0104, 0x0700, 0xffff, 0x0123, 0x4567 };
+//    { 0x4d45, 0x4741, 0x5344, 0x9999, 0x9900, 0xffff, 0x1234, 0x5678 };
+
+// get a 32bit value from the data area
+static s32 get32(int offs)
+{
+  u16 *a = msd_data + (offs/2);
+  return (a[0] << 16) | a[1];
+}
+
+// send commands to cdd
+static void cdd_play(s32 lba)
+{
+  int secs = lba / 75;
+  int mins = secs / 60;
+  lba -= secs * 75;
+  secs -= mins * 60;
+  s68k_write8(0xff8044, mins/10);
+  s68k_write8(0xff8045, mins%10);
+  s68k_write8(0xff8046, secs/10);
+  s68k_write8(0xff8047, secs%10);
+  s68k_write8(0xff8048, lba /10);
+  s68k_write8(0xff8049, lba %10);
+
+  s68k_write8(0xff8042, 0x03);
+  s68k_write8(0xff804b, 0xff);
+}
+
+static void cdd_pause(void)
+{
+  s68k_write8(0xff8042, 0x06);
+  s68k_write8(0xff804b, 0xff);
+}
+
+static void cdd_resume(void)
+{
+  s68k_write8(0xff8042, 0x07);
+  s68k_write8(0xff804b, 0xff);
+}
+
+static void cdd_stop(void)
+{
+  msd_index = msd_readlba = -1;
+  s68k_write8(0xff8042, 0x01);
+  s68k_write8(0xff804b, 0xff);
+}
+
+// play a track, looping from offset if enabled
+static void msd_playtrack(int idx, s32 offs, int loop)
+{
+  msd_index = idx-1;
+  msd_loop = loop;
+  msd_readlba = -1;
+
+  msd_startlba = cdd.toc.tracks[msd_index].start + 150;
+  msd_endlba = cdd.toc.tracks[msd_index].end;
+  msd_looplba = msd_startlba + offs;
+
+  cdd_play(msd_startlba);
+}
+
+// play a range of sectors, with looping if enabled
+static void msd_playsectors(s32 startlba, s32 endlba, s32 looplba, int loop)
+{
+  msd_index = 99;
+  msd_loop = loop;
+  msd_readlba = -1;
+
+  msd_startlba = startlba + 150;
+  msd_endlba = endlba + 150;
+  msd_looplba = looplba + 150;
+
+  cdd_play(msd_startlba);
+}
+
+// read a block of data
+static void msd_readdata(s32 lba)
+{
+  msd_index = -1;
+  msd_readlba = lba;
+
+  cdd_play(msd_readlba);
+}
+
+// transfer data to data area
+static void msd_transfer()
+{
+  if (cdd.status == CD_PLAY)
+    cdd_read_data((u8 *)msd_data);
+}
+
+// update msd state (called every 1/75s)
+void msd_update()
+{
+  if (msd_initialized) {
+    // CD LEDs
+    s68k_write8(0xff8000,(cdd.status == CD_PLAY) | 0x2);
+
+    if (cdd.status == CD_PLAY) {
+      if (msd_readlba >= 0 && cdd.lba >= msd_readlba) {
+        // read done
+        msd_command = 0;
+      }
+      else if (msd_index >= 0) {
+        msd_command = 0;
+        if (cdd.lba > msd_endlba || cdd.index > msd_index) {
+          if (!msd_loop || msd_index < 0) {
+            cdd_stop();
+            // audio done
+          } else
+            cdd_play(msd_looplba);
+        }
+      }
+    }
+  }
+}
+
+// process a MEGASD command
+void msd_process(u16 d)
+{
+  msd_command = d; // busy
+
+  switch (d >> 8) {
+  case 0x10: memcpy(msd_data, verser, sizeof(verser)); msd_command = 0; break;
+
+  case 0x11: msd_playtrack(d&0xff, 0, 0); break;
+  case 0x12: msd_playtrack(d&0xff, 0, 1); break;
+  case 0x1a: msd_playtrack(d&0xff, get32(0), 1); break;
+  case 0x1b: msd_playsectors(get32(0), get32(4), get32(8), d&0xff); break;
+
+  case 0x13: cdd_pause();
+             msd_command = 0; break;
+  case 0x14: cdd_resume();
+             msd_command = 0; break;
+
+  case 0x16: msd_result = !(s68k_read8(0xff8036) & 0x1) << 8;
+             msd_command = 0; break;
+
+  case 0x17: msd_readdata(get32(0)); break;
+  case 0x18: msd_transfer();
+             msd_command = 0; break;
+  case 0x19: msd_readdata(++msd_readlba); break;
+
+  case 0x27: msd_result = cdd.toc.last << 8;
+             msd_command = 0; break;
+
+  default:   msd_command = msd_result = 0; break; // not supported
+  }
+}
+
+// initialize MEGASD
+static void msd_init(void)
+{
+  if (!msd_initialized) {
+    msd_initialized = 1;
+
+    // enable CD drive
+    s68k_write8(0xff8037, 0x4);
+
+    PicoResetHook = msd_reset;
+  }
+}
+
+void msd_reset(void)
+{
+  if (msd_initialized) {
+    msd_initialized = msd_command = 0;
+    cdd_stop();
+
+    s68k_write8(0xff8000, 0x0);
+    s68k_write8(0xff8001, 0x0);
+
+    PicoResetHook = NULL;
+  }
+}
+
+// memory r/w functions
+static u32 msd_read16(u32 a)
+{
+  u16 d = 0;
+
+  if (a >= 0x03f800) {
+    d = msd_data[(a&0x7ff)>>1];
+  } else if (a >= 0x03f7f0) {
+    switch (a&0xe) {
+      case 0x6: d = 0x5241; break; // RA
+      case 0x8: d = 0x5445; break; // TE
+      case 0xc: d = msd_result; break;
+      case 0xe: d = msd_command; break;
+    }
+  } else if (a < Pico.romsize)
+    d = *(u16 *)(Pico.rom + a);
+
+  return d;
+}
+
+static u32 msd_read8(u32 a)
+{
+  u16 d = msd_read16(a);
+
+  if (!(a&1)) d >>= 8;
+  return d;
+}
+
+void msd_write16(u32 a, u32 d)
+{
+  if (a == 0x03f7fa) {
+    // en/disable overlay
+    u32 base = 0x040000-(1<<M68K_MEM_SHIFT);
+    if ((u16)d == 0xcd54) {
+      msd_init();
+      cpu68k_map_set(m68k_read8_map,  base, 0x03ffff, msd_read8, 1);
+      cpu68k_map_set(m68k_read16_map, base, 0x03ffff, msd_read16, 1);
+    } else if (Pico.romsize > base) {
+      cpu68k_map_set(m68k_read8_map,  base, 0x03ffff, Pico.rom+base, 0);
+      cpu68k_map_set(m68k_read16_map, base, 0x03ffff, Pico.rom+base, 0);
+    }
+  } else if (a == 0x03f7fe) {
+    // command port
+    msd_process(d);
+  } else if (a >= 0x03f800) {
+    // data area
+    msd_data[(a&0x7ff) >> 1] = d;
+  }
+}
+
+void msd_write8(u32 a, u32 d)
+{
+  if (a >= 0x03f800) {
+    // data area
+    ((u8 *)msd_data)[MEM_BE2(a&0x7ff)] = d;
+  }
+}
diff --git a/pico/cd/megasd.h b/pico/cd/megasd.h
new file mode 100644 (file)
index 0000000..49a753e
--- /dev/null
@@ -0,0 +1,11 @@
+
+
+extern u16 msd_command, msd_result;
+extern u16 msd_data[0x800/2];
+
+extern void msd_update(void);
+extern void msd_process(u16 d);
+extern void msd_reset(void);
+
+extern void msd_write8(u32 a, u32 d);
+extern void msd_write16(u32 a, u32 d);
index 461dc0d..ae53126 100644 (file)
@@ -9,6 +9,7 @@
 \r
 #include "../pico_int.h"\r
 #include "../memory.h"\r
+#include "megasd.h"\r
 \r
 uptr s68k_read8_map  [0x1000000 >> M68K_MEM_SHIFT];\r
 uptr s68k_read16_map [0x1000000 >> M68K_MEM_SHIFT];\r
@@ -1244,6 +1245,10 @@ PICO_INTERNAL void PicoMemSetupCD(void)
     // MSU cartridge. Fake BIOS detection\r
     cpu68k_map_set(m68k_read8_map,   0x400000, 0x41ffff, PicoReadM68k8_bios, 1);\r
     cpu68k_map_set(m68k_read16_map,  0x400000, 0x41ffff, PicoReadM68k16_bios, 1);\r
+    // MD+ on MEGASD.\r
+    cpu68k_map_set(m68k_write8_map,  0x040000-(1<<M68K_MEM_SHIFT), 0x03ffff, msd_write8, 1);\r
+    cpu68k_map_set(m68k_write16_map, 0x040000-(1<<M68K_MEM_SHIFT), 0x03ffff, msd_write16, 1);\r
+    msd_reset();\r
   } else {\r
     // RAM cart\r
     cpu68k_map_set(m68k_read8_map,   0x400000, 0x7fffff, PicoReadM68k8_ramc, 1);\r
index 91bb2a8..3b5bdf8 100644 (file)
@@ -421,8 +421,6 @@ static NOINLINE u32 port_read(int i)
   // Decap Attack reportedly doesn't work on Nomad but works on must\r
   // other MD revisions (different pull-up strength?).\r
   u32 mask = 0x3f;\r
-  if (CYCLES_GE(padTHLatency[i], SekCyclesDone()+100))\r
-    padTHLatency[i] = SekCyclesDone(); // kludge to cope with cycle wrap\r
   if (CYCLES_GE(SekCyclesDone(), padTHLatency[i])) {\r
     mask |= 0x40;\r
     padTHLatency[i] = SekCyclesDone();\r
index 68ca6ff..f3d547d 100644 (file)
@@ -100,7 +100,7 @@ endif
 SRCS_COMMON += $(R)pico/cd/mcd.c $(R)pico/cd/memory.c $(R)pico/cd/sek.c \
        $(R)pico/cd/cdc.c $(R)pico/cd/cdd.c $(R)pico/cd/cd_image.c \
        $(R)pico/cd/cd_parse.c $(R)pico/cd/gfx.c $(R)pico/cd/gfx_dma.c \
-       $(R)pico/cd/misc.c $(R)pico/cd/pcm.c
+       $(R)pico/cd/misc.c $(R)pico/cd/pcm.c $(R)pico/cd/megasd.c
 # 32X
 ifneq "$(no_32x)" "1"
 SRCS_COMMON += $(R)pico/32x/32x.c $(R)pico/32x/memory.c $(R)pico/32x/draw.c \
index 051f8b4..403c4c4 100644 (file)
@@ -184,7 +184,7 @@ static const char *find_bios(int *region, const char *cd_fname)
                        (*region == 8 ? "EU" : "JAP") : "USA");\r
        }\r
 \r
-       // look for MSU.MD rom file. XXX another extension list? ugh...\r
+       // look for MSU.MD or MD+ rom file. XXX another extension list? ugh...\r
        static const char *md_exts[] = { "gen", "smd", "md", "32x" };\r
        char *ext = strrchr(cd_fname, '.');\r
        int extpos = ext ? ext-cd_fname : strlen(cd_fname);\r