audio: added SSG-EG to YM2612, plus some timing changes for SN76496+YM2612
authorkub <derkub@gmail.com>
Tue, 7 Jan 2020 23:49:13 +0000 (00:49 +0100)
committerkub <derkub@gmail.com>
Tue, 7 Jan 2020 23:49:13 +0000 (00:49 +0100)
13 files changed:
Makefile
cpu/drc/emit_arm64.c
pico/memory.c
pico/pico.h
pico/pico_cmn.c
pico/pico_int.h
pico/sms.c
pico/sound/mix.c
pico/sound/mix_arm.S
pico/sound/sound.c
pico/sound/ym2612.c
pico/sound/ym2612.h
pico/sound/ym2612_arm.S

index 49116ce..053e160 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -36,7 +36,7 @@ endif
 
 ifeq ("$(PLATFORM)",$(filter "$(PLATFORM)","gp2x" "opendingux" "rpi1"))
 # very small caches, avoid optimization options making the binary much bigger
-CFLAGS += -finline-limit=43 -fno-unroll-loops -fno-ipa-cp -ffast-math
+CFLAGS += -finline-limit=42 -fno-unroll-loops -fno-ipa-cp -ffast-math
 # this gets you about 20% better execution speed on 32bit arm/mips
 CFLAGS += -fno-common -fno-stack-protector -fno-guess-branch-probability -fno-caller-saves -fno-tree-loop-if-convert -fno-regmove
 endif
index 2e87316..f4645bc 100644 (file)
@@ -1393,7 +1393,7 @@ static void emith_sync_t(int sr)
        else if (tcond >= 0) {
                int tmp = rcache_get_tmp();
                EMIT(A64_CSET(tcond, tmp));
-               EMIT(A64_BFI_IMM(sr, tmp, 0, 1)); // assumes SR.T = bit 0
+               EMIT(A64_BFI_IMM(sr, tmp, __builtin_ffs(T)-1, 1));
                rcache_free_tmp(tmp);
        }
        tcond = -1;
index cc82f78..9fe3a08 100644 (file)
@@ -546,7 +546,7 @@ static void PicoWrite8_z80(u32 a, u32 d)
   }\r
   if ((a & 0x6000) == 0x4000) { // FM Sound\r
     if (PicoIn.opt & POPT_EN_FM)\r
-      Pico.m.status |= ym2612_write_local(a & 3, d & 0xff, 0) & 1;\r
+      ym2612_write_local(a & 3, d & 0xff, 0);\r
     return;\r
   }\r
   // TODO: probably other VDP access too? Maybe more mirrors?\r
@@ -1059,6 +1059,8 @@ static int ym2612_write_local(u32 a, u32 d, int is_from_z80)
       break;\r
   }\r
 \r
+  int scanline = get_scanline(is_from_z80);\r
+  PsndDoFM(scanline);\r
 #ifdef __GP2X__\r
   if (PicoIn.opt & POPT_EXT_FM)\r
     return YM2612Write_940(a, d, get_scanline(is_from_z80));\r
@@ -1224,7 +1226,7 @@ static unsigned char z80_md_bank_read(unsigned short a)
 static void z80_md_ym2612_write(unsigned int a, unsigned char data)\r
 {\r
   if (PicoIn.opt & POPT_EN_FM)\r
-    Pico.m.status |= ym2612_write_local(a, data, 1) & 1;\r
+    ym2612_write_local(a, data, 1);\r
 }\r
 \r
 static void z80_md_vdp_br_write(unsigned int a, unsigned char data)\r
index a9359a1..daf5dfd 100644 (file)
@@ -70,7 +70,7 @@ extern void *p32x_bios_g, *p32x_bios_m, *p32x_bios_s;
 #define POPT_EN_DRC         (1<<17)\r
 #define POPT_DIS_SPRITE_LIM (1<<18)\r
 #define POPT_DIS_IDLE_DET   (1<<19)\r
-#define POPT_EN_32X         (1<<20)\r
+#define POPT_EN_32X         (1<<20) // x0 0000\r
 #define POPT_EN_PWM         (1<<21)\r
 #define POPT_PWM_IRQ_OPT    (1<<22)\r
 \r
index 1f89da9..5fa0b16 100644 (file)
@@ -88,7 +88,6 @@ static void do_timing_hacks_vb(void)
 static int PicoFrameHints(void)
 {
   struct PicoVideo *pv = &Pico.video;
-  int line_sample = Pico.m.pal ? 68 : 93;
   int vdp_slots = (Pico.video.reg[12] & 1) ? 18 : 16;
   int lines, y, lines_vis, skip;
   int vcnt_wrap, vcnt_adj;
@@ -150,23 +149,6 @@ static int PicoFrameHints(void)
       }
     }
 
-    // get samples from sound chips
-    if ((y == 224 || y == line_sample) && PicoIn.sndOut)
-    {
-      cycles = SekCyclesDone();
-
-      if (Pico.m.z80Run && !Pico.m.z80_reset && (PicoIn.opt&POPT_EN_Z80))
-        PicoSyncZ80(cycles);
-#ifdef PICO_CD
-      if (PicoIn.AHW & PAHW_MCD)
-        pcd_sync_s68k(cycles, 0);
-#endif
-#ifdef PICO_32X
-      p32x_sync_sh2s(cycles);
-#endif
-      PsndGetSamples(y);
-    }
-
     // Run scanline:
     Pico.t.m68c_line_start = Pico.t.m68c_aim;
     do_timing_hacks_as(pv, vdp_slots);
@@ -238,10 +220,6 @@ static int PicoFrameHints(void)
   p32x_start_blank();
 #endif
 
-  // get samples from sound chips
-  if (y == 224 && PicoIn.sndOut)
-    PsndGetSamples(y);
-
   // Run scanline:
   CPUS_RUN(CYCLES_M68K_LINE - CYCLES_M68K_VINT_LAG);
 
@@ -298,7 +276,7 @@ static int PicoFrameHints(void)
   pv->status |= ((pv->reg[1] >> 3) ^ SR_VB) & SR_VB; // forced blanking
 
   // last scanline
-  Pico.m.scanline = y;
+  Pico.m.scanline = y++;
   pv->v_counter = 0xff;
   pv->lwrite_cnt = 0;
 
@@ -337,6 +315,11 @@ static int PicoFrameHints(void)
 #ifdef PICO_32X
   p32x_sync_sh2s(cycles);
 #endif
+
+  // get samples from sound chips
+  if (PicoIn.sndOut)
+    PsndGetSamples(y);
+
   timers_cycle();
 
   pv->hint_cnt = hint;
index 0fc458e..d3da72c 100644 (file)
@@ -336,7 +336,7 @@ struct PicoMisc
   unsigned char  eeprom_cycle; // EEPROM cycle number\r
   unsigned char  eeprom_slave; // EEPROM slave word for X24C02 and better SRAMs\r
   unsigned char  eeprom_status;\r
-  unsigned char  status;       // rapid_ym2612, multi_ym_updates\r
+  unsigned char  pad1;         // was ym2612 status\r
   unsigned short dma_xfers;    // 18\r
   unsigned char  eeprom_wb[2]; // EEPROM latch/write buffer\r
   unsigned int  frame_count;   // 1c for movies and idle det\r
@@ -433,6 +433,8 @@ struct PicoSound
   int len_e_cnt;\r
   short dac_line;\r
   short psg_line;\r
+  unsigned int fm_mult;                 // samples per line in Q16\r
+  unsigned int fm_pos;                  // last FM position in Q16\r
 };\r
 \r
 // run tools/mkoffsets pico/pico_int_offs.h if you change these\r
@@ -872,9 +874,10 @@ PICO_INTERNAL void PsndReset(void);
 PICO_INTERNAL void PsndStartFrame(void);\r
 PICO_INTERNAL void PsndDoDAC(int line_to);\r
 PICO_INTERNAL void PsndDoPSG(int line_to);\r
+PICO_INTERNAL void PsndDoFM(int line_to);\r
 PICO_INTERNAL void PsndClear(void);\r
 PICO_INTERNAL void PsndGetSamples(int y);\r
-PICO_INTERNAL void PsndGetSamplesMS(void);\r
+PICO_INTERNAL void PsndGetSamplesMS(int y);\r
 \r
 // sms.c\r
 #ifndef NO_SMS\r
index 2800e20..b016f19 100644 (file)
@@ -320,16 +320,12 @@ void PicoFrameMS(void)
       }
     }
 
-    // 224 because of how it's done for MD...
-    if (y == 224 && PicoIn.sndOut)
-      PsndGetSamplesMS();
-
     cycles_aim += cycles_line;
     cycles_done += z80_run((cycles_aim - cycles_done) >> 8) << 8;
   }
 
-  if (PicoIn.sndOut && Pico.snd.psg_line < lines)
-    PsndDoPSG(lines - 1);
+  if (PicoIn.sndOut)
+    PsndGetSamplesMS(lines);
 }
 
 void PicoFrameDrawOnlyMS(void)
index 242cb37..4b4bbdd 100644 (file)
 #define MINOUT         (-32768)
 
 /* limitter */
