Skip to content

Commit

Permalink
core, add save/load for md+
Browse files Browse the repository at this point in the history
  • Loading branch information
irixxxx committed Sep 25, 2024
1 parent dd7882a commit d600fee
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 71 deletions.
124 changes: 59 additions & 65 deletions pico/cd/megasd.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
* MEGASD enhancement support as "documented" in "MegaSD DEV Manual Rev.2"
*
* Emulates parts of the MEGASD API for "CD enhanced Megadrive games". Missing:
* - all commands directly accessing files on the MEGASD storage
* - Fader and volume control
* - enhanced SSF2 mapper
* - PCM access
* - enhanced SSF2 mapper (currently uses standard SSF2 mapper instead)
* - PCM access (only possible through enhanced SSF2 mapper)
* The missing features are AFAIK not used by any currently available patch.
* I'm not going to look into these until I see it used somewhere.
*/
Expand All @@ -21,15 +22,7 @@

#define CDD_PLAY_OFFSET 3 // CDD starts this many sectors early

// 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
struct megasd Pico_msd; // MEGASD state

static u16 verser[] = // mimick version 1.04 R7, serial 0x12345678
{ 0x4d45, 0x4741, 0x5344, 0x0104, 0x0700, 0xffff, 0x1234, 0x5678 };
Expand All @@ -38,7 +31,7 @@ static u16 verser[] = // mimick version 1.04 R7, serial 0x12345678
// get a 32bit value from the data area
static s32 get32(int offs)
{
u16 *a = msd_data + (offs/2);
u16 *a = Pico_msd.data + (offs/2);
return (a[0] << 16) | a[1];
}

Expand Down Expand Up @@ -75,7 +68,7 @@ static void cdd_resume(void)

static void cdd_stop(void)
{
msd_index = msd_readlba = -1;
Pico_msd.index = Pico_msd.readlba = -1;
s68k_write8(0xff8042, 0x01);
s68k_write8(0xff804b, 0xff);
}
Expand All @@ -86,91 +79,91 @@ static void msd_playtrack(int idx, s32 offs, int loop)
track_t *track;

if (idx < 1 || idx > cdd.toc.last) {
msd_result = msd_command = 0;
Pico_msd.result = Pico_msd.command = 0;
return;
}
msd_index = idx-1;
Pico_msd.index = idx-1;

track = &cdd.toc.tracks[msd_index];
track = &cdd.toc.tracks[Pico_msd.index];
if (track->loop) {
// overridden by some bizarre proprietary extensions in the cue file
// NB using these extensions definitely prevents using CHD files with MD+!
loop = track->loop > 0;
offs = track->loop_lba;
}

msd_loop = loop;
msd_readlba = -1;
Pico_msd.loop = loop;
Pico_msd.readlba = -1;

msd_startlba = track->start + 150;
msd_endlba = track->end + 150;
msd_looplba = msd_startlba + offs;
Pico_msd.startlba = track->start + 150;
Pico_msd.endlba = track->end + 150;
Pico_msd.looplba = Pico_msd.startlba + offs;

cdd_play(msd_startlba);
cdd_play(Pico_msd.startlba);
}

// play a range of sectors, with looping if enabled
static void msd_playsectors(s32 startlba, s32 endlba, s32 looplba, int loop)
{
if (startlba < 0 || startlba >= cdd.toc.tracks[cdd.toc.last].start) {
msd_result = msd_command = 0;
Pico_msd.result = Pico_msd.command = 0;
return;
}

msd_index = 99;
msd_loop = loop;
msd_readlba = -1;
Pico_msd.index = 99;
Pico_msd.loop = loop;
Pico_msd.readlba = -1;

msd_startlba = startlba + 150;
msd_endlba = endlba + 150;
msd_looplba = looplba + 150;
Pico_msd.startlba = startlba + 150;
Pico_msd.endlba = endlba + 150;
Pico_msd.looplba = looplba + 150;

cdd_play(msd_startlba);
cdd_play(Pico_msd.startlba);
}

