static const int state_deltas[16] = { -1, -1, 0, 0, 1, 2, 2, 3, -1, -1, 0, 0, 1, 2, 2, 3 };
-static int sample = 0, state = 0;
-static s32 stepsamples = (44100LL<<16)/ADPCM_CLOCK;
-static s32 samplepos;
-static int samplegain;
+s32 stepsamples = (44100LL<<16)/ADPCM_CLOCK;
-static int startpin, irqenable;
-static enum { RESET, START, HDR, COUNT } portstate = RESET;
-static int rate, silence, nibbles, highlow, cache;
+static struct xpcm_state {
+ s32 samplepos; // leftover duration for current sample wrt sndrate, Q16
+ int sample; // current sample
+ short state; // ADPCM engine state
+ short samplegain; // programmable gain
+
+ char startpin; // value on the !START pin
+ char irqenable; // IRQ enabled?
+
+ char portstate; // data stream state
+ short silence; // silence blocks still to be played
+ short rate, nibbles; // ADPCM nibbles still to be played
+ unsigned char highlow, cache; // nibble selector and cache
+} xpcm;
+enum { RESET, START, HDR, COUNT }; // portstate
// SEGA Pico specific filtering
#define FP(f) (int)((f)*(1<<QB)) // convert to fixpoint
static struct iir2 { // 2nd order IIR
- s32 a[2], gain; // coefficients
- s32 y[3], x[3]; // filter history
+ s32 a[2], gain; // coefficients
+ s32 x[3], y[3]; // filter history
} filters[4];
static struct iir2 *filter;
if (!iir)
return sample;
+ // NB Butterworth specific!
iir->x[0] = iir->x[1]; iir->x[1] = iir->x[2];
iir->x[2] = sample * iir->gain; // Qb
iir->y[0] = iir->y[1]; iir->y[1] = iir->y[2];
PICO_INTERNAL void PicoPicoPCMResetN(int pin)
{
if (!pin) {
- portstate = RESET;
- sample = samplepos = state = 0;
- portstate = nibbles = silence = 0;
- } else if (portstate == RESET)
- portstate = START;
+ xpcm.portstate = RESET;
+ xpcm.sample = xpcm.samplepos = xpcm.state = 0;
+ xpcm.nibbles = xpcm.silence = 0;
+ } else if (xpcm.portstate == RESET)
+ xpcm.portstate = START;
}
PICO_INTERNAL void PicoPicoPCMStartN(int pin)
{
- startpin = pin;
+ xpcm.startpin = pin;
}
PICO_INTERNAL int PicoPicoPCMBusyN(void)
{
- return (portstate <= START);
+ return (xpcm.portstate <= START);
}
stepsamples = ((u64)PicoIn.sndRate<<16)/ADPCM_CLOCK;
// compute filter coefficients, cutoff at half the ADPCM sample rate
- PicoPicoFilterCoeff(&filters[1], 5000/2, PicoIn.sndRate); // 5-6 KHz
- PicoPicoFilterCoeff(&filters[2], 8000/2, PicoIn.sndRate); // 8-12 KHz
- PicoPicoFilterCoeff(&filters[3], 14000/2, PicoIn.sndRate); // 14-16 KHz
+ PicoPicoFilterCoeff(&filters[1], 6000/2, PicoIn.sndRate); // 5-6 KHz
+ PicoPicoFilterCoeff(&filters[2], 9000/2, PicoIn.sndRate); // 8-12 KHz
+ PicoPicoFilterCoeff(&filters[3], 15000/2, PicoIn.sndRate); // 14-16 KHz
}
PICO_INTERNAL void PicoPicoPCMGain(int gain)
{
- samplegain = gain*4;
+ xpcm.samplegain = gain*4;
}
PICO_INTERNAL void PicoPicoPCMFilter(int index)
PICO_INTERNAL void PicoPicoPCMIrqEn(int enable)
{
- irqenable = (enable ? 3 : 0);
+ xpcm.irqenable = (enable ? 3 : 0);
}
// TODO need an interupt pending mask?
PICO_INTERNAL int PicoPicoIrqAck(int level)
{
- return (PicoPicohw.fifo_bytes < FIFO_IRQ_THRESHOLD && level != irqenable ? irqenable : 0);
+ return (PicoPicohw.fifo_bytes < FIFO_IRQ_THRESHOLD && level != xpcm.irqenable
+ ? xpcm.irqenable : 0);
}
#define apply_filter(v) PicoPicoFilterApply(filter, v)
+// compute next ADPCM sample
#define do_sample(nibble) \
{ \
- sample += step_deltas[state][nibble]; \
- state += state_deltas[nibble]; \
- state = (state < 0 ? 0 : state > 15 ? 15 : state); \
+ xpcm.sample += step_deltas[xpcm.state][nibble]; \
+ xpcm.state += state_deltas[nibble]; \
+ xpcm.state = (xpcm.state < 0 ? 0 : xpcm.state > 15 ? 15 : xpcm.state); \
}
+// writes samples with sndRate, nearest neighbour resampling, filtering
#define write_sample(buffer, length, stereo) \
{ \
- while (samplepos > 0 && length > 0) { \
- int val = Limit(samplegain*sample, 16383, -16384); \
- samplepos -= 1<<16; \
+ while (xpcm.samplepos > 0 && length > 0) { \
+ int val = Limit(xpcm.samplegain*xpcm.sample, 16383, -16384); \
+ xpcm.samplepos -= 1<<16; \
length --; \
if (buffer) { \
int out = apply_filter(val); \
// loop over FIFO data, generating ADPCM samples
while (length > 0 && src < lim)
{
- if (silence > 0) {
- silence --;
- sample = 0;
- samplepos += stepsamples*256;
+ if (xpcm.silence > 0) {
+ xpcm.silence --;
+ xpcm.sample = 0;
+ xpcm.samplepos += stepsamples*256;
- } else if (nibbles > 0) {
- nibbles --;
+ } else if (xpcm.nibbles > 0) {
+ xpcm.nibbles --;
- if (highlow)
- cache = *src++;
+ if (xpcm.highlow)
+ xpcm.cache = *src++;
else
- cache <<= 4;
- highlow = !highlow;
+ xpcm.cache <<= 4;
+ xpcm.highlow = !xpcm.highlow;
- do_sample((cache & 0xf0) >> 4);
- samplepos += stepsamples*rate;
+ do_sample((xpcm.cache & 0xf0) >> 4);
+ xpcm.samplepos += stepsamples*xpcm.rate;
- } else switch (portstate) {
+ } else switch (xpcm.portstate) {
case RESET:
- sample = 0;
- samplepos += length<<16;
+ xpcm.sample = 0;
+ xpcm.samplepos += length<<16;
break;
case START:
- if (startpin) {
+ if (xpcm.startpin) {
if (*src)
- portstate ++;
+ xpcm.portstate ++;
else // kill 0x00 bytes at stream start
src ++;
} else {
- sample = 0;
- samplepos += length<<16;
+ xpcm.sample = 0;
+ xpcm.samplepos += length<<16;
}
break;
case HDR:
srcval = *src++;
- nibbles = silence = rate = 0;
- highlow = 1;
+ xpcm.nibbles = xpcm.silence = xpcm.rate = 0;
+ xpcm.highlow = 1;
if (srcval == 0) { // terminator
// HACK, kill leftover odd byte to avoid restart (Minna de Odorou)
if (lim-src == 1) src++;
- portstate = START;
+ xpcm.portstate = START;
} else switch (srcval >> 6) {
- case 0: silence = (srcval & 0x3f) + 1; break;
- case 1: rate = (srcval & 0x3f) + 1; nibbles = 256; break;
- case 2: rate = (srcval & 0x3f) + 1; portstate = COUNT; break;
+ case 0: xpcm.silence = (srcval & 0x3f) + 1; break;
+ case 1: xpcm.rate = (srcval & 0x3f) + 1; xpcm.nibbles = 256; break;
+ case 2: xpcm.rate = (srcval & 0x3f) + 1; xpcm.portstate = COUNT; break;
case 3: break;
}
break;
case COUNT:
- nibbles = *src++ + 1; portstate = HDR;
+ xpcm.nibbles = *src++ + 1; xpcm.portstate = HDR;
break;
}
elprintf(EL_PICOHW, "xpcm update: over %i", di);
if (!irq && di < FIFO_IRQ_THRESHOLD)
- irq = irqenable;
+ irq = xpcm.irqenable;
PicoPicohw.fifo_bytes = di;
} else if (src == lim && src != PicoPicohw.xpcm_buffer) {
PicoPicohw.xpcm_ptr = PicoPicohw.xpcm_buffer;
elprintf(EL_PICOHW, "xpcm update: under %i", length);
if (!irq)
- irq = irqenable;
+ irq = xpcm.irqenable;
PicoPicohw.fifo_bytes = 0;
}
if (buffer && length) {
// for underflow, use last sample to avoid clicks
- int val = Limit(samplegain*sample, 16383, -16384);
+ int val = Limit(xpcm.samplegain*xpcm.sample, 16383, -16384);
while (length--) {
int out = apply_filter(val);
*buffer++ += out;
}
}
}
+
+PICO_INTERNAL int PicoPicoPCMSave(void *buffer, int length)
+{
+ u8 *bp = buffer;
+
+ if (length < sizeof(xpcm) + sizeof(filters)) {
+ elprintf(EL_ANOMALY, "save buffer too small?");
+ return 0;
+ }
+
+ memcpy(bp, &xpcm, sizeof(xpcm));
+ bp += sizeof(xpcm);
+ memcpy(bp, filters, sizeof(filters));
+ bp += sizeof(filters);
+ return (bp - (u8*)buffer);
+}
+
+PICO_INTERNAL void PicoPicoPCMLoad(void *buffer, int length)
+{
+ u8 *bp = buffer;
+
+ if (length >= sizeof(xpcm))
+ memcpy(&xpcm, bp, sizeof(xpcm));
+ bp += sizeof(xpcm);
+ if (length >= sizeof(xpcm) + sizeof(filters))
+ memcpy(filters, bp, sizeof(filters));
+ bp += sizeof(filters);
+}
CHUNK_CD_CDC,\r
CHUNK_CD_CDD,\r
CHUNK_YM2413,\r
+ CHUNK_PICO_PCM,\r
+ CHUNK_PICO,\r
//\r
CHUNK_DEFAULT_COUNT,\r
CHUNK_CARTHW_ = CHUNK_CARTHW, // 64 (defined in PicoInt)\r
"SSH2 BIOS", // 35\r
"SDRAM",\r
"DRAM",\r
- "PAL",\r
- "events",\r
- "YM2413", //40\r
+ "32X palette",\r
+ "32X events",\r
};\r
\r
static int write_chunk(chunk_name_e name, int len, void *data, void *file)\r
int retval = -1;\r
int len;\r
\r
+ buf2 = malloc(CHUNK_LIMIT_W);\r
+ if (buf2 == NULL)\r
+ return -1;\r
+\r
areaWrite("PicoSEXT", 1, 8, file);\r
areaWrite(&ver, 1, 4, file);\r
\r
CHECKED_WRITE_BUFF(CHUNK_RAM, PicoMem.ram);\r
CHECKED_WRITE_BUFF(CHUNK_VSRAM, PicoMem.vsram);\r
CHECKED_WRITE_BUFF(CHUNK_IOPORTS, PicoMem.ioports);\r
- ym2612_pack_state();\r
- ym_regs = YM2612GetRegs();\r
- CHECKED_WRITE(CHUNK_FM, 0x200+4, ym_regs);\r
+ if (PicoIn.AHW & PAHW_PICO) {\r
+ len = PicoPicoPCMSave(buf2, CHUNK_LIMIT_W);\r
+ CHECKED_WRITE(CHUNK_PICO_PCM, len, buf2);\r
+ CHECKED_WRITE(CHUNK_PICO, sizeof(PicoPicohw), &PicoPicohw);\r
+ } else {\r
+ ym2612_pack_state();\r
+ ym_regs = YM2612GetRegs();\r
+ CHECKED_WRITE(CHUNK_FM, 0x200+4, ym_regs);\r
+ }\r
\r
if (!(PicoIn.opt & POPT_DIS_IDLE_DET))\r
SekInitIdleDet();\r
ym_regs = YM2413GetRegs();\r
CHECKED_WRITE(CHUNK_YM2413, 0x40+4, ym_regs);\r
}\r
+ CHECKED_WRITE(CHUNK_PSG, 28*4, sn76496_regs);\r
+\r
+ if (!(PicoIn.AHW & PAHW_PICO)) {\r
+ z80_pack(buff_z80);\r
+ CHECKED_WRITE_BUFF(CHUNK_Z80, buff_z80);\r
+ CHECKED_WRITE_BUFF(CHUNK_ZRAM, PicoMem.zram);\r
+ }\r
\r
CHECKED_WRITE_BUFF(CHUNK_VRAM, PicoMem.vram);\r
- CHECKED_WRITE_BUFF(CHUNK_ZRAM, PicoMem.zram);\r
CHECKED_WRITE_BUFF(CHUNK_CRAM, PicoMem.cram);\r
- CHECKED_WRITE_BUFF(CHUNK_MISC, Pico.m);\r
\r
+ CHECKED_WRITE_BUFF(CHUNK_MISC, Pico.m);\r
PicoVideoSave();\r
CHECKED_WRITE_BUFF(CHUNK_VIDEO, Pico.video);\r
\r
- z80_pack(buff_z80);\r
- CHECKED_WRITE_BUFF(CHUNK_Z80, buff_z80);\r
- CHECKED_WRITE(CHUNK_PSG, 28*4, sn76496_regs);\r
-\r
if (PicoIn.AHW & PAHW_MCD)\r
{\r
- buf2 = malloc(CHUNK_LIMIT_W);\r
- if (buf2 == NULL)\r
- return -1;\r
-\r
memset(buff, 0, sizeof(buff));\r
SekPackCpu(buff, 1);\r
if (Pico_mcd->s68k_regs[3] & 4) // 1M mode?\r
ym2612_unpack_state();\r
break;\r
\r
+ case CHUNK_PICO_PCM:\r
+ CHECKED_READ(len, buf);\r
+ PicoPicoPCMLoad(buf, len);\r
+ break;\r
+ case CHUNK_PICO:\r
+ CHECKED_READ_BUFF(PicoPicohw);\r
+ break;\r
+\r
case CHUNK_SMS:\r
CHECKED_READ_BUFF(Pico.ms);\r
break;\r