-#define Limit16(val) { \
-       val -= (val >> 2); \
-       if ((short)val != val) val = (val < 0 ? MINOUT : MAXOUT); \
-}
+#define Limit16(val) \
+       if ((short)val != val) val = (val < 0 ? MINOUT : MAXOUT)
 
 int mix_32_to_16l_level;
 
 static struct iir2 { // 2-pole IIR
        int     x[2];           // sample buffer
        int     y[2];           // filter intermediates
+       int     i;
 } lfi2, rfi2;
 
 // NB ">>" rounds to -infinity, "/" to 0. To compensate the effect possibly use
index bb7388d..104b306 100644 (file)
@@ -400,6 +400,8 @@ m32_16l_st_l_no_unal2:
     ldmfd   sp!, {r4-r11,lr}
     bx      lr
 
+#endif /* __GP2X__ */
+
 .global mix_reset @ void
 mix_reset:
     ldr     r0, =filter
@@ -409,11 +411,7 @@ mix_reset:
     bx      lr
 
 .data
-    DCfilt  r4, r10
-    DCfilt  r5, r11
 filter:
     .ds     8
 
-#endif /* __GP2X__ */
-
 @ vim:filetype=armasm
index 30d4a07..f4cd424 100644 (file)
@@ -32,52 +32,17 @@ extern int *sn76496_regs;
 static void dac_recalculate(void)\r
 {\r
   int lines = Pico.m.pal ? 313 : 262;\r
-  int mid = Pico.m.pal ? 68 : 93;\r
-  int i, dac_cnt, pos, len;\r
+  int i, pos;\r
 \r
-  if (Pico.snd.len <= lines)\r
-  {\r
-    // shrinking algo\r
-    dac_cnt = -Pico.snd.len;\r
-    len=1; pos=0;\r
-    dac_info[225] = 1;\r
-\r
-    for(i=226; i != 225; i++)\r
-    {\r
-      if (i >= lines) i = 0;\r
-      if(dac_cnt < 0) {\r
-        pos++;\r
-        dac_cnt += lines;\r
-      }\r
-      dac_cnt -= Pico.snd.len;\r
-      dac_info[i] = pos;\r
-    }\r
-  }\r
-  else\r
+  pos = 0; // Q16\r
+\r
+  for(i = 0; i <= lines; i++)\r
   {\r
-    // stretching\r
-    dac_cnt = Pico.snd.len;\r
-    pos=0;\r
-    for(i = 225; i != 224; i++)\r
-    {\r
-      if (i >= lines) i = 0;\r
-      len=0;\r
-      while(dac_cnt >= 0) {\r
-        dac_cnt -= lines;\r
-        len++;\r
-      }\r
-      if (i == mid) // midpoint\r
-        while(pos+len < Pico.snd.len/2) {\r
-          dac_cnt -= lines;\r
-          len++;\r
-        }\r
-      dac_cnt += Pico.snd.len;\r
-      pos += len;\r
-      dac_info[i] = pos;\r
-    }\r
+    dac_info[i] = ((pos+(1<<15)) >> 16); // round to nearest\r
+    pos += Pico.snd.fm_mult;\r
   }\r
-  for (i = lines; i < sizeof(dac_info) / sizeof(dac_info[0]); i++)\r
-    dac_info[i] = dac_info[0];\r
+  for (i = lines+1; i < sizeof(dac_info) / sizeof(dac_info[0]); i++)\r
+    dac_info[i] = dac_info[i-1];\r
 }\r
 \r
 \r
@@ -95,6 +60,7 @@ void PsndRerate(int preserve_state)
 {\r
   void *state = NULL;\r
   int target_fps = Pico.m.pal ? 50 : 60;\r
+  int target_lines = Pico.m.pal ? 313 : 262;\r
 \r
   if (preserve_state) {\r
     state = malloc(0x204);\r
@@ -121,6 +87,9 @@ void PsndRerate(int preserve_state)
   Pico.snd.len_e_add = ((PicoIn.sndRate - Pico.snd.len * target_fps) << 16) / target_fps;\r
   Pico.snd.len_e_cnt = 0;\r
 \r
+  // samples per line\r
+  Pico.snd.fm_mult = 65536.0 * PicoIn.sndRate / (target_fps*target_lines);\r
+\r
   // recalculate dac info\r
   dac_recalculate();\r
 \r
@@ -149,8 +118,7 @@ PICO_INTERNAL void PsndStartFrame(void)
   }\r
 \r
   Pico.snd.dac_line = Pico.snd.psg_line = 0;\r
-  Pico.m.status &= ~1;\r
-  dac_info[224] = Pico.snd.len_use;\r
+  Pico.snd.fm_pos = 0;\r
 }\r
 \r
 PICO_INTERNAL void PsndDoDAC(int line_to)\r
@@ -159,9 +127,6 @@ PICO_INTERNAL void PsndDoDAC(int line_to)
   int dout = ym2612.dacout;\r
   int line_from = Pico.snd.dac_line;\r
 \r
-  if (line_to >= 313)\r
-    line_to = 312;\r
-\r
   pos  = dac_info[line_from];\r
   pos1 = dac_info[line_to + 1];\r
   len = pos1 - pos;\r
@@ -188,14 +153,9 @@ PICO_INTERNAL void PsndDoPSG(int line_to)
   int pos, pos1, len;\r
   int stereo = 0;\r
 \r
-  if (line_to >= 313)\r
-    line_to = 312;\r
-\r
   pos  = dac_info[line_from];\r
   pos1 = dac_info[line_to + 1];\r
   len = pos1 - pos;\r
-  //elprintf(EL_STATUS, "%3d %3d %3d %3d %3d",\r
-  //  pos, pos1, len, line_from, line_to);\r
   if (len <= 0)\r
     return;\r
 \r
@@ -211,6 +171,34 @@ PICO_INTERNAL void PsndDoPSG(int line_to)
   SN76496Update(PicoIn.sndOut + pos, len, stereo);\r
 }\r
 \r
