core, complete rewrite of Pico adpcm driver
authorkub <derkub@gmail.com>
Sat, 20 Jan 2024 16:48:16 +0000 (17:48 +0100)
committerkub <derkub@gmail.com>
Mon, 22 Jan 2024 18:44:09 +0000 (19:44 +0100)
pico/pico/memory.c
pico/pico/pico.c
pico/pico/xpcm.c
pico/pico_int.h
pico/sek.c

index 14bf5d4..f294b03 100644 (file)
@@ -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,49 +25,43 @@ void dump(u16 w)
 }
 */
 
-static u32 PicoRead8_pico(u32 a)
+static u32 PicoRead16_pico(u32 a)
 {
   u32 d = 0;
 
-  if ((a & 0xffffe0) == 0x800000) // Pico I/O
+  switch (a & 0x1e)
   {
-    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;
+    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;
   }
-
-unhandled:
-  elprintf(EL_UIO, "m68k unmapped r8  [%06x] @%06x", a, SekPc);
   return d;
 }
 
-static u32 PicoRead16_pico(u32 a)
+static u32 PicoRead8_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);
+  if ((a & 0xffffe0) == 0x800000) // Pico I/O
+  {
+    d = PicoRead16_pico(a);
+    if (!(a & 1)) d >>= 8;
+    return d & 0xff;
+  }
 
+  elprintf(EL_UIO, "m68k unmapped r8  [%06x] @%06x", a, SekPc);
   return d;
 }
 
@@ -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);
index 1266ce8..81d535a 100644 (file)
@@ -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.
 //    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;
-  }
 }
-
index 5d13a4b..ef0f870 100644 (file)
 /*
  * 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)
+
+#define ADPCM_CLOCK    (1280000/4)
+
+#define FIFO_IRQ_THRESHOLD 16
 
-static const int TableQuant[8] =
+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, 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;
+
+
+// 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)
+{
+  if (cutoff >= rate/2) {
+    memset(iir, 0, sizeof(*iir));
+    return;
+  }
+
+  // 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);
+}
+
+static int PicoPicoFilterApply(struct iir2 *iir, int sample)
+{
+  if (!iir)
+    return 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;
 
-static int sample = 0, quant = 0, sgn = 0;
-static int stepsamples = (44100<<10)/16000;
+  // 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 PicoPicoPCMReset(void)
+PICO_INTERNAL void PicoPicoPCMIrqEn(int enable)
 {
-  sample = sgn = 0;
-  quant = 0x7f;
-  memset(PicoPicohw.xpcm_buffer, 0, sizeof(PicoPicohw.xpcm_buffer));
+  irqenable = (enable ? 3 : 0);
 }
 
-PICO_INTERNAL void PicoPicoPCMRerate(int xpcm_rate)
+// TODO need an interupt pending mask?
+PICO_INTERNAL int PicoPicoIrqAck(int level)
 {
-  stepsamples = (PicoIn.sndRate<<10)/xpcm_rate;
+  return (PicoPicohw.fifo_bytes < FIFO_IRQ_THRESHOLD && level != irqenable ? irqenable : 0);
 }
 
-#define XSHIFT 6
 
-#define do_sample() \
+// 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;
 
-    srcval = *src & 0xf;
-    do_sample();
+      do_sample((cache & 0xf0) >> 4);
+      samplepos += stepsamples*rate;
 
-    if (buffer)
-      for (needsamples += stepsamples; needsamples > (1<<10) && length > 0; needsamples -= (1<<10), length--) {
-        *buffer++ += sample;
-        if (stereo) { buffer[0] = buffer[-1]; buffer++; }
+    } 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;
       }
 
-    // lame normalization stuff, needed due to wrong adpcm algo
-    sgn += (sample < 0) ? -1 : 1;
-    if (sgn < -16 || sgn > 16) sample -= sample >> 5;
+    write_sample(buffer, length, stereo);
   }
 
-  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;
-  }
+  } else if (src == lim && src != PicoPicohw.xpcm_buffer) {
+    PicoPicohw.xpcm_ptr = PicoPicohw.xpcm_buffer;
+    elprintf(EL_PICOHW, "xpcm update: under %i", length);
 
-  elprintf(EL_PICOHW, "xpcm update: under %i", length);
-  PicoPicohw.xpcm_ptr = PicoPicohw.xpcm_buffer;
+    if (!irq)
+      irq = irqenable;
+    PicoPicohw.fifo_bytes = 0;
+  }
 
-end:
-  if (buffer && stereo)
-    // still must expand SN76496 to stereo
-    for (; length > 0; buffer+=2, length--)
-      buffer[1] = buffer[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);
+  }
 
-  sample = sgn = 0;
-  quant = 0x7f;
+  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;
+    }
+  }
 }
-
index 52734c6..e9437c1 100644 (file)
@@ -842,11 +842,17 @@ unsigned int pcd_pcm_read(unsigned int a);
 // pico/pico.c\r
 PICO_INTERNAL void PicoInitPico(void);\r
 PICO_INTERNAL void PicoReratePico(void);\r
+PICO_INTERNAL int PicoPicoIrqAck(int level);\r
 \r
 // pico/xpcm.c\r
 PICO_INTERNAL void PicoPicoPCMUpdate(short *buffer, int length, int stereo);\r
-PICO_INTERNAL void PicoPicoPCMReset(void);\r
-PICO_INTERNAL void PicoPicoPCMRerate(int xpcm_rate);\r
+PICO_INTERNAL void PicoPicoPCMResetN(int pin);\r
+PICO_INTERNAL void PicoPicoPCMStartN(int pin);\r
+PICO_INTERNAL int PicoPicoPCMBusyN(void);\r
+PICO_INTERNAL void PicoPicoPCMGain(int gain);\r
+PICO_INTERNAL void PicoPicoPCMFilter(int index);\r
+PICO_INTERNAL void PicoPicoPCMIrqEn(int enable);\r
+PICO_INTERNAL void PicoPicoPCMRerate(void);\r
 \r
 // sek.c\r
 PICO_INTERNAL void SekInit(void);\r
index 236d03f..68aeb17 100644 (file)
@@ -41,7 +41,7 @@ static int do_ack(int level)
   else if (pv->pending_ints & pv->reg[0] & 0x10)\r
     pv->pending_ints &= ~0x10;\r
 \r
-  return 0;\r
+  return (PicoIn.AHW & PAHW_PICO ? PicoPicoIrqAck(level) : 0);\r
 }\r
 \r
 /* callbacks */\r