// read a block of data
static void msd_readdata(s32 lba)
{
if (lba < 0 || lba >= cdd.toc.tracks[cdd.toc.last].start) {
msd_result = msd_command = 0;
Pico_msd.result = Pico_msd.command = 0;
return;
}

msd_index = -1;
msd_readlba = lba + 150;
Pico_msd.index = -1;
Pico_msd.readlba = lba + 150;

cdd_play(msd_readlba);
cdd_play(Pico_msd.readlba);
}

// transfer data to data area
static void msd_transfer()
{
if (cdd.status == CD_PLAY)
cdd_read_data((u8 *)msd_data);
cdd_read_data((u8 *)Pico_msd.data);
}

// update msd state (called every 1/75s)
// update msd state (called every 1/75s, like CDD irq)
void msd_update()
{
if (msd_initialized) {
if (Pico_msd.initialized) {
// CD LEDs
s68k_write8(0xff8000,(cdd.status == CD_PLAY) | 0x2);

cdd.latency = 0; // MEGASD has no latency in this mode

if (cdd.status == CD_PLAY) {
if (msd_readlba >= 0 && cdd.lba >= msd_readlba) {
if (Pico_msd.readlba >= 0 && cdd.lba >= Pico_msd.readlba) {
// read done
msd_command = 0;
Pico_msd.command = 0;
}
else if (msd_index >= 0) {
msd_command = 0;
if (cdd.lba >= msd_endlba-1 || cdd.index > msd_index) {
if (!msd_loop || msd_index < 0) {
else if (Pico_msd.index >= 0) {
Pico_msd.command = 0;
if (cdd.lba >= Pico_msd.endlba-1 || cdd.index > Pico_msd.index) {
if (!Pico_msd.loop || Pico_msd.index < 0) {
cdd_stop();
// audio done
} else
cdd_play(msd_looplba - CDD_PLAY_OFFSET);
cdd_play(Pico_msd.looplba - CDD_PLAY_OFFSET);
}
}
}
Expand All @@ -180,42 +173,43 @@ void msd_update()
// process a MEGASD command
void msd_process(u16 d)
{
msd_command = d; // busy
Pico_msd.command = d; // busy

switch (d >> 8) {
case 0x10: memcpy(msd_data, verser, sizeof(verser)); msd_command = 0; break;
case 0x10: memcpy(Pico_msd.data, verser, sizeof(verser)); Pico_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;
Pico_msd.command = 0; break;
case 0x14: cdd_resume();
msd_command = 0; break;
Pico_msd.command = 0; break;

case 0x16: msd_result = !(s68k_read8(0xff8036) & 0x1) << 8;
msd_command = 0; break;
case 0x16: Pico_msd.result = !(s68k_read8(0xff8036) & 0x1) << 8;
Pico_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-150 + 1); break;
Pico_msd.command = 0; break;
case 0x19: msd_readdata(Pico_msd.readlba-150 + 1); break;

case 0x27: msd_result = cdd.toc.last << 8;
msd_command = 0; break;
case 0x27: Pico_msd.result = cdd.toc.last << 8;
Pico_msd.command = 0; break;

default: elprintf(EL_ANOMALY, "unknown MEGASD command %02x", msd_command);
msd_command = msd_result = 0; break; // not supported
default: elprintf(EL_ANOMALY, "unknown MEGASD command %02x", Pico_msd.command);
Pico_msd.command = Pico_msd.result = 0; break; // not supported
}
}

// initialize MEGASD
static void msd_init(void)
{
if (!msd_initialized) {
msd_initialized = 1;
if (!Pico_msd.initialized) {
Pico_msd.initialized = 1;
Pico_msd.index = Pico_msd.readlba = -1;

// enable CD drive
s68k_write8(0xff8037, 0x4);
Expand All @@ -226,8 +220,8 @@ static void msd_init(void)

void msd_reset(void)
{
if (msd_initialized) {
msd_initialized = msd_command = 0;
if (Pico_msd.initialized) {
Pico_msd.initialized = Pico_msd.command = 0;
cdd_stop();

s68k_write8(0xff8000, 0x0);
Expand All @@ -244,13 +238,13 @@ static u32 msd_read16(u32 a)

a = (u16)a;
if (a >= 0x0f800) {
d = msd_data[(a&0x7ff)>>1];
d = Pico_msd.data[(a&0x7ff)>>1];
} else if (a >= 0xf7f0) {
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;
case 0xc: d = Pico_msd.result; break;
case 0xe: d = Pico_msd.command; break;
}
} else if (a < Pico.romsize)
d = *(u16 *)(Pico.rom + a);
Expand Down Expand Up @@ -288,14 +282,14 @@ void msd_write16(u32 a, u32 d)
msd_process(d);
} else if (a >= 0xf800) {
// data area
msd_data[(a&0x7ff) >> 1] = d;
Pico_msd.data[(a&0x7ff) >> 1] = d;
}
}

void msd_write8(u32 a, u32 d)
{
if ((u16)a >= 0xf800) {
// data area
((u8 *)msd_data)[MEM_BE2(a&0x7ff)] = d;
((u8 *)Pico_msd.data)[MEM_BE2(a&0x7ff)] = d;
}
}
34 changes: 28 additions & 6 deletions pico/cd/megasd.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,33 @@
/*
* PicoDrive
* (C) irixxxx, 2024
*
* MEGASD enhancement support as "documented" in "MegaSD DEV Manual Rev.2"
*/

struct megasd {
// modifiable fields visible through the interface
u16 data[0x800/2];
u16 command;
u16 result;

extern u16 msd_command, msd_result;
extern u16 msd_data[0x800/2];
// internal state
s8 initialized; // CD drive has been initialized

extern void msd_update(void);
extern void msd_process(u16 d);
extern void msd_reset(void);
s8 loop; // playback should loop?
s16 index; // >= 0 if playing audio
s32 startlba, endlba, looplba; // lba's for playback

extern void msd_write8(u32 a, u32 d);
s32 readlba; // >= 0 if reading data

s32 pad[8];
};

extern struct megasd Pico_msd;


extern void msd_update(void); // 75Hz update, like CDD irq
extern void msd_reset(void); // reset state

extern void msd_write8(u32 a, u32 d); // interface
extern void msd_write16(u32 a, u32 d);
5 changes: 5 additions & 0 deletions pico/state.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <cpu/sh2/sh2.h>
#include "sound/ym2612.h"
#include "sound/emu2413/emu2413.h"
#include "cd/megasd.h"
#include "state.h"

// sn76496 & ym2413
Expand Down Expand Up @@ -135,6 +136,7 @@ typedef enum {
CHUNK_YM2413,
CHUNK_PICO_PCM,
CHUNK_PICO,
CHUNK_CD_MSD,
//
CHUNK_DEFAULT_COUNT,
CHUNK_CARTHW_ = CHUNK_CARTHW, // 64 (defined in PicoInt)
Expand Down Expand Up @@ -309,6 +311,8 @@ static int state_save(void *file)
len = cdd_context_save(buf2);
CHECKED_WRITE(CHUNK_CD_CDD, len, buf2);

CHECKED_WRITE_BUFF(CHUNK_CD_MSD, Pico_msd);

if (Pico_mcd->s68k_regs[3] & 4) // convert back
wram_2M_to_1M(Pico_mcd->word_ram2M);
}
Expand Down Expand Up @@ -497,6 +501,7 @@ static int state_load(void *file)
case CHUNK_GA_REGS: CHECKED_READ_BUFF(Pico_mcd->s68k_regs); break;
case CHUNK_PCM: CHECKED_READ_BUFF(Pico_mcd->pcm); break;
case CHUNK_MISC_CD: CHECKED_READ_BUFF(Pico_mcd->m); break;
case CHUNK_CD_MSD: CHECKED_READ_BUFF(Pico_msd); break;

case CHUNK_CD_EVT:
CHECKED_READ2(0x40, buf);
Expand Down

0 comments on commit d600fee

Please sign in to comment.