mirror of
https://github.com/RaySollium99/picodrive.git
synced 2025-09-05 07:17:45 -04:00
core, complete rewrite of Pico adpcm driver
This commit is contained in:
parent
831bffdd31
commit
f1b425e380
5 changed files with 308 additions and 176 deletions
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* PicoDrive
|
||||
* (C) notaz, 2008
|
||||
* (C) irixxxx, 2024
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
|
@ -24,52 +25,46 @@ void dump(u16 w)
|
|||
}
|
||||
*/
|
||||
|
||||
static u32 PicoRead16_pico(u32 a)
|
||||
{
|
||||
u32 d = 0;
|
||||
|
||||
switch (a & 0x1e)
|
||||
{
|
||||
case 0x00: d = PicoPicohw.r1; break;
|
||||
case 0x02: d = PicoIn.pad[0]&0x1f; // d-pad
|
||||
d |= (PicoIn.pad[0]&0x20) << 2; // pen push -> C
|
||||
d = ~d;
|
||||
break;
|
||||
case 0x04: d = (PicoPicohw.pen_pos[0] >> 8); break; // what is MS bit for? Games read it..
|
||||
case 0x06: d = PicoPicohw.pen_pos[0] & 0xff; break;
|
||||
case 0x08: d = (PicoPicohw.pen_pos[1] >> 8); break;
|
||||
case 0x0a: d = PicoPicohw.pen_pos[1] & 0xff; break;
|
||||
case 0x0c: d = (1 << (PicoPicohw.page & 7)) - 1; break;
|
||||
case 0x10: d = (PicoPicohw.fifo_bytes > 0x3f) ? 0 : (0x3f - PicoPicohw.fifo_bytes); break;
|
||||
case 0x12: d = (PicoPicohw.fifo_bytes | !PicoPicoPCMBusyN()) ? 0 : 0x8000;
|
||||
d |= PicoPicohw.r12 & 0x7fff;
|
||||
break;
|
||||
default: elprintf(EL_UIO, "m68k unmapped r16 [%06x] @%06x", a, SekPc); break;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
static u32 PicoRead8_pico(u32 a)
|
||||
{
|
||||
u32 d = 0;
|
||||
|
||||
if ((a & 0xffffe0) == 0x800000) // Pico I/O
|
||||
{
|
||||
switch (a & 0x1f)
|
||||
{
|
||||
case 0x01: d = PicoPicohw.r1; break;
|
||||
case 0x03:
|
||||
d = PicoIn.pad[0]&0x1f; // d-pad
|
||||
d |= (PicoIn.pad[0]&0x20) << 2; // pen push -> C
|
||||
d = ~d;
|
||||
break;
|
||||
|
||||
case 0x05: d = (PicoPicohw.pen_pos[0] >> 8); break; // what is MS bit for? Games read it..
|
||||
case 0x07: d = PicoPicohw.pen_pos[0] & 0xff; break;
|
||||
case 0x09: d = (PicoPicohw.pen_pos[1] >> 8); break;
|
||||
case 0x0b: d = PicoPicohw.pen_pos[1] & 0xff; break;
|
||||
case 0x0d: d = (1 << (PicoPicohw.page & 7)) - 1; break;
|
||||
case 0x12: d = PicoPicohw.fifo_bytes == 0 ? 0x80 : 0; break; // guess
|
||||
default:
|
||||
goto unhandled;
|
||||
}
|
||||
return d;
|
||||
d = PicoRead16_pico(a);
|
||||
if (!(a & 1)) d >>= 8;
|
||||
return d & 0xff;
|
||||
}
|
||||
|
||||
unhandled:
|
||||
elprintf(EL_UIO, "m68k unmapped r8 [%06x] @%06x", a, SekPc);
|
||||
return d;
|
||||
}
|
||||
|
||||
static u32 PicoRead16_pico(u32 a)
|
||||
{
|
||||
u32 d = 0;
|
||||
|
||||
if (a == 0x800010)
|
||||
d = (PicoPicohw.fifo_bytes > 0x3f) ? 0 : (0x3f - PicoPicohw.fifo_bytes);
|
||||
else if (a == 0x800012)
|
||||
d = PicoPicohw.fifo_bytes == 0 ? 0x8000 : 0; // guess
|
||||
else
|
||||
elprintf(EL_UIO, "m68k unmapped r16 [%06x] @%06x", a, SekPc);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
static void PicoWrite8_pico(u32 a, u32 d)
|
||||
{
|
||||
switch (a & ~0x800000) {
|
||||
|
@ -97,10 +92,20 @@ static void PicoWrite16_pico(u32 a, u32 d)
|
|||
}
|
||||
}
|
||||
else if (a == 0x800012) {
|
||||
int r12_old = PicoPicohw.r12;
|
||||
PicoPicohw.r12 = d;
|
||||
if (r12_old != d)
|
||||
PicoReratePico();
|
||||
|
||||
PicoPicoPCMGain(8 - (d & 0x0007)); // volume
|
||||
PicoPicoPCMFilter((d & 0x00c0) >> 6); // low pass filter
|
||||
PicoPicoPCMIrqEn(d & 0x4000); // PCM IRQ enable
|
||||
|
||||
if (d & 0x8000) { // PCM reset if 1 is written (dalmatians)?
|
||||
PsndDoPCM(cycles_68k_to_z80(SekCyclesDone() - Pico.t.m68c_frame_start));
|
||||
PicoPicoPCMResetN(0);
|
||||
PicoPicohw.xpcm_ptr = PicoPicohw.xpcm_buffer;
|
||||
PicoPicohw.fifo_bytes = 0;
|
||||
PicoPicoPCMResetN(1);
|
||||
}
|
||||
// other bits used in software: 0x3f00.
|
||||
}
|
||||
else
|
||||
elprintf(EL_UIO, "m68k unmapped w16 [%06x] %04x @%06x", a, d & 0xffff, SekPc);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* PicoDrive
|
||||
* (C) notaz, 2008
|
||||
* (C) irixxxx, 2024
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
|
@ -12,72 +13,40 @@
|
|||
// 0x2f8 - 0x3f3
|
||||
picohw_state PicoPicohw;
|
||||
|
||||
static int prev_line_cnt_irq3 = 0, prev_line_cnt_irq5 = 0;
|
||||
static int fifo_bytes_line = (16000<<16)/60/262/2;
|
||||
|
||||
static const int guessed_rates[] = { 8000, 14000, 12000, 14000, 16000, 18000, 16000, 16000 }; // ?
|
||||
|
||||
#define PICOHW_FIFO_IRQ_THRESHOLD 12
|
||||
|
||||
PICO_INTERNAL void PicoReratePico(void)
|
||||
{
|
||||
int rate = guessed_rates[PicoPicohw.r12 & 7];
|
||||
if (Pico.m.pal)
|
||||
fifo_bytes_line = (rate<<16)/50/313/2;
|
||||
else fifo_bytes_line = (rate<<16)/60/262/2;
|
||||
PicoPicoPCMRerate(rate);
|
||||
PicoPicoPCMRerate();
|
||||
}
|
||||
|
||||
static void PicoLinePico(void)
|
||||
{
|
||||
PicoPicohw.line_counter++;
|
||||
|
||||
#if 1
|
||||
if ((PicoPicohw.r12 & 0x4003) && PicoPicohw.line_counter - prev_line_cnt_irq3 > 200) {
|
||||
prev_line_cnt_irq3 = PicoPicohw.line_counter;
|
||||
// just a guess/hack, allows 101 Dalmantians to boot
|
||||
elprintf(EL_PICOHW, "irq3");
|
||||
SekInterrupt(3);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (PicoPicohw.fifo_bytes > 0)
|
||||
{
|
||||
PicoPicohw.fifo_line_bytes += fifo_bytes_line;
|
||||
if (PicoPicohw.fifo_line_bytes >= (1<<16)) {
|
||||
PicoPicohw.fifo_bytes -= PicoPicohw.fifo_line_bytes >> 16;
|
||||
PicoPicohw.fifo_line_bytes &= 0xffff;
|
||||
if (PicoPicohw.fifo_bytes < 0)
|
||||
PicoPicohw.fifo_bytes = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
PicoPicohw.fifo_line_bytes = 0;
|
||||
|
||||
#if 1
|
||||
if (PicoPicohw.fifo_bytes_prev >= PICOHW_FIFO_IRQ_THRESHOLD &&
|
||||
PicoPicohw.fifo_bytes < PICOHW_FIFO_IRQ_THRESHOLD) {
|
||||
prev_line_cnt_irq3 = PicoPicohw.line_counter; // ?
|
||||
elprintf(EL_PICOHW, "irq3, fb=%i", PicoPicohw.fifo_bytes);
|
||||
SekInterrupt(3);
|
||||
}
|
||||
PicoPicohw.fifo_bytes_prev = PicoPicohw.fifo_bytes;
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
if (PicoPicohw.line_counter - prev_line_cnt_irq5 > 512) {
|
||||
prev_line_cnt_irq5 = PicoPicohw.line_counter;
|
||||
elprintf(EL_PICOHW, "irq5");
|
||||
SekInterrupt(5);
|
||||
}
|
||||
#endif
|
||||
// update sound so that irq for FIFO refill is generated
|
||||
if ((PicoPicohw.fifo_bytes | !PicoPicoPCMBusyN()) && (Pico.m.scanline & 7) == 7)
|
||||
PsndDoPCM(cycles_68k_to_z80(SekCyclesDone() - Pico.t.m68c_frame_start));
|
||||
}
|
||||
|
||||
static void PicoResetPico(void)
|
||||
{
|
||||
PicoPicoPCMReset();
|
||||
PicoPicoPCMResetN(1);
|
||||
PicoPicoPCMStartN(1);
|
||||
PicoPicohw.xpcm_ptr = PicoPicohw.xpcm_buffer;
|
||||
PicoPicohw.fifo_bytes = 0;
|
||||
PicoPicohw.r12 = 0;
|
||||
|
||||
PicoPicoPCMIrqEn(0);
|
||||
PicoPicoPCMFilter(0);
|
||||
PicoPicoPCMGain(8);
|
||||
|
||||
// map version register
|
||||
PicoDetectRegion();
|
||||
switch (Pico.m.hardware >> 6) {
|
||||
case 0: PicoPicohw.r1 = 0x40; break; // JP NTSC
|
||||
case 1: PicoPicohw.r1 = 0x00; break; // JP PAL
|
||||
case 2: PicoPicohw.r1 = 0x60; break; // US
|
||||
case 3: PicoPicohw.r1 = 0x20; break; // EU
|
||||
}
|
||||
}
|
||||
|
||||
PICO_INTERNAL void PicoInitPico(void)
|
||||
|
@ -90,15 +59,4 @@ PICO_INTERNAL void PicoInitPico(void)
|
|||
memset(&PicoPicohw, 0, sizeof(PicoPicohw));
|
||||
PicoPicohw.pen_pos[0] = 0x03c + 320/2;
|
||||
PicoPicohw.pen_pos[1] = 0x200 + 240/2;
|
||||
prev_line_cnt_irq3 = prev_line_cnt_irq5 = 0;
|
||||
|
||||
// map version register
|
||||
PicoDetectRegion();
|
||||
switch (Pico.m.hardware >> 6) {
|
||||
case 0: PicoPicohw.r1 = 0x40; break;
|
||||
case 1: PicoPicohw.r1 = 0x00; break;
|
||||
case 2: PicoPicohw.r1 = 0x60; break;
|
||||
case 3: PicoPicohw.r1 = 0x20; break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
309
pico/pico/xpcm.c
309
pico/pico/xpcm.c
|
@ -1,122 +1,285 @@
|
|||
/*
|
||||
* PicoDrive
|
||||
* (C) notaz, 2008
|
||||
* (C) irixxxx, 2024
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
*
|
||||
* The following ADPCM algorithm was stolen from MAME aica driver.
|
||||
* I'm quite sure it's not the right one, but it's the
|
||||
* best sounding of the ones that I tried.
|
||||
* The following ADPCM algorithm was derived from MAME upd7759 driver.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include "../pico_int.h"
|
||||
|
||||
#define ADPCMSHIFT 8
|
||||
#define ADFIX(f) (int) ((double)f * (double)(1<<ADPCMSHIFT))
|
||||
|
||||
/* limitter */
|
||||
#define Limit(val, max, min) { \
|
||||
if ( val > max ) val = max; \
|
||||
else if ( val < min ) val = min; \
|
||||
}
|
||||
#define Limit(val, max, min) \
|
||||
(val > max ? max : val < min ? min : val)
|
||||
|
||||
static const int TableQuant[8] =
|
||||
#define ADPCM_CLOCK (1280000/4)
|
||||
|
||||
#define FIFO_IRQ_THRESHOLD 16
|
||||
|
||||
static const int step_deltas[16][16] =
|
||||
{
|
||||
ADFIX(0.8984375),
|
||||
ADFIX(0.8984375),
|
||||
ADFIX(0.8984375),
|
||||
ADFIX(0.8984375),
|
||||
ADFIX(1.19921875),
|
||||
ADFIX(1.59765625),
|
||||
ADFIX(2.0),
|
||||
ADFIX(2.3984375)
|
||||
{ 0, 0, 1, 2, 3, 5, 7, 10, 0, 0, -1, -2, -3, -5, -7, -10 },
|
||||
{ 0, 1, 2, 3, 4, 6, 8, 13, 0, -1, -2, -3, -4, -6, -8, -13 },
|
||||
{ 0, 1, 2, 4, 5, 7, 10, 15, 0, -1, -2, -4, -5, -7, -10, -15 },
|
||||
{ 0, 1, 3, 4, 6, 9, 13, 19, 0, -1, -3, -4, -6, -9, -13, -19 },
|
||||
{ 0, 2, 3, 5, 8, 11, 15, 23, 0, -2, -3, -5, -8, -11, -15, -23 },
|
||||
{ 0, 2, 4, 7, 10, 14, 19, 29, 0, -2, -4, -7, -10, -14, -19, -29 },
|
||||
{ 0, 3, 5, 8, 12, 16, 22, 33, 0, -3, -5, -8, -12, -16, -22, -33 },
|
||||
{ 1, 4, 7, 10, 15, 20, 29, 43, -1, -4, -7, -10, -15, -20, -29, -43 },
|
||||
{ 1, 4, 8, 13, 18, 25, 35, 53, -1, -4, -8, -13, -18, -25, -35, -53 },
|
||||
{ 1, 6, 10, 16, 22, 31, 43, 64, -1, -6, -10, -16, -22, -31, -43, -64 },
|
||||
{ 2, 7, 12, 19, 27, 37, 51, 76, -2, -7, -12, -19, -27, -37, -51, -76 },
|
||||
{ 2, 9, 16, 24, 34, 46, 64, 96, -2, -9, -16, -24, -34, -46, -64, -96 },
|
||||
{ 3, 11, 19, 29, 41, 57, 79, 117, -3, -11, -19, -29, -41, -57, -79, -117 },
|
||||
{ 4, 13, 24, 36, 50, 69, 96, 143, -4, -13, -24, -36, -50, -69, -96, -143 },
|
||||
{ 4, 16, 29, 44, 62, 85, 118, 175, -4, -16, -29, -44, -62, -85, -118, -175 },
|
||||
{ 6, 20, 36, 54, 76, 104, 144, 214, -6, -20, -36, -54, -76, -104, -144, -214 },
|
||||
};
|
||||
|
||||
// changed using trial and error..
|
||||
//static const int quant_mul[16] = { 1, 3, 5, 7, 9, 11, 13, 15, -1, -3, -5, -7, -9, -11, -13, -15 };
|
||||
static const int quant_mul[16] = { 1, 3, 5, 7, 9, 11, 13, -1, -1, -3, -5, -7, -9, -11, -13, -15 };
|
||||
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, quant = 0, sgn = 0;
|
||||
static int stepsamples = (44100<<10)/16000;
|
||||
static int sample = 0, state = 0;
|
||||
static s32 stepsamples = (44100LL<<16)/ADPCM_CLOCK;
|
||||
static s32 samplepos;
|
||||
static int samplegain;
|
||||
|
||||
static int startpin, irqenable;
|
||||
static enum { RESET, START, HDR, COUNT } portstate = RESET;
|
||||
static int rate, silence, nibbles, highlow, cache;
|
||||
|
||||
|
||||
PICO_INTERNAL void PicoPicoPCMReset(void)
|
||||
// SEGA Pico specific filtering
|
||||
|
||||
#define QB 16 // mantissa bits
|
||||
#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
|
||||
} filters[4];
|
||||
static struct iir2 *filter;
|
||||
|
||||
|
||||
static void PicoPicoFilterCoeff(struct iir2 *iir, int cutoff, int rate)
|
||||
{
|
||||
sample = sgn = 0;
|
||||
quant = 0x7f;
|
||||
memset(PicoPicohw.xpcm_buffer, 0, sizeof(PicoPicohw.xpcm_buffer));
|
||||
if (cutoff >= rate/2) {
|
||||
memset(iir, 0, sizeof(*iir));
|
||||
return;
|
||||
}
|
||||
|
||||
PICO_INTERNAL void PicoPicoPCMRerate(int xpcm_rate)
|
||||
{
|
||||
stepsamples = (PicoIn.sndRate<<10)/xpcm_rate;
|
||||
// compute 2nd order butterworth filter coefficients
|
||||
double a = 1 / tan(M_PI * cutoff / rate);
|
||||
double axa = a*a;
|
||||
double gain = 1/(1 + M_SQRT2*a + axa);
|
||||
iir->gain = FP(gain);
|
||||
iir->a[0] = FP(2 * (axa-1) * gain);
|
||||
iir->a[1] = FP(-(1 - M_SQRT2*a + axa) * gain);
|
||||
}
|
||||
|
||||
#define XSHIFT 6
|
||||
static int PicoPicoFilterApply(struct iir2 *iir, int sample)
|
||||
{
|
||||
if (!iir)
|
||||
return sample;
|
||||
|
||||
#define do_sample() \
|
||||
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];
|
||||
iir->y[2] = (iir->x[0] + 2*iir->x[1] + iir->x[2]
|
||||
+ iir->y[0]*iir->a[1] + iir->y[1]*iir->a[0]) >> QB;
|
||||
return iir->y[2];
|
||||
}
|
||||
|
||||
|
||||
// pin functions, N designating a negated pin
|
||||
|
||||
PICO_INTERNAL void PicoPicoPCMResetN(int pin)
|
||||
{
|
||||
if (!pin) {
|
||||
portstate = RESET;
|
||||
sample = samplepos = state = 0;
|
||||
portstate = nibbles = silence = 0;
|
||||
} else if (portstate == RESET)
|
||||
portstate = START;
|
||||
}
|
||||
|
||||
PICO_INTERNAL void PicoPicoPCMStartN(int pin)
|
||||
{
|
||||
startpin = pin;
|
||||
}
|
||||
|
||||
PICO_INTERNAL int PicoPicoPCMBusyN(void)
|
||||
{
|
||||
return (portstate <= START);
|
||||
}
|
||||
|
||||
|
||||
// configuration functions
|
||||
|
||||
PICO_INTERNAL void PicoPicoPCMRerate(void)
|
||||
{
|
||||
// output samples per chip clock
|
||||
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
|
||||
}
|
||||
|
||||
PICO_INTERNAL void PicoPicoPCMGain(int gain)
|
||||
{
|
||||
samplegain = gain*4;
|
||||
}
|
||||
|
||||
PICO_INTERNAL void PicoPicoPCMFilter(int index)
|
||||
{
|
||||
filter = filters+index;
|
||||
if (filter->a[0] == 0)
|
||||
filter = NULL;
|
||||
}
|
||||
|
||||
PICO_INTERNAL void PicoPicoPCMIrqEn(int enable)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
// adpcm operation
|
||||
|
||||
#define apply_filter(v) PicoPicoFilterApply(filter, v)
|
||||
|
||||
#define do_sample(nibble) \
|
||||
{ \
|
||||
int delta = quant * quant_mul[srcval] >> XSHIFT; \
|
||||
sample += delta - (delta >> 2); /* 3/4 */ \
|
||||
quant = (quant * TableQuant[srcval&7]) >> ADPCMSHIFT; \
|
||||
Limit(quant, 0x6000, 0x7f); \
|
||||
Limit(sample, 32767*3/4, -32768*3/4); \
|
||||
sample += step_deltas[state][nibble]; \
|
||||
state += state_deltas[nibble]; \
|
||||
state = (state < 0 ? 0 : state > 15 ? 15 : state); \
|
||||
}
|
||||
|
||||
#define write_sample(buffer, length, stereo) \
|
||||
{ \
|
||||
while (samplepos > 0 && length > 0) { \
|
||||
int val = Limit(samplegain*sample, 16383, -16384); \
|
||||
samplepos -= 1<<16; \
|
||||
length --; \
|
||||
if (buffer) { \
|
||||
int out = apply_filter(val); \
|
||||
*buffer++ += out; \
|
||||
if (stereo) *buffer++ += out; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
PICO_INTERNAL void PicoPicoPCMUpdate(short *buffer, int length, int stereo)
|
||||
{
|
||||
unsigned char *src = PicoPicohw.xpcm_buffer;
|
||||
unsigned char *lim = PicoPicohw.xpcm_ptr;
|
||||
int srcval, needsamples = 0;
|
||||
int srcval, irq = 0;
|
||||
|
||||
if (src == lim) goto end;
|
||||
// leftover partial sample from last run
|
||||
write_sample(buffer, length, stereo);
|
||||
|
||||
for (; length > 0 && src < lim; src++)
|
||||
// loop over FIFO data, generating ADPCM samples
|
||||
while (length > 0 && src < lim)
|
||||
{
|
||||
srcval = *src >> 4;
|
||||
do_sample();
|
||||
if (silence > 0) {
|
||||
silence --;
|
||||
sample = 0;
|
||||
samplepos += stepsamples*256;
|
||||
|
||||
if (buffer)
|
||||
for (needsamples += stepsamples; needsamples > (1<<10) && length > 0; needsamples -= (1<<10), length--) {
|
||||
*buffer++ += sample;
|
||||
if (stereo) { buffer[0] = buffer[-1]; buffer++; }
|
||||
} else if (nibbles > 0) {
|
||||
nibbles --;
|
||||
|
||||
if (highlow)
|
||||
cache = *src++;
|
||||
else
|
||||
cache <<= 4;
|
||||
highlow = !highlow;
|
||||
|
||||
do_sample((cache & 0xf0) >> 4);
|
||||
samplepos += stepsamples*rate;
|
||||
|
||||
} else switch (portstate) {
|
||||
case RESET:
|
||||
sample = 0;
|
||||
samplepos += length<<16;
|
||||
break;
|
||||
case START:
|
||||
if (startpin) {
|
||||
if (*src)
|
||||
portstate ++;
|
||||
else // kill 0x00 bytes at stream start
|
||||
src ++;
|
||||
} else {
|
||||
sample = 0;
|
||||
samplepos += length<<16;
|
||||
}
|
||||
break;
|
||||
case HDR:
|
||||
srcval = *src++;
|
||||
nibbles = silence = rate = 0;
|
||||
highlow = 1;
|
||||
if (srcval == 0) { // terminator
|
||||
// HACK, kill leftover odd byte to avoid restart (Minna de Odorou)
|
||||
if (lim-src == 1) src++;
|
||||
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 3: break;
|
||||
}
|
||||
break;
|
||||
case COUNT:
|
||||
nibbles = *src++ + 1; portstate = HDR;
|
||||
break;
|
||||
}
|
||||
|
||||
srcval = *src & 0xf;
|
||||
do_sample();
|
||||
|
||||
if (buffer)
|
||||
for (needsamples += stepsamples; needsamples > (1<<10) && length > 0; needsamples -= (1<<10), length--) {
|
||||
*buffer++ += sample;
|
||||
if (stereo) { buffer[0] = buffer[-1]; buffer++; }
|
||||
write_sample(buffer, length, stereo);
|
||||
}
|
||||
|
||||
// lame normalization stuff, needed due to wrong adpcm algo
|
||||
sgn += (sample < 0) ? -1 : 1;
|
||||
if (sgn < -16 || sgn > 16) sample -= sample >> 5;
|
||||
}
|
||||
|
||||
if (src < lim) {
|
||||
// buffer cleanup, generate irq if lowwater reached
|
||||
if (src < lim && src != PicoPicohw.xpcm_buffer) {
|
||||
int di = lim - src;
|
||||
memmove(PicoPicohw.xpcm_buffer, src, di);
|
||||
PicoPicohw.xpcm_ptr = PicoPicohw.xpcm_buffer + di;
|
||||
elprintf(EL_PICOHW, "xpcm update: over %i", di);
|
||||
// adjust fifo
|
||||
|
||||
if (!irq && di < FIFO_IRQ_THRESHOLD)
|
||||
irq = irqenable;
|
||||
PicoPicohw.fifo_bytes = di;
|
||||
return;
|
||||
}
|
||||
|
||||
elprintf(EL_PICOHW, "xpcm update: under %i", length);
|
||||
} else if (src == lim && src != PicoPicohw.xpcm_buffer) {
|
||||
PicoPicohw.xpcm_ptr = PicoPicohw.xpcm_buffer;
|
||||
elprintf(EL_PICOHW, "xpcm update: under %i", length);
|
||||
|
||||
end:
|
||||
if (buffer && stereo)
|
||||
// still must expand SN76496 to stereo
|
||||
for (; length > 0; buffer+=2, length--)
|
||||
buffer[1] = buffer[0];
|
||||
|
||||
sample = sgn = 0;
|
||||
quant = 0x7f;
|
||||
if (!irq)
|
||||
irq = irqenable;
|
||||
PicoPicohw.fifo_bytes = 0;
|
||||
}
|
||||
|
||||
// TODO need an IRQ mask somewhere to avoid loosing one in cases of HINT/VINT
|
||||
if (irq && SekIrqLevel != irq) {
|
||||
elprintf(EL_PICOHW, "irq%d", irq);
|
||||
if (SekIrqLevel < irq)
|
||||
SekInterrupt(irq);
|
||||
}
|
||||
|
||||
if (buffer && length) {
|
||||
// for underflow, use last sample to avoid clicks
|
||||
int val = Limit(samplegain*sample, 16383, -16384);
|
||||
while (length--) {
|
||||
int out = apply_filter(val);
|
||||
*buffer++ += out;
|
||||
if (stereo) *buffer++ += out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -842,11 +842,17 @@ unsigned int pcd_pcm_read(unsigned int a);
|
|||
// pico/pico.c
|
||||
PICO_INTERNAL void PicoInitPico(void);
|
||||
PICO_INTERNAL void PicoReratePico(void);
|
||||
PICO_INTERNAL int PicoPicoIrqAck(int level);
|
||||
|
||||
// pico/xpcm.c
|
||||
PICO_INTERNAL void PicoPicoPCMUpdate(short *buffer, int length, int stereo);
|
||||
PICO_INTERNAL void PicoPicoPCMReset(void);
|
||||
PICO_INTERNAL void PicoPicoPCMRerate(int xpcm_rate);
|
||||
PICO_INTERNAL void PicoPicoPCMResetN(int pin);
|
||||
PICO_INTERNAL void PicoPicoPCMStartN(int pin);
|
||||
PICO_INTERNAL int PicoPicoPCMBusyN(void);
|
||||
PICO_INTERNAL void PicoPicoPCMGain(int gain);
|
||||
PICO_INTERNAL void PicoPicoPCMFilter(int index);
|
||||
PICO_INTERNAL void PicoPicoPCMIrqEn(int enable);
|
||||
PICO_INTERNAL void PicoPicoPCMRerate(void);
|
||||
|
||||
// sek.c
|
||||
PICO_INTERNAL void SekInit(void);
|
||||
|
|
|
@ -41,7 +41,7 @@ static int do_ack(int level)
|
|||
else if (pv->pending_ints & pv->reg[0] & 0x10)
|
||||
pv->pending_ints &= ~0x10;
|
||||
|
||||
return 0;
|
||||
return (PicoIn.AHW & PAHW_PICO ? PicoPicoIrqAck(level) : 0);
|
||||
}
|
||||
|
||||
/* callbacks */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue