Browse Source

sound, remove hysteresis (lessens distortion, increases frequency limit) and sms, improve save/load + gg compatibility

root 2 years ago
parent
commit
77f9e1f6fa
3 changed files with 61 additions and 51 deletions
  1. 5 1
      pico/pico_int.h
  2. 54 32
      pico/sms.c
  3. 2 18
      pico/sound/sound.c

+ 5 - 1
pico/pico_int.h

@@ -348,7 +348,11 @@ struct PicoMS
   unsigned char io_ctl;
   unsigned char nmi_state;
   unsigned char mapper;
-  unsigned char pad[0x4d];
+  unsigned char fm_ctl;
+  unsigned char vdp_buffer;
+  unsigned char vdp_hlatch;
+  unsigned char io_gg[0x08];
+  unsigned char pad[0x42];
 };
 
 // emu state and data for the asm code

+ 54 - 32
pico/sms.c

@@ -20,17 +20,15 @@ extern void YM2413_regWrite(unsigned reg);
 extern void YM2413_dataWrite(unsigned data);
 
 
-static unsigned short ymflag = 0xffff;
-static u8 vdp_buffer;
-static u8 vdp_hlatch;
+static int vdp_lastline;
 
 static unsigned char vdp_data_read(void)
 {
   struct PicoVideo *pv = &Pico.video;
   unsigned char d;
 
-  d = vdp_buffer;
-  vdp_buffer = PicoMem.vramb[MEM_LE2(pv->addr)];
+  d = Pico.ms.vdp_buffer;
+  Pico.ms.vdp_buffer = PicoMem.vramb[MEM_LE2(pv->addr)];
   pv->addr = (pv->addr + 1) & 0x3fff;
   pv->pending = 0;
   return d;
@@ -46,6 +44,14 @@ static unsigned char vdp_ctl_read(void)
   pv->pending = pv->pending_ints = 0;
   pv->status = 0;
 
+  if (pv->reg[0] & 0x04)
+    d |= 0x1f; // unused bits in mode 4 read as 1
+
+  // the vint state must be set one instruction in advance, else reading status
+  // would never report vint before the irq handler, which it does on a real SMS
+  if (vdp_lastline && z80_cyclesLeft < 10) // e.g. Chicago GG
+    d |= 0x80;
+
   elprintf(EL_SR, "VDP sr: %02x", d);
   return d;
 }
@@ -58,7 +64,7 @@ static void vdp_data_write(unsigned char d)
     if (Pico.m.hardware & 0x1) { // GG, same layout as MD
       unsigned a = pv->addr & 0x3f;
       if (a & 0x1) { // write complete color on high byte write
-        u16 c = ((d&0x0f) << 8) | vdp_buffer;
+        u16 c = ((d&0x0f) << 8) | Pico.ms.vdp_buffer;
         if (PicoMem.cram[a >> 1] != c) Pico.m.dirtyPal = 1;
         PicoMem.cram[a >> 1] = c;
       }
@@ -73,7 +79,7 @@ static void vdp_data_write(unsigned char d)
   }
   pv->addr = (pv->addr + 1) & 0x3fff;
 
-  vdp_buffer = d;
+  Pico.ms.vdp_buffer = d;
   pv->pending = 0;
 }
 