+PICO_INTERNAL void PsndDoFM(int line_to)\r
+{\r
+  int pos, len;\r
+  int stereo = 0;\r
+\r
+  // Q16, number of samples to fill in buffer\r
+  len = ((line_to-1) * Pico.snd.fm_mult) - Pico.snd.fm_pos;\r
+\r
+  // don't do this too often (no more than 256 per sec)\r
+  if (len >> 16 <= PicoIn.sndRate >> 9)\r
+    return;\r
+\r
+  // update position and calculate buffer offset and length\r
+  pos = Pico.snd.fm_pos >> 16;\r
+  Pico.snd.fm_pos += len;\r
+  len = (Pico.snd.fm_pos >> 16) - pos;\r
+\r
+  // fill buffer\r
+  if (PicoIn.opt & POPT_EN_STEREO) {\r
+    stereo = 1;\r
+    pos <<= 1;\r
+  }\r
+  if (PicoIn.opt & POPT_EN_FM)\r
+    YM2612UpdateOne(PsndBuffer + pos, len, stereo, 1);\r
+  else\r
+    memset32(PsndBuffer + pos, 0, len<<stereo);\r
+}\r
+\r
 // cdda\r
 static void cdda_raw_update(int *buffer, int length)\r
 {\r
@@ -275,11 +263,12 @@ PICO_INTERNAL void PsndClear(void)
 \r
 static int PsndRender(int offset, int length)\r
 {\r
-  int  buf32_updated = 0;\r
-  int *buf32 = PsndBuffer+offset;\r
+  int *buf32;\r
   int stereo = (PicoIn.opt & 8) >> 3;\r
+  int fmlen = (Pico.snd.fm_pos >> 16) - offset;\r
 \r
   offset <<= stereo;\r
+  buf32 = PsndBuffer+offset;\r
 \r
   pprof_start(sound);\r
 \r
@@ -288,14 +277,15 @@ static int PsndRender(int offset, int length)
     return length;\r
   }\r
 \r
-  // Add in the stereo FM buffer\r
-  if (PicoIn.opt & POPT_EN_FM) {\r
-    buf32_updated = YM2612UpdateOne(buf32, length, stereo, 1);\r
-  } else\r
-    memset32(buf32, 0, length<<stereo);\r
-\r
-//printf("active_chs: %02x\n", buf32_updated);\r
-  (void)buf32_updated;\r
+  // Add in parts of the FM buffer not yet done\r
+  if (length-fmlen > 0) {\r
+    int *fmbuf = buf32 + (fmlen << stereo);\r
+    if (PicoIn.opt & POPT_EN_FM)\r
+      YM2612UpdateOne(fmbuf, length-fmlen, stereo, 1);\r
+    else\r
+      memset32(fmbuf, 0, (length-fmlen)<<stereo);\r
+    Pico.snd.fm_pos += (length-fmlen)<<16;\r
+  }\r
 \r
   // CD: PCM sound\r
   if (PicoIn.AHW & PAHW_MCD) {\r
@@ -327,7 +317,6 @@ static int PsndRender(int offset, int length)
   return length;\r
 }\r
 \r
-// to be called on 224 or line_sample scanlines only\r
 PICO_INTERNAL void PsndGetSamples(int y)\r
 {\r
   static int curr_pos = 0;\r
@@ -336,33 +325,20 @@ PICO_INTERNAL void PsndGetSamples(int y)
     PsndDoDAC(y - 1);\r
   PsndDoPSG(y - 1);\r
 \r
-  if (y == 224)\r
-  {\r
-    if (Pico.m.status & 2)\r
-         curr_pos += PsndRender(curr_pos, Pico.snd.len-Pico.snd.len/2);\r
-    else curr_pos  = PsndRender(0, Pico.snd.len_use);\r
-    if (Pico.m.status & 1)\r
-         Pico.m.status |=  2;\r
-    else Pico.m.status &= ~2;\r
-    if (PicoIn.writeSound)\r
-      PicoIn.writeSound(curr_pos * ((PicoIn.opt & POPT_EN_STEREO) ? 4 : 2));\r
-    // clear sound buffer\r
-    PsndClear();\r
-    Pico.snd.dac_line = 224;\r
-    dac_info[224] = 0;\r
-  }\r
-  else if (Pico.m.status & 3) {\r
-    Pico.m.status |=  2;\r
-    Pico.m.status &= ~1;\r
-    curr_pos = PsndRender(0, Pico.snd.len/2);\r
-  }\r
+  curr_pos  = PsndRender(0, Pico.snd.len_use);\r
+\r
+  if (PicoIn.writeSound)\r
+    PicoIn.writeSound(curr_pos * ((PicoIn.opt & POPT_EN_STEREO) ? 4 : 2));\r
+  // clear sound buffer\r
+  PsndClear();\r
+  Pico.snd.dac_line = y;\r
 }\r
 \r
-PICO_INTERNAL void PsndGetSamplesMS(void)\r
+PICO_INTERNAL void PsndGetSamplesMS(int y)\r
 {\r
   int length = Pico.snd.len_use;\r
 \r
-  PsndDoPSG(223);\r
+  PsndDoPSG(y - 1);\r
 \r
   // upmix to "stereo" if needed\r
   if (PicoIn.opt & POPT_EN_STEREO) {\r
@@ -374,8 +350,6 @@ PICO_INTERNAL void PsndGetSamplesMS(void)
   if (PicoIn.writeSound != NULL)\r
     PicoIn.writeSound(length * ((PicoIn.opt & POPT_EN_STEREO) ? 4 : 2));\r
   PsndClear();\r
-\r
-  dac_info[224] = 0;\r
 }\r
 \r
 // vim:shiftwidth=2:ts=2:expandtab\r
index 5640852..23f106d 100644 (file)
@@ -7,6 +7,8 @@
 ** document it ("proprietary") and tells to write 0 to SSG-EG control register.\r
 **\r
 ** updated with fixes from mame 0.216 (file version 1.5.1) (kub)\r
+** SSG-EG readded from GenPlus (kub)\r
+** linear sample interpolation for chip to output rate adaption (kub)\r
 */\r
 \r
 /*\r
@@ -174,16 +176,6 @@ void memset32(int *dest, int c, int count);
 \r
 #define EG_TIMER_OVERFLOW (3*(1<<EG_SH)) /* envelope generator timer overflows every 3 samples (on real chip) */\r
 \r
-#define MAXOUT         (+32767)\r
-#define MINOUT         (-32768)\r
-\r
-/* limitter */\r
-#define Limit(val, max,min) { \\r
-       if ( val > max )      val = max; \\r
-       else if ( val < min ) val = min; \\r
-}\r
-\r
-\r
 /*  TL_TAB_LEN is calculated as:\r
 *   13 - sinus amplitude bits     (Y axis)\r
 *   2  - sinus sign bit           (Y axis)\r
@@ -289,8 +281,8 @@ O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
 O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),\r
 \r
 /* rates 00-11 */\r
-O(18),O(18),O( 0),O( 0),\r
-O( 0),O( 0),O( 2),O( 2),\r
+O(18),O(18),O( 2),O( 3),\r
+O( 0),O( 1),O( 2),O( 3),\r
 O( 0),O( 1),O( 2),O( 3),\r
 O( 0),O( 1),O( 2),O( 3),\r
 O( 0),O( 1),O( 2),O( 3),\r
@@ -554,6 +546,13 @@ INLINE void set_timers( int v )
                ym2612.OPN.ST.status &= ~1;\r
 }\r
 \r
+INLINE void recalc_volout(FM_SLOT *SLOT)\r
+{\r
+       INT16 vol_out = SLOT->volume;\r
+       if ((SLOT->ssg&0x0c) == 0x0c)\r
+               vol_out = (0x200 - SLOT->volume) & MAX_ATT_INDEX;\r
+       SLOT->vol_out = vol_out + SLOT->tl;\r
+}\r
 \r
 INLINE void FM_KEYON(int c , int s )\r
 {\r
@@ -562,13 +561,15 @@ INLINE void FM_KEYON(int c , int s )
        {\r
                SLOT->key = 1;\r
                SLOT->phase = 0;                /* restart Phase Generator */\r
+               SLOT->ssg ^= SLOT->ssgn;\r
+               SLOT->ssgn = 0;\r
+               SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC;\r
                if (SLOT->ar + SLOT->ksr < 32+62) {\r
-                       SLOT->state = (SLOT->volume > MIN_ATT_INDEX) ? EG_ATT :\r
-                               ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC);\r
+                       if (SLOT->volume > MIN_ATT_INDEX) SLOT->state = EG_ATT;\r
                } else {\r
                        SLOT->volume = MIN_ATT_INDEX;\r
-                       SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC;\r
                }\r
+               recalc_volout(SLOT);\r
                ym2612.slot_mask |= (1<<s) << (c*4);\r
        }\r
 }\r
@@ -579,8 +580,18 @@ INLINE void FM_KEYOFF(int c , int s )
        if( SLOT->key )\r
        {\r
                SLOT->key = 0;\r
-               if (SLOT->state>EG_REL)\r
+               if (SLOT->state>EG_REL) {\r
                        SLOT->state = EG_REL;/* phase -> Release */\r
+                       if (SLOT->ssg&0x08) {\r
+                               if (SLOT->ssg&0x04)\r
+                                       SLOT->volume = (0x200 - SLOT->volume);\r
+                               if (SLOT->volume >= 0x200) {\r
+                                       SLOT->volume = MAX_ATT_INDEX;\r
+                                       SLOT->state  = EG_OFF;\r
+                               }\r
+                       }\r
+               }\r
+               SLOT->vol_out = SLOT->volume + SLOT->tl;\r
        }\r
 }\r
 \r
@@ -597,12 +608,15 @@ INLINE void set_det_mul(FM_CH *CH, FM_SLOT *SLOT, int v)
 INLINE void set_tl(FM_SLOT *SLOT, int v)\r
 {\r
        SLOT->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */\r
+       if (SLOT->state > EG_REL)\r
+               recalc_volout(SLOT);\r
 }\r
 \r
 /* set attack rate & key scale  */\r
 INLINE void set_ar_ksr(FM_CH *CH, FM_SLOT *SLOT, int v)\r
 {\r
        UINT8 old_KSR = SLOT->KSR;\r
+       int eg_sh_ar, eg_sel_ar;\r
 \r
        SLOT->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;\r
 \r
@@ -611,24 +625,20 @@ INLINE void set_ar_ksr(FM_CH *CH, FM_SLOT *SLOT, int v)
        {\r
                CH->SLOT[SLOT1].Incr=-1;\r
        }\r
+\r
+       /* refresh Attack rate */\r
+       if ((SLOT->ar + SLOT->ksr) < 32+62)\r
+       {\r
+               eg_sh_ar  = eg_rate_shift [SLOT->ar  + SLOT->ksr ];\r
+               eg_sel_ar = eg_rate_select[SLOT->ar  + SLOT->ksr ];\r
+       }\r
        else\r
        {\r
-               int eg_sh_ar, eg_sel_ar;\r
-\r
-               /* refresh Attack rate */\r
-               if ((SLOT->ar + SLOT->ksr) < 32+62)\r
-               {\r
-                       eg_sh_ar  = eg_rate_shift [SLOT->ar  + SLOT->ksr ];\r
-                       eg_sel_ar = eg_rate_select[SLOT->ar  + SLOT->ksr ];\r
-               }\r
-               else\r
-               {\r
-                       eg_sh_ar  = 0;\r
-                       eg_sel_ar = 18;\r
-               }\r
-\r
-               SLOT->eg_pack_ar = eg_inc_pack[eg_sel_ar] | (eg_sh_ar<<24);\r
+               eg_sh_ar  = 0;\r
+               eg_sel_ar = 18;\r
        }\r
+\r
+       SLOT->eg_pack_ar = eg_inc_pack[eg_sel_ar] | (eg_sh_ar<<24);\r
 }\r
 \r
 /* set decay rate */\r
@@ -750,7 +760,7 @@ INLINE int advance_lfo(int lfo_ampm, UINT32 lfo_cnt_old, UINT32 lfo_cnt)
        return lfo_ampm;\r
 }\r
 \r
