Pico PCM, only one hardcoded mode for now
authornotaz <notasas@gmail.com>
Sun, 11 May 2008 19:51:59 +0000 (19:51 +0000)
committernotaz <notasas@gmail.com>
Sun, 11 May 2008 19:51:59 +0000 (19:51 +0000)
git-svn-id: file:///home/notaz/opt/svn/PicoDrive@444 be3aeb3a-fb24-0410-a615-afba39da0efa

Pico/Pico.h
Pico/Pico/Memory.c
Pico/Pico/Pico.c
Pico/Pico/xpcm.c [new file with mode: 0644]
Pico/PicoInt.h
Pico/sound/sound.c
platform/linux/Makefile

index 8398e74..0178590 100644 (file)
@@ -71,14 +71,18 @@ extern int  (*PicoMCDcloseTray)(void);
 extern int PicoCDBuffers;\r
 \r
 // Pico/Pico.c\r
+#define XPCM_BUFFER_SIZE (320+32)\r
 typedef struct\r
 {\r
        int pen_pos[2];\r
        int page;\r
        // internal\r
-       int fifo_bytes;\r
+       int fifo_bytes;      // free bytes in FIFO\r
+       int fifo_line_bytes; // float part, << 16\r
        int line_counter;\r
-       unsigned int r1, r12;\r
+       unsigned short r1, r12;\r
+       unsigned char xpcm_buffer[XPCM_BUFFER_SIZE+4];\r
+       unsigned char *xpcm_ptr;\r
 } picohw_state;\r
 extern picohw_state PicoPicohw;\r
 \r
index 2216691..5760324 100644 (file)
@@ -143,8 +143,20 @@ static void PicoWritePico16(u32 a,u16 d)
   if ((a&0xfffff0)==0xc00000) { PicoVideoWrite(a,(u16)d); return; } // VDP
 
 //  if (a == 0x800010) dump(d);
-  if (a == 0x800010) PicoPicohw.fifo_bytes += 2;
-  if (a == 0x800012) PicoPicohw.r12 = d;
+  if (a == 0x800010)
+  {
+    PicoPicohw.fifo_bytes += 2;
+
+    if (PicoPicohw.xpcm_ptr < PicoPicohw.xpcm_buffer + XPCM_BUFFER_SIZE) {
+      *PicoPicohw.xpcm_ptr++ = d >> 8;
+      *PicoPicohw.xpcm_ptr++ = d;
+    }
+    else if (PicoPicohw.xpcm_ptr == PicoPicohw.xpcm_buffer + XPCM_BUFFER_SIZE) {
+      elprintf(EL_ANOMALY, "xpcm_buffer overflow!");
+      PicoPicohw.xpcm_ptr++;
+    }
+  }
+  else if (a == 0x800012) PicoPicohw.r12 = d;
 
   elprintf(EL_UIO, "w16: %06x, %04x", a&0xffffff, d);
 }
index e1133c0..ece6d64 100644 (file)
@@ -6,8 +6,9 @@
 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; // fifo bytes/line. FIXME: other rates, modes
 
-static void PicoLineHookPico(int count)
+static void PicoLinePico(int count)
 {
   PicoPicohw.line_counter += count;
 
@@ -29,11 +30,18 @@ static void PicoLineHookPico(int count)
   }
 #endif
 