@@ -110,7 +116,7 @@ static void vdp_ctl_write(u8 d)
     pv->addr &= 0x00ff;
     pv->addr |= (d & 0x3f) << 8;
     if (pv->type == 0) {
-      vdp_buffer = PicoMem.vramb[MEM_LE2(pv->addr)];
+      Pico.ms.vdp_buffer = PicoMem.vramb[MEM_LE2(pv->addr)];
       pv->addr = (pv->addr + 1) & 0x3fff;
     }
   } else {
@@ -122,12 +128,13 @@ static void vdp_ctl_write(u8 d)
 
 static unsigned char z80_sms_in(unsigned short a)
 {
-  unsigned char d = 0;
+  unsigned char d = 0xff;
 
+  a &= 0xff;
   elprintf(EL_IO, "z80 port %04x read", a);
-  if((a&0xff)>= 0xf0){
+  if(a >= 0xf0){
     if (PicoIn.opt & POPT_EN_YM2413){
-      switch((a&0xff))
+      switch(a)
       {
       case 0xf0:
         // FM reg port
@@ -137,18 +144,24 @@ static unsigned char z80_sms_in(unsigned short a)
         break;
       case 0xf2:
         // bit 0 = 1 active FM Pac
-        d = 0xf8 | ymflag;
+        d = 0xf8 | Pico.ms.fm_ctl;
         break;
       }
     }
   }
   else{
-    a &= 0xc1;
-    switch (a)
+    switch (a & 0xc1)
     {
       case 0x00:
       case 0x01:
-        d = 0xff & ~(PicoIn.pad[0] & 0x80);
+        if ((Pico.m.hardware & 0x1) && a < 0x8) { // GG I/O area
+          switch (a) {
+          case 0: d = 0xff & ~(PicoIn.pad[0] & 0x80); break;
+          case 1: d = Pico.ms.io_gg[1] | Pico.ms.io_gg[2]; break;
+          case 5: d = Pico.ms.io_gg[5] & 0xf8; break;
+          default: d = Pico.ms.io_gg[a]; break;
+          }
+        }
         break;
 
       case 0x40: /* V counter */
@@ -157,7 +170,7 @@ static unsigned char z80_sms_in(unsigned short a)
         break;
 
       case 0x41: /* H counter */
-	d = vdp_hlatch;
+	d = Pico.ms.vdp_hlatch;
         elprintf(EL_HVCNT, "H counter read: %02x", d);
         break;
 
@@ -187,9 +200,10 @@ static void z80_sms_out(unsigned short a, unsigned char d)
 {
   elprintf(EL_IO, "z80 port %04x write %02x", a, d);
 
-  if((a&0xff)>= 0xf0){
+  a &= 0xff;
+  if(a >= 0xf0){
     if (PicoIn.opt & POPT_EN_YM2413){
-      switch((a&0xff))
+      switch(a)
       {
         case 0xf0:
           // FM reg port
@@ -201,31 +215,38 @@ static void z80_sms_out(unsigned short a, unsigned char d)
           break;
         case 0xf2:
           // bit 0 = 1 active FM Pac
-          ymflag = d & 0x1;
+          Pico.ms.fm_ctl = d & 0x1;
           break;
       }
     }
   }
   else{
-    a &= 0xc1;
-    switch (a)
+    switch (a & 0xc1)
     {
+      case 0x00:
+        if ((Pico.m.hardware & 0x1) && a < 0x8)   // GG I/O area
+          Pico.ms.io_gg[a] = d;
+        break;
       case 0x01:
-        // latch hcounter if one of the TH lines is switched to 1
-        if ((Pico.ms.io_ctl ^ d) & d & 0xa0) {
-          unsigned c = 228-z80_cyclesLeft;
-          // 171 slots per scanline of 228 clocks, runs from 0xf4-0x93,0xe9-0xf3
-          // this matches h counter tables in SMSVDPTest
-          c = (((c+2) * ((171<<8)/228))>>8)-1 + 0xf4; // Q8 to avoid dividing
-          if (c > 0x193) c += 0xe9-0x93-1;
-          vdp_hlatch = (u8)c;
+        if ((Pico.m.hardware & 0x1) && a < 0x8) { // GG I/O area
+          Pico.ms.io_gg[a] = d;
+        } else {
+          // pad. latch hcounter if one of the TH lines is switched to 1
+          if ((Pico.ms.io_ctl ^ d) & d & 0xa0) {
+            unsigned c = 228-z80_cyclesLeft;
+            // 171 slots per scanline of 228 clocks, counted 0xf4-0x93,0xe9-0xf3
+            // this matches h counter tables in SMSVDPTest
+            c = (((c+2) * ((171<<8)/228))>>8)-1 + 0xf4; // Q8 to avoid dividing
+            if (c > 0x193) c += 0xe9-0x93-1;
+            Pico.ms.vdp_hlatch = (u8)c;
+          }
+          Pico.ms.io_ctl = d;
         }
-        Pico.ms.io_ctl = d;
         break;
 
       case 0x40:
       case 0x41:
-        PsndDoPSG(z80_cyclesDone());
+        PsndDoPSG(Pico.m.scanline*228 + 228-z80_cyclesLeft);
         SN76496Write(d);
         break;
 
@@ -333,7 +354,7 @@ void PicoResetMS(void)
 {
   z80_reset();
   PsndReset(); // pal must be known here
-  ymflag = 0xffff;
+  Pico.ms.fm_ctl = 0xff;
   Pico.m.dirtyPal = 1;
 
   if (PicoIn.hwSelect) {
@@ -460,6 +481,7 @@ void PicoFrameMS(void)
 
   for (y = 0; y < lines; y++)
   {
+    vdp_lastline = lines_vis == y;
     pv->v_counter = Pico.m.scanline = y;
     if (y > 218)
       pv->v_counter = y - 6;

+ 2 - 18
pico/sound/sound.c

@@ -130,10 +130,6 @@ PICO_INTERNAL void PsndDoDAC(int cyc_to)
   // number of samples to fill in buffer (Q20)
   len = (cyc_to * Pico.snd.clkl_mult) - Pico.snd.dac_pos;
 
-  // don't do this too often (about once every 2 scanlines)
-  if (len <= PicoIn.sndRate << 3) // Q16, (PicoIn.sndRate << 16) >> 13
-    return;
-
   // update position and calculate buffer offset and length
   pos = (Pico.snd.dac_pos+0x80000) >> 20;
   Pico.snd.dac_pos += len;
@@ -173,10 +169,6 @@ PICO_INTERNAL void PsndDoPSG(int cyc_to)
   // number of samples to fill in buffer (Q20)
   len = (cyc_to * Pico.snd.clkl_mult) - Pico.snd.psg_pos;
 
-  // don't do this too often (about once every 2 scanlines)
-  if (len <= PicoIn.sndRate << 3) // Q16, (PicoIn.sndRate << 16) >> 13
-    return;
-
   // update position and calculate buffer offset and length
   pos = (Pico.snd.psg_pos+0x80000) >> 20;
   Pico.snd.psg_pos += len;
@@ -204,10 +196,6 @@ PICO_INTERNAL void PsndDoYM2413(int cyc_to)
   // number of samples to fill in buffer (Q20)
   len = (cyc_to * Pico.snd.clkl_mult) - Pico.snd.ym2413_pos;
 
-  // don't do this too often (about once every 2 scanlines)
-  if (len <= PicoIn.sndRate << 3) // Q16, (PicoIn.sndRate << 16) >> 13
-    return;
-
   // update position and calculate buffer offset and length
   pos = (Pico.snd.ym2413_pos+0x80000) >> 20;
   Pico.snd.ym2413_pos += len;
@@ -245,13 +233,9 @@ PICO_INTERNAL void PsndDoFM(int cyc_to)
   int pos, len;
   int stereo = 0;
 
-  // Q16, number of samples since last call
+  // Q20, number of samples since last call
   len = (cyc_to * Pico.snd.clkl_mult) - Pico.snd.fm_pos;
 
-  // don't do this too often (about once every 2 scanlines)
-  if (len <= PicoIn.sndRate << 3) // Q16, (PicoIn.sndRate << 16) >> 13
-    return;
-
   // update position and calculate buffer offset and length
   pos = (Pico.snd.fm_pos+0x80000) >> 20;
   Pico.snd.fm_pos += len;
@@ -351,7 +335,7 @@ static int PsndRender(int offset, int length)
     return length;
   }
 
-  // Fill up DAC output in case of missing samples (Q16 rounding errors)
+  // Fill up DAC output in case of missing samples (Q rounding errors)
   if (length-daclen > 0) {
     short *dacbuf = PicoIn.sndOut + (daclen << stereo);
     Pico.snd.dac_pos += (length-daclen) << 20;