-INLINE void update_eg_phase(UINT16 *vol_out, FM_SLOT *SLOT, UINT32 eg_cnt)\r
+INLINE void update_eg_phase(FM_SLOT *SLOT, UINT32 eg_cnt)\r
 {\r
        INT32 volume = SLOT->volume;\r
        UINT32 pack = SLOT->eg_pack[SLOT->state - 1];\r
@@ -763,44 +773,113 @@ INLINE void update_eg_phase(UINT16 *vol_out, FM_SLOT *SLOT, UINT32 eg_cnt)
        eg_inc_val = pack >> ((eg_cnt >> shift) & 7) * 3;\r
        eg_inc_val = (1 << (eg_inc_val & 7)) >> 1;\r
 \r
-       switch (SLOT->state)\r
-       {\r
-       case EG_ATT:            /* attack phase */\r
-               volume += ( ~volume * eg_inc_val ) >> 4;\r
-               if ( volume <= MIN_ATT_INDEX )\r
+       if (SLOT->ssg&0x08) {\r
+               switch (SLOT->state)\r
                {\r
-                       volume = MIN_ATT_INDEX;\r
-                       SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS: EG_DEC;\r
-               }\r
-               break;\r
+               case EG_ATT:    /* attack phase */\r
+                       volume += ( ~volume * eg_inc_val ) >> 4;\r
+                       if ( volume <= MIN_ATT_INDEX )\r
+                       {\r
+                               volume = MIN_ATT_INDEX;\r
+                               SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS: EG_DEC;\r
+                       }\r
+                       break;\r
 \r
-       case EG_DEC:    /* decay phase */\r
-               volume += eg_inc_val;\r
-               if ( volume >= (INT32) SLOT->sl )\r
-                       SLOT->state = EG_SUS;\r
-               break;\r
+               case EG_DEC:    /* decay phase */\r
+                       if (volume < 0x200)\r
+                               volume += 4*eg_inc_val;\r
+                       if ( volume >= (INT32) SLOT->sl )\r
+                               SLOT->state = EG_SUS;\r
+                       break;\r
 \r
-       case EG_SUS:    /* sustain phase */\r
-               volume += eg_inc_val;\r
-               if ( volume >= MAX_ATT_INDEX )\r
-               {\r
-                       volume = MAX_ATT_INDEX;\r
-                       /* do not change SLOT->state (verified on real chip) */\r
+               case EG_SUS:    /* sustain phase */\r
+                       if (volume < 0x200)\r
+                               volume += 4*eg_inc_val;\r
+                       break;\r
+\r
+               case EG_REL:    /* release phase */\r
+                       if (volume < 0x200)\r
+                               volume += 4*eg_inc_val;\r
+                       if ( volume >= 0x200 )\r
+                       {\r
+                               volume = MAX_ATT_INDEX;\r
+                               SLOT->state = EG_OFF;\r
+                       }\r
+                       break;\r
                }\r
-               break;\r
 \r
-       case EG_REL:    /* release phase */\r
-               volume += eg_inc_val;\r
-               if ( volume >= MAX_ATT_INDEX )\r
+               SLOT->vol_out = volume + SLOT->tl;\r
+               if ((SLOT->ssg&0x04) && (SLOT->state > EG_REL))\r
+                       SLOT->vol_out = ((0x200 - volume) & MAX_ATT_INDEX) + SLOT->tl;\r
+       } else {\r
+               switch (SLOT->state)\r
                {\r
-                       volume = MAX_ATT_INDEX;\r
-                       SLOT->state = EG_OFF;\r
+               case EG_ATT:            /* attack phase */\r
+                       volume += ( ~volume * eg_inc_val ) >> 4;\r
+                       if ( volume <= MIN_ATT_INDEX )\r
+                       {\r
+                               volume = MIN_ATT_INDEX;\r
+                               SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS: EG_DEC;\r
+                       }\r
+                       break;\r
+\r
+               case EG_DEC:    /* decay phase */\r
+                       volume += eg_inc_val;\r
+                       if ( volume >= (INT32) SLOT->sl )\r
+                               SLOT->state = EG_SUS;\r
+                       break;\r
+\r
+               case EG_SUS:    /* sustain phase */\r
+                       volume += eg_inc_val;\r
+                       if ( volume >= MAX_ATT_INDEX )\r
+                       {\r
+                               volume = MAX_ATT_INDEX;\r
+                               /* do not change SLOT->state (verified on real chip) */\r
+                       }\r
+                       break;\r
+\r
+               case EG_REL:    /* release phase */\r
+                       volume += eg_inc_val;\r
+                       if ( volume >= MAX_ATT_INDEX )\r
+                       {\r
+                               volume = MAX_ATT_INDEX;\r
+                               SLOT->state = EG_OFF;\r
+                       }\r
+                       break;\r
                }\r
-               break;\r
-       }\r
 \r
+               SLOT->vol_out = volume + SLOT->tl;\r
+       }\r
        SLOT->volume = volume;\r
-       *vol_out = SLOT->tl + volume; /* tl is 7bit<<3, volume 0-1023 (0-2039 total) */\r
+}\r
+\r
+INLINE void update_ssg_eg_phase(FM_SLOT *SLOT)\r
+{\r
+       if (SLOT->ssg&0x01) {\r
+               if (SLOT->ssg&0x02) {\r
+                       SLOT->ssg ^= SLOT->ssgn ^ 4;\r
+                       SLOT->ssgn = 4;\r
+               }\r
+\r
+               if (SLOT->state != EG_ATT && !(SLOT->ssg&0x04))\r
+                       SLOT->volume  = MAX_ATT_INDEX;\r
+       } else {\r
+               if (SLOT->ssg&0x02) {\r
+                       SLOT->ssg ^= 4;\r
+                       SLOT->ssgn ^= 4;\r
+               } else\r
+                       SLOT->phase = 0;\r
+\r
+               if (SLOT->state != EG_ATT) {\r
+                       SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC;\r
+                       if (SLOT->ar + SLOT->ksr < 32+62) {\r
+                               if (SLOT->volume > MIN_ATT_INDEX) SLOT->state = EG_ATT;\r
+                       } else {\r
+                               SLOT->volume = MIN_ATT_INDEX;\r
+                       }\r
+               }\r
+       }\r
+       recalc_volout(SLOT);\r
 }\r
 #endif\r
 \r