-  if ((PicoPicohw.line_counter & 3) == 0 || count > 10)
+  if (PicoPicohw.fifo_bytes > 0)
   {
-    if (PicoPicohw.fifo_bytes > 0)
-      PicoPicohw.fifo_bytes--;
+    PicoPicohw.fifo_line_bytes += fifo_bytes_line * count;
+    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 0
   if (PicoPicohw.line_counter - prev_line_cnt_irq5 > 512) {
@@ -44,20 +52,26 @@ static void PicoLineHookPico(int count)
 #endif
 }
 
+static void PicoResetPico(void)
+{
+  PicoPicoPCMReset();
+  PicoPicohw.xpcm_ptr = PicoPicohw.xpcm_buffer;
+}
+
 PICO_INTERNAL int PicoInitPico(void)
 {
   elprintf(EL_STATUS, "Pico detected");
-  PicoLineHook = PicoLineHookPico;
+  PicoLineHook = PicoLinePico;
+  PicoResetHook = PicoResetPico;
 
   PicoAHW = PAHW_PICO;
   memset(&PicoPicohw, 0, sizeof(PicoPicohw));
   PicoPicohw.pen_pos[0] = 0x03c + 352/2;
   PicoPicohw.pen_pos[1] = 0x200 + 252/2;
-  prev_line_cnt_irq3 = 0, prev_line_cnt_irq5 = 0;
+  prev_line_cnt_irq3 = prev_line_cnt_irq5 = 0;
 
   // map version register
   PicoDetectRegion();
-  elprintf(EL_STATUS, "a %x", Pico.m.hardware);
   switch (Pico.m.hardware >> 6) {
     case 0: PicoPicohw.r1 = 0x00; break;
     case 1: PicoPicohw.r1 = 0x00; break;
diff --git a/Pico/Pico/xpcm.c b/Pico/Pico/xpcm.c
new file mode 100644 (file)
index 0000000..a92f4d6
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+#include "../PicoInt.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; \
+}
+
+const int TableQuant[8] =
+{
+  ADFIX(0.8984375),
+  ADFIX(0.8984375),
+  ADFIX(0.8984375),
+  ADFIX(0.8984375),
+  ADFIX(1.19921875),
+  ADFIX(1.59765625),
+  ADFIX(2.0),
+  ADFIX(2.3984375)
+};
+
+// changed using trial and error..
+//const int quant_mul[16] = { 1, 3, 5, 7, 9, 11, 13, 15, -1, -3, -5, -7, -9, -11, -13, -15 };
+const int quant_mul[16]   = { 1, 3, 5, 7, 9, 11, 13, -1, -1, -3, -5, -7, -9, -11, -13, -15 };
+
+static int sample = 0, quant = 0;
+
+PICO_INTERNAL void PicoPicoPCMReset(void)
+{
+  sample = 0;
+  quant = 0x7f;
+  memset(PicoPicohw.xpcm_buffer, 0, sizeof(PicoPicohw.xpcm_buffer));
+}
+
+#define XSHIFT 7
+
+#define do_sample() \
+{ \
+  sample += quant * quant_mul[srcval] >> XSHIFT; \
+  quant = (quant * TableQuant[srcval&7]) >> ADPCMSHIFT; \
+  Limit(quant, 0x6000, 0x7f); \
+  Limit(sample, 32767, -32768); \
+}
+
+PICO_INTERNAL void PicoPicoPCMUpdate(short *buffer, int length, int stereo)
+{
+  unsigned char *src = PicoPicohw.xpcm_buffer;
+  unsigned char *lim = PicoPicohw.xpcm_ptr;
+  int srcval, stepsamples = (44100<<10)/16000, needsamples = 0; // TODO: stepsamples
+
+  if (src == lim)
+  {
+    if (stereo)
+      // still must expand SN76496 to stereo
+      for (; length > 0; buffer+=2, length--)
+        buffer[1] = buffer[0];
+    sample = quant = 0;
+    return;
+  }
+
+  for (; length > 0 && src < lim; src++)
+  {
+    srcval = *src >> 4;
+    do_sample();
+
+    for (needsamples += stepsamples; needsamples > (1<<10) && length > 0; needsamples -= (1<<10), length--) {
+      *buffer++ = sample;
+      if (stereo) { buffer[0] = buffer[-1]; buffer++; }
+    }
+
+    srcval = *src & 0xf;
+    do_sample();
+
+    for (needsamples += stepsamples; needsamples > (1<<10) && length > 0; needsamples -= (1<<10), length--) {
+      *buffer++ = sample;
+      if (stereo) { buffer[0] = buffer[-1]; buffer++; }
+    }
+  }
+
+  if (src < lim) {
+    int di = lim - src;
+    memmove(PicoPicohw.xpcm_buffer, src, di);
+    PicoPicohw.xpcm_ptr = PicoPicohw.xpcm_buffer + di;
+    elprintf(EL_STATUS, "xpcm update: over %i", di);
+  }
+  else
+  {
+    elprintf(EL_STATUS, "xpcm update: under %i", length);
+    PicoPicohw.xpcm_ptr = PicoPicohw.xpcm_buffer;
+  }
+}
+
index b5cb06d..eb78f8c 100644 (file)
@@ -444,6 +444,10 @@ PICO_INTERNAL int PicoFrameMCD(void);
 // Pico/Pico.c\r
 PICO_INTERNAL int PicoInitPico(void);\r
 \r
+// Pico/xpcm.c\r
+PICO_INTERNAL void PicoPicoPCMUpdate(short *buffer, int length, int stereo);\r
+PICO_INTERNAL void PicoPicoPCMReset(void);\r
+\r
 // Sek.c\r
 PICO_INTERNAL int SekInit(void);\r
 PICO_INTERNAL int SekReset(void);\r
index 4c13a2f..f0348db 100644 (file)
@@ -326,6 +326,11 @@ PICO_INTERNAL int PsndRender(int offset, int length)
   if (PicoOpt & POPT_EN_PSG)\r
     SN76496Update(PsndOut+offset, length, stereo);\r
 \r
+  if (PicoAHW & PAHW_PICO) {\r
+    PicoPicoPCMUpdate(PsndOut+offset, length, stereo);\r
+    return length;\r
+  }\r
+\r
   // Add in the stereo FM buffer\r
   if (PicoOpt & POPT_EN_FM) {\r
     buf32_updated = YM2612UpdateOne(buf32, length, stereo, 1);\r
index 5cde7f6..e3c89fc 100644 (file)
@@ -42,7 +42,7 @@ OBJS += Pico/cd/Pico.o Pico/cd/Memory.o Pico/cd/Sek.o Pico/cd/LC89510.o \
                Pico/cd/cd_sys.o Pico/cd/cd_file.o Pico/cd/cue.o Pico/cd/gfx_cd.o \
                Pico/cd/Area.o Pico/cd/Misc.o Pico/cd/pcm.o Pico/cd/buffering.o
 # Pico - Pico
-OBJS += Pico/Pico/Pico.o Pico/Pico/Memory.o
+OBJS += Pico/Pico/Pico.o Pico/Pico/Memory.o Pico/Pico/xpcm.o
 # Pico - sound
 OBJS += Pico/sound/sound.o Pico/sound/sn76496.o Pico/sound/ym2612.o Pico/sound/mix.o
 # Pico - carthw