@@ -846,6 +925,16 @@ static void chan_render_loop(chan_rend_context *ct, int *buffer, int length)
        {\r
                int smp = 0;            /* produced sample */\r
                unsigned int eg_out, eg_out2, eg_out4;\r
+               FM_SLOT *SLOT;\r
+\r
+               SLOT = &ct->CH->SLOT[SLOT1];\r
+               if ((SLOT->ssg&0x08) && SLOT->state > EG_REL && SLOT->volume >= 0x200) update_ssg_eg_phase(SLOT);\r
+               SLOT = &ct->CH->SLOT[SLOT2];\r
+               if ((SLOT->ssg&0x08) && SLOT->state > EG_REL && SLOT->volume >= 0x200) update_ssg_eg_phase(SLOT);\r
+               SLOT = &ct->CH->SLOT[SLOT3];\r
+               if ((SLOT->ssg&0x08) && SLOT->state > EG_REL && SLOT->volume >= 0x200) update_ssg_eg_phase(SLOT);\r
+               SLOT = &ct->CH->SLOT[SLOT4];\r
+               if ((SLOT->ssg&0x08) && SLOT->state > EG_REL && SLOT->volume >= 0x200) update_ssg_eg_phase(SLOT);\r
 \r
                if (ct->pack & 8) { /* LFO enabled ? (test Earthworm Jim in between demo 1 and 2) */\r
                        ct->pack = (ct->pack&0xffff) | (advance_lfo(ct->pack >> 16, ct->lfo_cnt, ct->lfo_cnt + ct->lfo_inc) << 16);\r
@@ -857,12 +946,58 @@ static void chan_render_loop(chan_rend_context *ct, int *buffer, int length)
                {\r
                        ct->eg_timer -= EG_TIMER_OVERFLOW;\r
                        ct->eg_cnt++;\r
-\r
-                       if (ct->CH->SLOT[SLOT1].state != EG_OFF) update_eg_phase(&ct->vol_out1, &ct->CH->SLOT[SLOT1], ct->eg_cnt);\r
-                       if (ct->CH->SLOT[SLOT2].state != EG_OFF) update_eg_phase(&ct->vol_out2, &ct->CH->SLOT[SLOT2], ct->eg_cnt);\r
-                       if (ct->CH->SLOT[SLOT3].state != EG_OFF) update_eg_phase(&ct->vol_out3, &ct->CH->SLOT[SLOT3], ct->eg_cnt);\r
-                       if (ct->CH->SLOT[SLOT4].state != EG_OFF) update_eg_phase(&ct->vol_out4, &ct->CH->SLOT[SLOT4], ct->eg_cnt);\r
+                       if (ct->eg_cnt >= 4096) ct->eg_cnt = 1;\r
+\r
+                       SLOT = &ct->CH->SLOT[SLOT1];\r
+                       SLOT->vol_ipol = SLOT->vol_out;\r
+                       if (SLOT->state != EG_OFF) update_eg_phase(SLOT, ct->eg_cnt);\r
+                       SLOT = &ct->CH->SLOT[SLOT2];\r
+                       SLOT->vol_ipol = SLOT->vol_out;\r
+                       if (SLOT->state != EG_OFF) update_eg_phase(SLOT, ct->eg_cnt);\r
+                       SLOT = &ct->CH->SLOT[SLOT3];\r
+                       SLOT->vol_ipol = SLOT->vol_out;\r
+                       if (SLOT->state != EG_OFF) update_eg_phase(SLOT, ct->eg_cnt);\r
+                       SLOT = &ct->CH->SLOT[SLOT4];\r
+                       SLOT->vol_ipol = SLOT->vol_out;\r
+                       if (SLOT->state != EG_OFF) update_eg_phase(SLOT, ct->eg_cnt);\r
+               }\r
+#if 0\r
+               UINT32 ifrac0 = ct->eg_timer / (EG_TIMER_OVERFLOW>>EG_SH);\r
+               UINT32 ifrac1 = (1<<EG_SH) - ifrac0;\r
+               SLOT = &ct->CH->SLOT[SLOT1];\r
+               ct->vol_out1 = (SLOT->vol_ipol*ifrac1 + SLOT->vol_out*ifrac0) >> EG_SH;\r
+               SLOT = &ct->CH->SLOT[SLOT2];\r
+               ct->vol_out2 = (SLOT->vol_ipol*ifrac1 + SLOT->vol_out*ifrac0) >> EG_SH;\r
+               SLOT = &ct->CH->SLOT[SLOT3];\r
+               ct->vol_out3 = (SLOT->vol_ipol*ifrac1 + SLOT->vol_out*ifrac0) >> EG_SH;\r
+               SLOT = &ct->CH->SLOT[SLOT4];\r
+               ct->vol_out4 = (SLOT->vol_ipol*ifrac1 + SLOT->vol_out*ifrac0) >> EG_SH;\r
+#else\r
+               switch (ct->eg_timer >> EG_SH)\r
+               {\r
+                       case 0:\r
+                               ct->vol_out1 =  ct->CH->SLOT[SLOT1].vol_ipol;\r
+                               ct->vol_out2 =  ct->CH->SLOT[SLOT2].vol_ipol;\r
+                               ct->vol_out3 =  ct->CH->SLOT[SLOT3].vol_ipol;\r
+                               ct->vol_out4 =  ct->CH->SLOT[SLOT4].vol_ipol;\r
+                               break;\r
+                       case (EG_TIMER_OVERFLOW>>EG_SH)-1:\r
+                               ct->vol_out1 =  ct->CH->SLOT[SLOT1].vol_out;\r
+                               ct->vol_out2 =  ct->CH->SLOT[SLOT2].vol_out;\r
+                               ct->vol_out3 =  ct->CH->SLOT[SLOT3].vol_out;\r
+                               ct->vol_out4 =  ct->CH->SLOT[SLOT4].vol_out;\r
+                               break;\r
+                       default:\r
+                               ct->vol_out1 =  (ct->CH->SLOT[SLOT1].vol_ipol +\r
+                                       ct->CH->SLOT[SLOT1].vol_out) >> 1;\r
+                               ct->vol_out2 =  (ct->CH->SLOT[SLOT2].vol_ipol +\r
+                                       ct->CH->SLOT[SLOT2].vol_out) >> 1;\r
+                               ct->vol_out3 =  (ct->CH->SLOT[SLOT3].vol_ipol +\r
+                                       ct->CH->SLOT[SLOT3].vol_out) >> 1;\r
+                               ct->vol_out4 =  (ct->CH->SLOT[SLOT4].vol_ipol +\r
+                                       ct->CH->SLOT[SLOT4].vol_out) >> 1;\r
                }\r
+#endif\r
 \r
                if (ct->pack & 4) continue; /* output disabled */\r
 \r
@@ -892,7 +1027,7 @@ static void chan_render_loop(chan_rend_context *ct, int *buffer, int length)
                        if (ct->pack & (1<<(SLOT4+8))) eg_out4 += add;\r
                }\r
 \r
-               switch( ct->CH->ALGO )\r
+               switch( ct->algo&0x7 )\r
                {\r
                        case 0:\r
                        {\r
@@ -1086,6 +1221,33 @@ static void chan_render_finish(void)
        ym2612.OPN.lfo_cnt = crct.lfo_cnt;\r
 }\r
 \r
+static UINT32 update_lfo_phase(FM_SLOT *SLOT, UINT32 block_fnum)\r
+{\r
+       UINT32 fnum_lfo;\r
+       INT32  lfo_fn_table_index_offset;\r
+       UINT8  blk;\r
+       UINT32 fn;\r
+       int fc,fdt;\r
+\r
+       fnum_lfo   = ((block_fnum & 0x7f0) >> 4) * 32 * 8;\r
+       lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + crct.CH->pms + ((crct.pack>>16)&0xff) ];\r
+       if (lfo_fn_table_index_offset)  /* LFO phase modulation active */\r
+       {\r
+               block_fnum = block_fnum*2 + lfo_fn_table_index_offset;\r
+               blk = (block_fnum&0x7000) >> 12;\r
+               fn  = block_fnum & 0xfff;\r
+\r
+               /* phase increment counter */\r
+               fc = (fn_table[fn]>>(7-blk));\r
+\r
+               fdt = fc + SLOT->DT[crct.CH->kcode];\r
+               if (fdt < 0) fdt += fn_table[0x7ff*2] >> 2;\r
+\r
+               return (fdt * SLOT->mul) >> 1;\r
+       } else\r
+               return SLOT->Incr;\r
+}\r
+\r
 static int chan_render(int *buffer, int length, int c, UINT32 flags) // flags: stereo, ?, disabled, ?, pan_r, pan_l\r
 {\r
        crct.CH = &ym2612.CH[c];\r
@@ -1114,58 +1276,22 @@ static int chan_render(int *buffer, int length, int c, UINT32 flags) // flags: s
        crct.phase3 = crct.CH->SLOT[SLOT3].phase;\r
        crct.phase4 = crct.CH->SLOT[SLOT4].phase;\r
 \r
-       /* current output from EG circuit (without AM from LFO) */\r
-       crct.vol_out1 = crct.CH->SLOT[SLOT1].tl + ((UINT32)crct.CH->SLOT[SLOT1].volume);\r
-       crct.vol_out2 = crct.CH->SLOT[SLOT2].tl + ((UINT32)crct.CH->SLOT[SLOT2].volume);\r
-       crct.vol_out3 = crct.CH->SLOT[SLOT3].tl + ((UINT32)crct.CH->SLOT[SLOT3].volume);\r
-       crct.vol_out4 = crct.CH->SLOT[SLOT4].tl + ((UINT32)crct.CH->SLOT[SLOT4].volume);\r
-\r
        crct.op1_out = crct.CH->op1_out;\r
        crct.algo = crct.CH->ALGO & 7;\r
 \r
-       if(crct.CH->pms)\r
+       if(crct.CH->pms && (ym2612.OPN.ST.mode & 0xC0) && c == 2) {\r
+               /* 3 slot mode */\r
+               crct.incr1 = update_lfo_phase(&crct.CH->SLOT[SLOT1], ym2612.OPN.SL3.block_fnum[1]);\r
+               crct.incr2 = update_lfo_phase(&crct.CH->SLOT[SLOT2], ym2612.OPN.SL3.block_fnum[2]);\r
+               crct.incr3 = update_lfo_phase(&crct.CH->SLOT[SLOT3], ym2612.OPN.SL3.block_fnum[0]);\r
+               crct.incr4 = update_lfo_phase(&crct.CH->SLOT[SLOT4], crct.CH->block_fnum);\r
+       }\r
+       else if(crct.CH->pms)\r
        {\r
-               /* add support for 3 slot mode */\r
-               UINT32 block_fnum = crct.CH->block_fnum;\r
-\r
-               UINT32 fnum_lfo   = ((block_fnum & 0x7f0) >> 4) * 32 * 8;\r
-               INT32  lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + crct.CH->pms + ((crct.pack>>16)&0xff) ];\r
-\r
-               if (lfo_fn_table_index_offset)  /* LFO phase modulation active */\r
-               {\r
-                       UINT8  blk;\r
-                       UINT32 fn;\r
-                       int kc,fc,fdt;\r
-\r
-                       block_fnum = block_fnum*2 + lfo_fn_table_index_offset;\r
-                       blk = (block_fnum&0x7000) >> 12;\r
-                       fn  = block_fnum & 0xfff;\r
-\r
-                       /* keyscale code */\r
-                       kc = (blk<<2) | opn_fktable[(fn >> 7) & 0xf];\r
-                       /* phase increment counter */\r
-                       fc = (fn_table[fn]>>(7-blk));\r
-\r
-                       fdt = fc + crct.CH->SLOT[SLOT1].DT[kc];\r
-                       if (fdt < 0) fdt += fn_table[0x7ff*2] >> 2;\r
-                       crct.incr1 = (fdt*crct.CH->SLOT[SLOT1].mul) >> 1;\r
-                       fdt = fc + crct.CH->SLOT[SLOT2].DT[kc];\r
-                       if (fdt < 0) fdt += fn_table[0x7ff*2] >> 2;\r
-                       crct.incr2 = (fdt*crct.CH->SLOT[SLOT2].mul) >> 1;\r
-                       fdt = fc + crct.CH->SLOT[SLOT3].DT[kc];\r
-                       if (fdt < 0) fdt += fn_table[0x7ff*2] >> 2;\r
-                       crct.incr3 = (fdt*crct.CH->SLOT[SLOT3].mul) >> 1;\r
-                       fdt = fc + crct.CH->SLOT[SLOT4].DT[kc];\r
-                       if (fdt < 0) fdt += fn_table[0x7ff*2] >> 2;\r
-                       crct.incr4 = (fdt*crct.CH->SLOT[SLOT4].mul) >> 1;\r
-               }\r
-               else    /* LFO phase modulation  = zero */\r
-               {\r
-                       crct.incr1 = crct.CH->SLOT[SLOT1].Incr;\r
-                       crct.incr2 = crct.CH->SLOT[SLOT2].Incr;\r
-                       crct.incr3 = crct.CH->SLOT[SLOT3].Incr;\r
-                       crct.incr4 = crct.CH->SLOT[SLOT4].Incr;\r
-               }\r
+               crct.incr1 = update_lfo_phase(&crct.CH->SLOT[SLOT1], crct.CH->block_fnum);\r
+               crct.incr2 = update_lfo_phase(&crct.CH->SLOT[SLOT2], crct.CH->block_fnum);\r
+               crct.incr3 = update_lfo_phase(&crct.CH->SLOT[SLOT3], crct.CH->block_fnum);\r
+               crct.incr4 = update_lfo_phase(&crct.CH->SLOT[SLOT4], crct.CH->block_fnum);\r
        }\r
        else    /* no LFO phase modulation */\r
        {\r
@@ -1297,8 +1423,13 @@ static void reset_channels(FM_CH *CH)
                CH[c].fc = 0;\r
                for(s = 0 ; s < 4 ; s++ )\r
                {\r
+                       CH[c].SLOT[s].Incr = -1;\r
+                       CH[c].SLOT[s].key = 0;\r
+                       CH[c].SLOT[s].phase = 0;\r
+                       CH[c].SLOT[s].ssg = CH[c].SLOT[s].ssgn = 0;\r
                        CH[c].SLOT[s].state= EG_OFF;\r
                        CH[c].SLOT[s].volume = MAX_ATT_INDEX;\r
+                       CH[c].SLOT[s].vol_out = MAX_ATT_INDEX;\r
                }\r
                CH[c].mem_value = CH[c].op1_out = 0;\r
        }\r
@@ -1503,8 +1634,10 @@ static int OPNWriteReg(int r, int v)
                break;\r
 \r
        case 0x90:      /* SSG-EG */\r
-               // removed.\r
-               ret = 0;\r
+               SLOT->ssg =  v&0x0f;\r
+               SLOT->ssg ^= SLOT->ssgn;\r
+               if (SLOT->state > EG_REL)\r
+                       recalc_volout(SLOT);\r
                break;\r
 \r
        case 0xa0:\r
index bbe6b1a..3a1ea7a 100644 (file)
@@ -53,6 +53,11 @@ typedef struct
                };\r
                UINT32 eg_pack[4];\r
        };\r
+\r
+       UINT8   ssg;            /* 0x30 SSG-EG waveform */\r
+       UINT8   ssgn;\r
+       UINT16  vol_out;        /* 0x32 current output from EG (without LFO) */\r
+       UINT16  vol_ipol;       /* 0x34 interpolator memory */\r
 } FM_SLOT;\r
 \r
 \r
@@ -176,21 +181,22 @@ int  YM2612PicoStateLoad2(int *tat, int *tbt);
 #else\r
 /* GP2X specific */\r
 #include "../../platform/gp2x/940ctl.h"\r
-#define YM2612Init(baseclock,rate) { \\r
+#define YM2612Init(baseclock,rate) do { \\r
        if (PicoIn.opt&POPT_EXT_FM) YM2612Init_940(baseclock, rate); \\r
        else               YM2612Init_(baseclock, rate); \\r
-}\r
-#define YM2612ResetChip() { \\r
+} while (0)\r
+#define YM2612ResetChip() do { \\r
        if (PicoIn.opt&POPT_EXT_FM) YM2612ResetChip_940(); \\r
        else               YM2612ResetChip_(); \\r
-}\r
-#define YM2612UpdateOne(buffer,length,stereo,is_buf_empty) \\r
+} while (0)\r
+#define YM2612UpdateOne(buffer,length,stereo,is_buf_empty) do { \\r
        (PicoIn.opt&POPT_EXT_FM) ? YM2612UpdateOne_940(buffer, length, stereo, is_buf_empty) : \\r
-                               YM2612UpdateOne_(buffer, length, stereo, is_buf_empty);\r
-#define YM2612PicoStateLoad() { \\r
+                               YM2612UpdateOne_(buffer, length, stereo, is_buf_empty); \\r
+} while (0)\r
+#define YM2612PicoStateLoad() do { \\r
        if (PicoIn.opt&POPT_EXT_FM) YM2612PicoStateLoad_940(); \\r
        else               YM2612PicoStateLoad_(); \\r
-}\r
+} while (0)\r
 #endif /* __GP2X__ */\r
 \r
 \r
index 9b80792..86e5f1c 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * PicoDrive
  * (C) notaz, 2006
+ * (C) kub, 2020       added SSG-EG and simple output rate interpolation
  *
  * This work is licensed under the terms of MAME license.
  * See COPYING file in the top-level directory.
@@ -18,7 +19,7 @@
 .equiv SLOT2, 2
 .equiv SLOT3, 1
 .equiv SLOT4, 3
-.equiv SLOT_STRUCT_SIZE, 0x30
+.equiv SLOT_STRUCT_SIZE, 0x38
 
 .equiv TL_TAB_LEN, 0x1A00
 
 .equiv EG_REL, 1
 .equiv EG_OFF, 0
 
-.equiv EG_SH,                    16             @ 16.16 fixed point (envelope generator timing)
+.equiv EG_SH,            16             @ 16.16 fixed point (envelope generator timing)
 .equiv EG_TIMER_OVERFLOW, (3*(1<<EG_SH)) @ envelope generator timer overflows every 3 samples (on real chip)
 .equiv LFO_SH,            24  /*  8.24 fixed point (LFO calculations)       */
 
-.equiv ENV_QUIET,                (2*13*256/8)
+.equiv ENV_QUIET,        (2*13*256/8)
 
 .text
 .align 2
 @ r5=slot, r1=eg_cnt, trashes: r0,r2,r3
 @ writes output to routp, but only if vol_out changes
 .macro update_eg_phase_slot slot
+    ldrh    r0, [r5,#0x32]       @ vol_out
     ldrb    r2, [r5,#0x17]       @ state
     add     r3, r5, #0x1c
+    strh    r0, [r5,#0x34]       @ vol_ipol
     tst     r2, r2
     beq     0f                   @ EG_OFF
 
     add     r3, r3, r3, lsl #1
     mov     r3, r2, lsr r3
     and     r3, r3, #7           @ eg_inc_val shift, may be 0
+    ldrb    r0, [r5,#0x30]       @ ssg
     ldrb    r2, [r5,#0x17]       @ state
-    ldrh    r0, [r5,#0x1a]       @ volume, unsigned (0-1023)
 
+    tst     r0, #0x08            @ ssg enabled?
+    bne     9f
+
+    @ non-SSG-EG mode
     cmp     r2, #4               @ EG_ATT
+    ldrh    r0, [r5,#0x1a]       @ volume, unsigned (0-1023)
     beq     4f
+
     cmp     r2, #2
     mov     r2, #1
     mov     r2, r2, lsl r3
 
 4:  @ EG_ATT
     subs    r3, r3, #1           @ eg_inc_val_shift - 1
-    mov     r2, #0
     mvnpl   r2, r0
-    mov     r2, r2, lsl r3
-    add     r0, r0, r2, asr #4
+    movpl   r2, r2, lsl r3
+    addpl   r0, r0, r2, asr #4
     cmp     r0, #0               @ if (volume <= MIN_ATT_INDEX)
     bgt     10f
     ldr     r2, [r5,#0x1c]
     strgeb  r3, [r5,#0x17]       @ state
 
 10: @ finish
-    ldrh    r3, [r5,#0x18]       @ tl
     strh    r0, [r5,#0x1a]       @ volume
+    b       11f
+
+9:  @ SSG-EG mode
+    cmp     r2, #4               @ EG_ATT
+    ldrh    r0, [r5,#0x1a]       @ volume, unsigned (0-1023)
+    beq     4f
+
+    cmp     r0, #0x200           @ if ( volume < 0x200 )
+    movlt   r0, #1
+    movlt   r3, r0, lsl r3
+    ldrlth  r0, [r5,#0x1a]       @ volume, unsigned (0-1023)
+    movlt   r3, r3, lsr #1       @ eg_inc_val
+    addlt   r0, r0, r3, lsr #2
+
+    cmp     r2, #2
+    blt     1f                   @ EG_REL
+    beq     10f                  @ EG_SUS - nothing more to do
+
+3:  @ EG_DEC
+    ldr     r2, [r5,#0x1c]       @ sl (can be 16bit?)
+    mov     r3, #EG_SUS
+    cmp     r0, r2               @ if ( volume >= (INT32) SLOT->sl )
+    strgeb  r3, [r5,#0x17]       @ state
+    b       10f
+
+4:  @ EG_ATT
+    subs    r3, r3, #1           @ eg_inc_val_shift - 1
+    mvnpl   r2, r0
+    movpl   r2, r2, lsl r3
+    addpl   r0, r0, r2, asr #4
+    cmp     r0, #0               @ if (volume <= MIN_ATT_INDEX)
+    bgt     10f
+    ldr     r2, [r5,#0x1c]
+    mov     r0, #0
+    cmp     r2, #0
+    movne   r3, #EG_DEC
+    moveq   r3, #EG_SUS
+    strb    r3, [r5,#0x17]       @ state
+    b       10f
+
+1:  @ EG_REL
+    mov     r2, #0x200
+    cmp     r0, r2               @ if ( volume >= 0x200 )
+    movge   r0, #1024
+    subge   r0, #1
+    movge   r3, #EG_OFF
+    strgeb  r3, [r5,#0x17]       @ state
+
+10: @ finish
+    strh    r0, [r5,#0x1a]       @ volume
+    ldrb    r2, [r5,#0x30]       @ ssg
+    ldrb    r3, [r5,#0x17]       @ state
+    cmp     r2, #0x0c            @ if ( ssg&0x04 && state > EG_REL )
+    cmpge   r3, #EG_REL+1
+    rsbge   r0, r0, #0x200       @ volume = (0x200-volume) & MAX_ATT
+    lslge   r0, r0, #10
+    lsrge   r0, r0, #10
+
+11:
+    ldrh    r3, [r5,#0x18]       @ tl
+    add     r0, r0, r3           @ volume += tl
+    strh    r0, [r5,#0x32]       @ vol_out
 .if     \slot == SLOT1
     mov     r6, r6, lsr #16
-    add     r0, r0, r3
     orr     r6, r0, r6, lsl #16
 .elseif \slot == SLOT2
     mov     r6, r6, lsl #16
-    add     r0, r0, r3
     mov     r0, r0, lsl #16
     orr     r6, r0, r6, lsr #16
 .elseif \slot == SLOT3
     mov     r7, r7, lsr #16
-    add     r0, r0, r3
     orr     r7, r0, r7, lsl #16
 .elseif \slot == SLOT4
     mov     r7, r7, lsl #16
-    add     r0, r0, r3
     mov     r0, r0, lsl #16
     orr     r7, r0, r7, lsr #16
 .endif
 0: @ EG_OFF
 .endm
 
+@ r5=slot, trashes: r0,r2,r3
+.macro update_ssg_eg
+    ldrh    r0, [r5,#0x30]                @ ssg+ssgn
+    ldrb    r2, [r5,#0x17]                @ state
+    ldrh    r3, [r5,#0x1a]                @ volume
+    tst     r0, #0x08                     @ ssg enabled?
+    beq     9f
+    cmp     r2, #EG_REL                   @ state > EG_REL?
+    ble     9f
+    cmp     r3, #0x200                    @ volume >= 0x200?
+    blt     9f
+
+    tst     r0, #0x01
+    beq     1f
+
+    tst     r0, #0x02
+    eorne   r0, r0, lsr #8                @ ssg ^= ssgn ^ 4
+    eorne   r0, r0, #0x4
+    orrne   r0, r0, #0x400                @ ssgn = 4
+    strneh  r0, [r5,#0x30]
+
+    eor     r0, r0, #0x4                  @ if ( !(ssg&0x04 )
+    tst     r0, #0x4
+    cmpne   r2, #EG_ATT                   @ if ( state != EG_ATT )
+    movne   r0, #0x400
+    subne   r0, r0, #1
+    strneh  r0, [r5,#0x1a]                @ volume = MAX_ATT
+    b       9f
+
+1:  tst     r0, #0x02
+    eorne   r0, r0, #0x4                  @ ssg ^= 4
+    eorne   r0, r0, #0x400                @ ssgn ^= 4
+    strneh  r0, [r5,#0x30]
+    moveq   r3, #0
+    streq   r3, [r5,#0x0c]                @ phase = 0
+
+    cmp     r2, #EG_ATT                   @ if ( state != EG_ATT )
+    beq     9f
+
+    ldr     r3, [r5,#0x1c]                @ sl
+    mov     r2, #EG_SUS                   @ state = sl==MIN_ATT ? EG_SUS:EG_DEC
+    cmp     r3, #0
+
+    ldr     r0, [r5,#0x04]                @ ar
+    ldr     r3, [r5,#0x14]                @ ksr
+    movne   r2, #EG_DEC
+    add     r0, r0, r3
+    cmp     r0, #32+62                    @ if ( ar+ksr >= 32+62 )
+    ldrlt   r0, [r5,#0x1a]
+    movge   r0, #0
+    strgeh  r0, [r5,#0x1a]                @ volume = MIN_ATT
+
+    cmp     r0, #0
+    movgt   r2, #EG_ATT
+    strb    r2, [r5,#0x17]                @ state
+9:
+.endm
 
 @ r12=lfo_ampm[31:16], r1=lfo_cnt_old, r2=lfo_cnt, r3=scratch
 .macro advance_lfo_m
 .endm
 
 
-/*
-.global update_eg_phase @ FM_SLOT *SLOT, UINT32 eg_cnt
-
-update_eg_phase:
-    stmfd   sp!, {r5,r6}
-    mov     r5, r0             @ slot
-    ldrh    r3, [r5,#0x18]       @ tl
-    ldrh    r6, [r5,#0x1a]       @ volume
-    add     r6, r6, r3
-    update_eg_phase_slot SLOT1
-    mov     r0, r6
-    ldmfd   sp!, {r5,r6}
-    bx      lr
-.pool
-
-
-.global advance_lfo @ int lfo_ampm, UINT32 lfo_cnt_old, UINT32 lfo_cnt
-
-advance_lfo:
-    mov     r12, r0, lsl #16
-    advance_lfo_m
-    mov     r0, r12, lsr #16
-    bx      lr
-.pool
-
-
-.global upd_algo0 @ chan_rend_context *c
-upd_algo0:
-    stmfd   sp!, {r4-r10,lr}
-    mov     lr, r0
-
-    PIC_LDR(r3, ip, ym_sin_tab)
-    PIC_LDR(r5, ip, ym_tl_tab)
-    ldmia   lr, {r6-r7}
-    ldr     r10, [lr, #0x54]
-    ldr     r12, [lr, #0x4c]
-
-    upd_algo0_m
-
-    ldmfd   sp!, {r4-r10,pc}
-.pool
-
-
-.global upd_algo1 @ chan_rend_context *c
-upd_algo1:
-    stmfd   sp!, {r4-r10,lr}
-    mov     lr, r0
-
-    PIC_LDR(r3, ip, ym_sin_tab)
-    PIC_LDR(r5, ip, ym_tl_tab)
-    ldmia   lr, {r6-r7}
-    ldr     r10, [lr, #0x54]
-    ldr     r12, [lr, #0x4c]
-
-    upd_algo1_m
-
-    ldmfd   sp!, {r4-r10,pc}
-.pool
-
-
-.global upd_algo2 @ chan_rend_context *c
-upd_algo2:
-    stmfd   sp!, {r4-r10,lr}
-    mov     lr, r0
-
-    PIC_LDR(r3, ip, ym_sin_tab)
-    PIC_LDR(r5, ip, ym_tl_tab)
-    ldmia   lr, {r6-r7}
-    ldr     r10, [lr, #0x54]
-    ldr     r12, [lr, #0x4c]
-
-    upd_algo2_m
-
-    ldmfd   sp!, {r4-r10,pc}
-.pool
-
-
-.global upd_algo3 @ chan_rend_context *c
-upd_algo3:
-    stmfd   sp!, {r4-r10,lr}
-    mov     lr, r0
-
-    PIC_LDR(r3, ip, ym_sin_tab)
-    PIC_LDR(r5, ip, ym_tl_tab)
-    ldmia   lr, {r6-r7}
-    ldr     r10, [lr, #0x54]
-    ldr     r12, [lr, #0x4c]
-
-    upd_algo3_m
-
-    ldmfd   sp!, {r4-r10,pc}
-.pool
-
-
-.global upd_algo4 @ chan_rend_context *c
-upd_algo4:
-    stmfd   sp!, {r4-r10,lr}
-    mov     lr, r0
-
-    PIC_LDR(r3, ip, ym_sin_tab)
-    PIC_LDR(r5, ip, ym_tl_tab)
-    ldmia   lr, {r6-r7}
-    ldr     r10, [lr, #0x54]
-    ldr     r12, [lr, #0x4c]
-
-    upd_algo4_m
-
-    ldmfd   sp!, {r4-r10,pc}
-.pool
-
-
-.global upd_algo5 @ chan_rend_context *c
-upd_algo5:
-    stmfd   sp!, {r4-r10,lr}
-    mov     lr, r0
-
-    PIC_LDR(r3, ip, ym_sin_tab)
-    PIC_LDR(r5, ip, ym_tl_tab)
-    ldmia   lr, {r6-r7}
-    ldr     r10, [lr, #0x54]
-    ldr     r12, [lr, #0x4c]
-
-    upd_algo5_m
-
-    ldmfd   sp!, {r4-r10,pc}
-.pool
-
-
-.global upd_algo6 @ chan_rend_context *c
-upd_algo6:
-    stmfd   sp!, {r4-r10,lr}
-    mov     lr, r0
-
-    PIC_LDR(r3, ip, ym_sin_tab)
-    PIC_LDR(r5, ip, ym_tl_tab)
-    ldmia   lr, {r6-r7}
-    ldr     r10, [lr, #0x54]
-    ldr     r12, [lr, #0x4c]
-
-    upd_algo6_m
-
-    ldmfd   sp!, {r4-r10,pc}
-.pool
-
-
-.global upd_algo7 @ chan_rend_context *c
-upd_algo7:
-    stmfd   sp!, {r4-r10,lr}
-    mov     lr, r0
-
-    PIC_LDR(r3, ip, ym_sin_tab)
-    PIC_LDR(r5, ip, ym_tl_tab)
-    ldmia   lr, {r6-r7}
-    ldr     r10, [lr, #0x54]
-    ldr     r12, [lr, #0x4c]
-
-    upd_algo7_m
-
-    ldmfd   sp!, {r4-r10,pc}
-.pool
-
-
-.global upd_slot1 @ chan_rend_context *c
-upd_slot1:
-    stmfd   sp!, {r4-r10,lr}
-    mov     lr, r0
-
-    PIC_LDR(r3, ip, ym_sin_tab)
-    PIC_LDR(r5, ip, ym_tl_tab)
-    ldmia   lr, {r6-r7}
-    ldr     r10, [lr, #0x54]
-    ldr     r12, [lr, #0x4c]
-
-    upd_slot1_m
-    str     r10, [lr, #0x38]
-
-    ldmfd   sp!, {r4-r10,pc}
-.pool
-*/
-
-
 @ lr=context, r12=pack (stereo, lastchan, disabled, lfo_enabled | pan_r, pan_l, ams[2] | AMmasks[4] | FB[4] | lfo_ampm[16])
 @ r0-r2=scratch, r3=sin_tab/scratch, r4=(length<<8)|unused[4],was_update,algo[3], r5=tl_tab/slot,
 @ r6-r7=vol_out[4], r8=eg_timer, r9=eg_timer_add[31:16], r10=op1_out, r11=buffer
@@ -730,14 +671,21 @@ chan_render_loop:
     add     r0,  lr, #0x44
     ldmia   r0,  {r8,r9}         @ eg_timer, eg_timer_add
     ldr     r10, [lr, #0x54]     @ op1_out
-    ldmia   lr,  {r6,r7}         @ load volumes
+@   ldmia   lr,  {r6,r7}         @ load volumes
+    ldr     r5, [lr, #0x40]      @ CH
+    ldrh    r6, [r5, #0x32]      @ vol_out values for all slots
+    ldrh    r2, [r5, #0x32+SLOT_STRUCT_SIZE*2]
+    ldrh    r7, [r5, #0x32+SLOT_STRUCT_SIZE]
+    ldrh    r3, [r5, #0x32+SLOT_STRUCT_SIZE*3]
+    orr     r6, r6, r2, lsl #16
+    orr     r7, r7, r3, lsl #16
 
     tst     r12, #8              @ lfo?
     beq     crl_loop
 
 crl_loop_lfo:
     add     r0, lr, #0x30
-    ldmia   r0, {r1,r2}
+    ldmia   r0, {r1,r2}          @ lfo_cnt, lfo_inc
 
     subs    r4, r4, #0x100
     bmi     crl_loop_end
@@ -754,15 +702,29 @@ crl_loop:
     subs    r4, r4, #0x100
     bmi     crl_loop_end
 
+    @ -- SSG --
+    add     r0, lr, #0x3c
+    ldmia   r0, {r1,r5}         @ eg_cnt, CH
+
+    @ r5=slot, trashes: r0,r2,r3
+    update_ssg_eg
+    add     r5, r5, #SLOT_STRUCT_SIZE*2 @ SLOT2 (2)
+    update_ssg_eg
+    sub     r5, r5, #SLOT_STRUCT_SIZE   @ SLOT3 (1)
+    update_ssg_eg
+    add     r5, r5, #SLOT_STRUCT_SIZE*2 @ SLOT4 (3)
+    update_ssg_eg
+    sub     r5, r5, #SLOT_STRUCT_SIZE*3
+
     @ -- EG --
     add     r8, r8, r9
     cmp     r8, #EG_TIMER_OVERFLOW
     bcc     eg_done
-    add     r0, lr, #0x3c
-    ldmia   r0, {r1,r5}         @ eg_cnt, CH
 eg_loop:
     sub     r8, r8, #EG_TIMER_OVERFLOW
     add     r1, r1, #1
+    cmp     r1, #4096
+    movge   r1, #1
                                         @ SLOT1 (0)
     @ r5=slot, r1=eg_cnt, trashes: r0,r2,r3
     update_eg_phase_slot SLOT1
@@ -774,8 +736,8 @@ eg_loop:
     update_eg_phase_slot SLOT4
 
     cmp     r8, #EG_TIMER_OVERFLOW
-    subcs   r5, r5, #SLOT_STRUCT_SIZE*3
-    bcs     eg_loop
+    sub     r5, r5, #SLOT_STRUCT_SIZE*3
+    bhs     eg_loop
     str     r1, [lr, #0x3c]
 
 eg_done:
@@ -787,6 +749,66 @@ eg_done:
     cmp     r0, #0x4
     beq     crl_loop
 
+    @ output interpolation
+#if 0
+    @ basic interpolator, interpolate in middle region, else use closer value
+    mov     r3, r8, lsr #EG_SH      @ eg_timer, [0..3<<EG_SH) after loop
+    cmp     r3, #(EG_TIMER_OVERFLOW>>EG_SH)/2
+    bgt     0f                      @ mix is vol_out
+
+    ldrh    r0, [r5,#0x34]          @ SLOT1 vol_ipol
+    lsleq   r2, r6, #16
+    addeq   r0, r0, r2, lsr #16
+    lsreq   r0, r0, #1
+    mov     r6, r6, lsr #16
+    orr     r6, r0, r6, lsl #16
+
+    ldrh    r0, [r5,#0x34+SLOT_STRUCT_SIZE*2] @ SLOT2 vol_ipol
+    addeq   r0, r0, r6, lsr #16
+    lsreq   r0, r0, #1
+    mov     r6, r6, lsl #16
+    orr     r6, r6, r0
+    ror     r6, r6, #16
+
+    ldrh    r0, [r5,#0x34+SLOT_STRUCT_SIZE]   @ SLOT3 vol_ipol
+    lsleq   r2, r7, #16
+    addeq   r0, r0, r2, lsr #16
+    lsreq   r0, r0, #1
+    mov     r7, r7, lsr #16
+    orr     r7, r0, r7, lsl #16
+
+    ldrh    r0, [r5,#0x34+SLOT_STRUCT_SIZE*3] @ SLOT4 vol_ipol
+    addeq   r0, r0, r7, lsr #16
+    lsreq   r0, r0, #1
+    mov     r7, r7, lsl #16
+    orr     r7, r7, r0
+    ror     r7, r7, #16
+#elif 0
+    @ super-basic... just take value closest to sample point
+    mov     r3, r8, lsr #EG_SH-1    @ eg_timer, [0..3<<EG_SH) after loop
+    cmp     r3, #(EG_TIMER_OVERFLOW>>EG_SH)
+    bgt     0f                      @ mix is vol_out
+
+    ldrh    r0, [r5,#0x34]          @ SLOT1 vol_ipol
+    mov     r6, r6, lsr #16
+    orr     r6, r0, r6, lsl #16
+
+    ldrh    r0, [r5,#0x34+SLOT_STRUCT_SIZE*2] @ SLOT2 vol_ipol
+    mov     r6, r6, lsl #16
+    orr     r6, r6, r0
+    ror     r6, r6, #16
+
+    ldrh    r0, [r5,#0x34+SLOT_STRUCT_SIZE]   @ SLOT3 vol_ipol
+    mov     r7, r7, lsr #16
+    orr     r7, r0, r7, lsl #16
+
+    ldrh    r0, [r5,#0x34+SLOT_STRUCT_SIZE*3] @ SLOT4 vol_ipol
+    mov     r7, r7, lsl #16
+    orr     r7, r7, r0
+    ror     r7, r7, #16
+#endif
+0:
+
     @ -- SLOT1 --
     PIC_LDR(r3, r2, ym_tl_tab)