sound, improve ym2612 accuracy (NB noticeably slower for low bitrates)
authorkub <derkub@gmail.com>
Wed, 22 Dec 2021 23:42:11 +0000 (00:42 +0100)
committerkub <derkub@gmail.com>
Wed, 22 Dec 2021 23:42:11 +0000 (00:42 +0100)
pico/sound/ym2612.c
pico/sound/ym2612.h
pico/sound/ym2612_arm.S

index a10e678..ccdce77 100644 (file)
@@ -8,7 +8,6 @@
 **\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
@@ -909,7 +908,7 @@ typedef struct
        UINT32 eg_timer;\r
        UINT32 eg_timer_add;\r
        UINT32 pack;     // 4c: stereo, lastchan, disabled, lfo_enabled | pan_r, pan_l, ams[2] | AMmasks[4] | FB[4] | lfo_ampm[16]\r
-       UINT32 algo;     /* 50: algo[3], was_update */\r
+       UINT32 algo;     /* 50: algo[3], was_update, unsued, upd_cnt[2], dac */\r
        INT32  op1_out;\r
 #ifdef _MIPS_ARCH_ALLEGREX\r
        UINT32 pad1[3+8];\r
@@ -921,317 +920,292 @@ typedef struct
 #include <limits.h>\r
 static int clip(int n) \r
 {\r
-    unsigned b = 14, s = n < 0;\r
-    int m = s + INT_MAX;\r
-    if (s + (n>>(b-1))) n = m >> (8*sizeof(int)-b);\r
-    return n;\r
+       unsigned b = 14, s = n < 0;\r
+       int m = s + INT_MAX;\r
+       if (s + (n>>(b-1))) n = m >> (8*sizeof(int)-b);\r
+       return n;\r
 }\r
 \r
-static void chan_render_loop(chan_rend_context *ct, int *buffer, int length)\r
+static void update_ssg_eg_channel(chan_rend_context *ct)\r
 {\r
-       int scounter;                                   /* sample counter */\r
+       FM_SLOT *SLOT;\r
 \r
-       /* sample generating loop */\r
-       for (scounter = 0; scounter < length; scounter++)\r
+       SLOT = &ct->CH->SLOT[SLOT1];\r
+       if ((SLOT->ssg&0x08) && SLOT->state > EG_REL && SLOT->volume >= 0x200)\r
+               ct->phase1 = update_ssg_eg_phase(SLOT, ct->phase1);\r
+       SLOT = &ct->CH->SLOT[SLOT2];\r
+       if ((SLOT->ssg&0x08) && SLOT->state > EG_REL && SLOT->volume >= 0x200)\r
+               ct->phase2 = update_ssg_eg_phase(SLOT, ct->phase2);\r
+       SLOT = &ct->CH->SLOT[SLOT3];\r
+       if ((SLOT->ssg&0x08) && SLOT->state > EG_REL && SLOT->volume >= 0x200)\r
+               ct->phase3 = update_ssg_eg_phase(SLOT, ct->phase3);\r
+       SLOT = &ct->CH->SLOT[SLOT4];\r
+       if ((SLOT->ssg&0x08) && SLOT->state > EG_REL && SLOT->volume >= 0x200)\r
+               ct->phase4 = update_ssg_eg_phase(SLOT, ct->phase4);\r
+}\r
+\r
+static void update_eg_phase_channel(chan_rend_context *ct)\r
+{\r
+       FM_SLOT *SLOT;\r
+\r
+       SLOT = &ct->CH->SLOT[SLOT1];\r
+       if (SLOT->state != EG_OFF) update_eg_phase(SLOT, ct->eg_cnt, ct->pack & 2);\r
+       SLOT = &ct->CH->SLOT[SLOT2];\r
+       if (SLOT->state != EG_OFF) update_eg_phase(SLOT, ct->eg_cnt, ct->pack & 2);\r
+       SLOT = &ct->CH->SLOT[SLOT3];\r
+       if (SLOT->state != EG_OFF) update_eg_phase(SLOT, ct->eg_cnt, ct->pack & 2);\r
+       SLOT = &ct->CH->SLOT[SLOT4];\r
+       if (SLOT->state != EG_OFF) update_eg_phase(SLOT, ct->eg_cnt, ct->pack & 2);\r
+}\r
+\r
+static int update_algo_channel(chan_rend_context *ct, unsigned int eg_out, unsigned int eg_out2, unsigned int eg_out4)\r
+{\r
+       int m2,c1,c2=0; /* Phase Modulation input for operators 2,3,4 */\r
+       int smp = 0;\r
+\r
+       switch( ct->algo&0x7 )\r
        {\r
-               int smp = 0;            /* produced sample */\r
-               unsigned int eg_out, eg_out2, eg_out4;\r
-               FM_SLOT *SLOT;\r
-               UINT32 cnt = ct->eg_timer_add+(ct->eg_timer & ((1<<EG_SH)-1));\r
-\r
-               if (ct->pack & 2) while (cnt >= 1<<EG_SH) {\r
-                       cnt -= 1<<EG_SH;\r
-                       SLOT = &ct->CH->SLOT[SLOT1];\r
-                       if ((SLOT->ssg&0x08) && SLOT->state > EG_REL && SLOT->volume >= 0x200)\r
-                               ct->phase1 = update_ssg_eg_phase(SLOT, ct->phase1);\r
-                       SLOT = &ct->CH->SLOT[SLOT2];\r
-                       if ((SLOT->ssg&0x08) && SLOT->state > EG_REL && SLOT->volume >= 0x200)\r
-                               ct->phase2 = update_ssg_eg_phase(SLOT, ct->phase2);\r
-                       SLOT = &ct->CH->SLOT[SLOT3];\r
-                       if ((SLOT->ssg&0x08) && SLOT->state > EG_REL && SLOT->volume >= 0x200)\r
-                               ct->phase3 = update_ssg_eg_phase(SLOT, ct->phase3);\r
-                       SLOT = &ct->CH->SLOT[SLOT4];\r
-                       if ((SLOT->ssg&0x08) && SLOT->state > EG_REL && SLOT->volume >= 0x200)\r
-                               ct->phase4 = update_ssg_eg_phase(SLOT, ct->phase4);\r
-               }\r
+               case 0:\r
+               {\r
+                       /* M1---C1---MEM---M2---C2---OUT */\r
+                       m2 = ct->mem;\r
+                       c1 = ct->op1_out>>16;\r
+                       if( eg_out2 < ENV_QUIET ) {             /* SLOT 2 */\r
+                               ct->mem = op_calc(ct->phase2, eg_out2, c1);\r
+                       }\r
+                       else ct->mem = 0;\r
+                       if (ct->eg_timer >= (1<<EG_SH)) break;\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
-                       ct->lfo_cnt += ct->lfo_inc;\r
+                       if( eg_out  < ENV_QUIET ) {             /* SLOT 3 */\r
+                               c2  = op_calc(ct->phase3, eg_out,  m2);\r
+                       }\r
+                       if( eg_out4 < ENV_QUIET ) {             /* SLOT 4 */\r
+                               smp = op_calc(ct->phase4, eg_out4, c2);\r
+                       }\r
+                       break;\r
                }\r
+               case 1:\r
+               {\r
+                       /* M1------+-MEM---M2---C2---OUT */\r
+                       /*      C1-+                     */\r
+                       m2 = ct->mem;\r
+                       ct->mem = ct->op1_out>>16;\r
+                       if( eg_out2 < ENV_QUIET ) {             /* SLOT 2 */\r
+                               ct->mem+= op_calc(ct->phase2, eg_out2, 0);\r
+                       }\r
+                       if (ct->eg_timer >= (1<<EG_SH)) break;\r
 \r
-               ct->eg_timer += ct->eg_timer_add;\r
-               if (ct->eg_timer < EG_TIMER_OVERFLOW) {\r
-                       SLOT = &ct->CH->SLOT[SLOT1];\r
-                       SLOT->vol_ipol = SLOT->vol_out;\r
-                       if (SLOT->state > EG_REL) recalc_volout(SLOT);\r
-                       SLOT = &ct->CH->SLOT[SLOT2];\r
-                       SLOT->vol_ipol = SLOT->vol_out;\r
-                       if (SLOT->state > EG_REL) recalc_volout(SLOT);\r
-                       SLOT = &ct->CH->SLOT[SLOT3];\r
-                       SLOT->vol_ipol = SLOT->vol_out;\r
-                       if (SLOT->state > EG_REL) recalc_volout(SLOT);\r
-                       SLOT = &ct->CH->SLOT[SLOT4];\r
-                       SLOT->vol_ipol = SLOT->vol_out;\r
-                       if (SLOT->state > EG_REL) recalc_volout(SLOT);\r
+                       if( eg_out  < ENV_QUIET ) {             /* SLOT 3 */\r
+                               c2  = op_calc(ct->phase3, eg_out,  m2);\r
+                       }\r
+                       if( eg_out4 < ENV_QUIET ) {             /* SLOT 4 */\r
+                               smp = op_calc(ct->phase4, eg_out4, c2);\r
+                       }\r
+                       break;\r
                }\r
-               else while (ct->eg_timer >= EG_TIMER_OVERFLOW)\r
+               case 2:\r
                {\r
-                       ct->eg_timer -= EG_TIMER_OVERFLOW;\r
-                       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, ct->pack & 2);\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, ct->pack & 2);\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, ct->pack & 2);\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, ct->pack & 2);\r
-               }\r
+                       /* M1-----------------+-C2---OUT */\r
+                       /*      C1---MEM---M2-+          */\r
+                       m2 = ct->mem;\r
+                       c2 = ct->op1_out>>16;\r
+                       if( eg_out2 < ENV_QUIET ) {             /* SLOT 2 */\r
+                               ct->mem = op_calc(ct->phase2, eg_out2, 0);\r
+                       }\r
+                       else ct->mem = 0;\r
+                       if (ct->eg_timer >= (1<<EG_SH)) break;\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
-#elif 1\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
-                               break;\r
-               }\r
-#elif 0\r
-               if (ct->eg_timer >> (EG_SH-1) < EG_TIMER_OVERFLOW >> EG_SH) {\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
-               } else {\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
+                       if( eg_out  < ENV_QUIET ) {             /* SLOT 3 */\r
+                               c2 += op_calc(ct->phase3, eg_out,  m2);\r
+                       }\r
+                       if( eg_out4 < ENV_QUIET ) {             /* SLOT 4 */\r
+                               smp = op_calc(ct->phase4, eg_out4, c2);\r
+                       }\r
+                       break;\r
                }\r
-#else\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
-#endif\r
-\r
-               if (ct->pack & 4) continue; /* output disabled */\r
-\r
-               /* calculate channel sample */\r
-               eg_out = ct->vol_out1;\r
-               if ( (ct->pack & 8) && (ct->pack&(1<<(SLOT1+8))) ) eg_out += ct->pack >> (((ct->pack&0xc0)>>6)+24);\r
-\r
-               if( eg_out < ENV_QUIET )        /* SLOT 1 */\r
+               case 3:\r
                {\r
-                       int out = 0;\r
+                       /* M1---C1---MEM------+-C2---OUT */\r
+                       /*                 M2-+          */\r
+                       c2 = ct->mem;\r
+                       c1 = ct->op1_out>>16;\r
+                       if( eg_out2 < ENV_QUIET ) {             /* SLOT 2 */\r
+                               ct->mem = op_calc(ct->phase2, eg_out2, c1);\r
+                       }\r
+                       else ct->mem = 0;\r
+                       if (ct->eg_timer >= (1<<EG_SH)) break;\r
 \r
-                       if (ct->pack&0xf000) out = ((ct->op1_out>>16) + ((ct->op1_out<<16)>>16)) << ((ct->pack&0xf000)>>12); /* op1_out0 + op1_out1 */\r
-                       ct->op1_out <<= 16;\r
-                       ct->op1_out |= (unsigned short)op_calc1(ct->phase1, eg_out, out);\r
-               } else {\r
-                       ct->op1_out <<= 16; /* op1_out0 = op1_out1; op1_out1 = 0; */\r
+                       if( eg_out  < ENV_QUIET ) {             /* SLOT 3 */\r
+                               c2 += op_calc(ct->phase3, eg_out,  0);\r
+                       }\r
+                       if( eg_out4 < ENV_QUIET ) {             /* SLOT 4 */\r
+                               smp = op_calc(ct->phase4, eg_out4, c2);\r
+                       }\r
+                       break;\r
                }\r
-\r
-               eg_out  = ct->vol_out3; // volume_calc(&CH->SLOT[SLOT3]);\r
-               eg_out2 = ct->vol_out2; // volume_calc(&CH->SLOT[SLOT2]);\r
-               eg_out4 = ct->vol_out4; // volume_calc(&CH->SLOT[SLOT4]);\r
-\r
-               if (ct->pack & 8) {\r
-                       unsigned int add = ct->pack >> (((ct->pack&0xc0)>>6)+24);\r
-                       if (ct->pack & (1<<(SLOT3+8))) eg_out  += add;\r
-                       if (ct->pack & (1<<(SLOT2+8))) eg_out2 += add;\r
-                       if (ct->pack & (1<<(SLOT4+8))) eg_out4 += add;\r
+               case 4:\r
+               {\r
+                       /* M1---C1-+-OUT */\r
+                       /* M2---C2-+     */\r
+                       /* MEM: not used */\r
+                       if (ct->eg_timer >= (1<<EG_SH)) break;\r
+\r
+                       c1 = ct->op1_out>>16;\r
+                       if( eg_out  < ENV_QUIET ) {             /* SLOT 3 */\r
+                               c2  = op_calc(ct->phase3, eg_out,  0);\r
+                       }\r
+                       if( eg_out2 < ENV_QUIET ) {             /* SLOT 2 */\r
+                               smp = op_calc(ct->phase2, eg_out2, c1);\r
+                       }\r
+                       if( eg_out4 < ENV_QUIET ) {             /* SLOT 4 */\r
+                               smp+= op_calc(ct->phase4, eg_out4, c2);\r
+                       }\r
+                       break;\r
                }\r
-\r
-               switch( ct->algo&0x7 )\r
+               case 5:\r
                {\r
-                       case 0:\r
-                       {\r
-                               /* M1---C1---MEM---M2---C2---OUT */\r
-                               int m2,c1,c2=0; /* Phase Modulation input for operators 2,3,4 */\r
-                               m2 = ct->mem;\r
-                               c1 = ct->op1_out>>16;\r
-                               if( eg_out  < ENV_QUIET ) {             /* SLOT 3 */\r
-                                       c2  = op_calc(ct->phase3, eg_out,  m2);\r
-                               }\r
-                               if( eg_out2 < ENV_QUIET ) {             /* SLOT 2 */\r
-                                       ct->mem = op_calc(ct->phase2, eg_out2, c1);\r
-                               }\r
-                               else ct->mem = 0;\r
-                               if( eg_out4 < ENV_QUIET ) {             /* SLOT 4 */\r
-                                       smp = op_calc(ct->phase4, eg_out4, c2);\r
-                               }\r
-                               break;\r
+                       /*    +----C1----+     */\r
+                       /* M1-+-MEM---M2-+-OUT */\r
+                       /*    +----C2----+     */\r
+                       if (ct->eg_timer >= (1<<EG_SH)) break;\r
+\r
+                       m2 = ct->mem;\r
+                       ct->mem = c1 = c2 = ct->op1_out>>16;\r
+                       if( eg_out < ENV_QUIET ) {              /* SLOT 3 */\r
+                               smp = op_calc(ct->phase3, eg_out, m2);\r
                        }\r
-                       case 1:\r
-                       {\r
-                               /* M1------+-MEM---M2---C2---OUT */\r
-                               /*      C1-+                     */\r
-                               int m2,c2=0;\r
-                               m2 = ct->mem;\r
-                               ct->mem = ct->op1_out>>16;\r
-                               if( eg_out  < ENV_QUIET ) {             /* SLOT 3 */\r
-                                       c2  = op_calc(ct->phase3, eg_out,  m2);\r
-                               }\r
-                               if( eg_out2 < ENV_QUIET ) {             /* SLOT 2 */\r
-                                       ct->mem+= op_calc(ct->phase2, eg_out2, 0);\r
-                               }\r
-                               if( eg_out4 < ENV_QUIET ) {             /* SLOT 4 */\r
-                                       smp = op_calc(ct->phase4, eg_out4, c2);\r
-                               }\r
-                               break;\r
+                       if( eg_out2 < ENV_QUIET ) {             /* SLOT 2 */\r
+                               smp+= op_calc(ct->phase2, eg_out2, c1);\r
                        }\r
-                       case 2:\r
-                       {\r
-                               /* M1-----------------+-C2---OUT */\r
-                               /*      C1---MEM---M2-+          */\r
-                               int m2,c2;\r
-                               m2 = ct->mem;\r
-                               c2 = ct->op1_out>>16;\r
-                               if( eg_out  < ENV_QUIET ) {             /* SLOT 3 */\r
-                                       c2 += op_calc(ct->phase3, eg_out,  m2);\r
-                               }\r
-                               if( eg_out2 < ENV_QUIET ) {             /* SLOT 2 */\r
-                                       ct->mem = op_calc(ct->phase2, eg_out2, 0);\r
-                               }\r
-                               else ct->mem = 0;\r
-                               if( eg_out4 < ENV_QUIET ) {             /* SLOT 4 */\r
-                                       smp = op_calc(ct->phase4, eg_out4, c2);\r
-                               }\r
-                               break;\r
+                       if( eg_out4 < ENV_QUIET ) {             /* SLOT 4 */\r
+                               smp+= op_calc(ct->phase4, eg_out4, c2);\r
                        }\r
-                       case 3:\r
-                       {\r
-                               /* M1---C1---MEM------+-C2---OUT */\r
-                               /*                 M2-+          */\r
-                               int c1,c2;\r
-                               c2 = ct->mem;\r
-                               c1 = ct->op1_out>>16;\r
-                               if( eg_out  < ENV_QUIET ) {             /* SLOT 3 */\r
-                                       c2 += op_calc(ct->phase3, eg_out,  0);\r
-                               }\r
-                               if( eg_out2 < ENV_QUIET ) {             /* SLOT 2 */\r
-                                       ct->mem = op_calc(ct->phase2, eg_out2, c1);\r
-                               }\r
-                               else ct->mem = 0;\r
-                               if( eg_out4 < ENV_QUIET ) {             /* SLOT 4 */\r
-                                       smp = op_calc(ct->phase4, eg_out4, c2);\r
-                               }\r
-                               break;\r
+                       break;\r
+               }\r
+               case 6:\r
+               {\r
+                       /* M1---C1-+     */\r
+                       /*      M2-+-OUT */\r
+                       /*      C2-+     */\r
+                       /* MEM: not used */\r
+                       if (ct->eg_timer >= (1<<EG_SH)) break;\r
+\r
+                       c1 = ct->op1_out>>16;\r
+                       if( eg_out < ENV_QUIET ) {              /* SLOT 3 */\r
+                               smp = op_calc(ct->phase3, eg_out,  0);\r
                        }\r
-                       case 4:\r
-                       {\r
-                               /* M1---C1-+-OUT */\r
-                               /* M2---C2-+     */\r
-                               /* MEM: not used */\r
-                               int c1,c2=0;\r
-                               c1 = ct->op1_out>>16;\r
-                               if( eg_out  < ENV_QUIET ) {             /* SLOT 3 */\r
-                                       c2  = op_calc(ct->phase3, eg_out,  0);\r
-                               }\r
-                               if( eg_out2 < ENV_QUIET ) {             /* SLOT 2 */\r
-                                       smp = op_calc(ct->phase2, eg_out2, c1);\r
-                               }\r
-                               if( eg_out4 < ENV_QUIET ) {             /* SLOT 4 */\r
-                                       smp+= op_calc(ct->phase4, eg_out4, c2);\r
-                               }\r
-                               break;\r
+                       if( eg_out2 < ENV_QUIET ) {             /* SLOT 2 */\r
+                               smp+= op_calc(ct->phase2, eg_out2, c1);\r
                        }\r
-                       case 5:\r
-                       {\r
-                               /*    +----C1----+     */\r
-                               /* M1-+-MEM---M2-+-OUT */\r
-                               /*    +----C2----+     */\r
-                               int m2,c1,c2;\r
-                               m2 = ct->mem;\r
-                               ct->mem = c1 = c2 = ct->op1_out>>16;\r
-                               if( eg_out < ENV_QUIET ) {              /* SLOT 3 */\r
-                                       smp = op_calc(ct->phase3, eg_out, m2);\r
-                               }\r
-                               if( eg_out2 < ENV_QUIET ) {             /* SLOT 2 */\r
-                                       smp+= op_calc(ct->phase2, eg_out2, c1);\r
-                               }\r
-                               if( eg_out4 < ENV_QUIET ) {             /* SLOT 4 */\r
-                                       smp+= op_calc(ct->phase4, eg_out4, c2);\r
-                               }\r
-                               break;\r
+                       if( eg_out4 < ENV_QUIET ) {             /* SLOT 4 */\r
+                               smp+= op_calc(ct->phase4, eg_out4, 0);\r
                        }\r
-                       case 6:\r
-                       {\r
-                               /* M1---C1-+     */\r
-                               /*      M2-+-OUT */\r
-                               /*      C2-+     */\r
-                               /* MEM: not used */\r
-                               int c1;\r
-                               c1 = ct->op1_out>>16;\r
-                               if( eg_out < ENV_QUIET ) {              /* SLOT 3 */\r
-                                       smp = op_calc(ct->phase3, eg_out,  0);\r
-                               }\r
-                               if( eg_out2 < ENV_QUIET ) {             /* SLOT 2 */\r
-                                       smp+= op_calc(ct->phase2, eg_out2, c1);\r
-                               }\r
-                               if( eg_out4 < ENV_QUIET ) {             /* SLOT 4 */\r
-                                       smp+= op_calc(ct->phase4, eg_out4, 0);\r
-                               }\r
-                               break;\r
+                       break;\r
+               }\r
+               case 7:\r
+               {\r
+                       /* M1-+     */\r
+                       /* C1-+-OUT */\r
+                       /* M2-+     */\r
+                       /* C2-+     */\r
+                       /* MEM: not used*/\r
+                       if (ct->eg_timer >= (1<<EG_SH)) break;\r
+\r
+                       smp = ct->op1_out>>16;\r
+                       if( eg_out < ENV_QUIET ) {              /* SLOT 3 */\r
+                               smp += op_calc(ct->phase3, eg_out,  0);\r
                        }\r
-                       case 7:\r
-                       {\r
-                               /* M1-+     */\r
-                               /* C1-+-OUT */\r
-                               /* M2-+     */\r
-                               /* C2-+     */\r
-                               /* MEM: not used*/\r
-                               smp = ct->op1_out>>16;\r
-                               if( eg_out < ENV_QUIET ) {              /* SLOT 3 */\r
-                                       smp += op_calc(ct->phase3, eg_out,  0);\r
-                               }\r
-                               if( eg_out2 < ENV_QUIET ) {             /* SLOT 2 */\r
-                                       smp += op_calc(ct->phase2, eg_out2, 0);\r
+                       if( eg_out2 < ENV_QUIET ) {             /* SLOT 2 */\r
+                               smp += op_calc(ct->phase2, eg_out2, 0);\r
+                       }\r
+                       if( eg_out4 < ENV_QUIET ) {             /* SLOT 4 */\r
+                               smp += op_calc(ct->phase4, eg_out4, 0);\r
+                       }\r
+                       break;\r
+               }\r
+       }\r
+       return smp;\r
+}\r
+\r
+static void chan_render_loop(chan_rend_context *ct, int *buffer, int length)\r
+{\r
+       int scounter;                                   /* sample counter */\r
+\r
+       /* sample generating loop */\r
+       for (scounter = 0; scounter < length; scounter++)\r
+       {\r
+               int smp = 0;            /* produced sample */\r
+               unsigned int eg_out, eg_out2, eg_out4;\r
+\r
+               ct->eg_timer += ct->eg_timer_add;\r
+               while (ct->eg_timer >= 1<<EG_SH) {\r
+                       ct->eg_timer -= 1<<EG_SH;\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
+                               ct->lfo_cnt += ct->lfo_inc;\r
+                       }\r
+                       if (ct->pack & 2)\r
+                               update_ssg_eg_channel(ct);\r
+\r
+                       if (ct->algo & 0x30)\r
+                               ct->algo -= 0x10;\r
+                       if (!(ct->algo & 0x30)) {\r
+                               ct->algo |= 0x30;\r
+                               ct->eg_cnt++;\r
+                               if (ct->eg_cnt >= 4096) ct->eg_cnt = 1;\r
+\r
+                               update_eg_phase_channel(ct);\r
+                       }\r
+\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
+\r
+                       if (ct->pack & 4) goto disabled; /* output disabled */\r
+\r
+                       /* calculate channel sample */\r
+                       if (ct->eg_timer < (2<<EG_SH) || (ct->pack&0xf000)) {\r
+                               eg_out = ct->vol_out1;\r
+                               if ( (ct->pack & 8) && (ct->pack&(1<<(SLOT1+8))) )\r
+                                       eg_out += ct->pack >> (((ct->pack&0xc0)>>6)+24);\r
+\r
+                               if( eg_out < ENV_QUIET )        /* SLOT 1 */\r
+                               {\r
+                                       int out = 0;\r
+\r
+                                       if (ct->pack&0xf000) out = ((ct->op1_out>>16) + ((ct->op1_out<<16)>>16)) << ((ct->pack&0xf000)>>12); /* op1_out0 + op1_out1 */\r
+                                       ct->op1_out <<= 16;\r
+                                       ct->op1_out |= (unsigned short)op_calc1(ct->phase1, eg_out, out);\r
+                               } else {\r
+                                       ct->op1_out <<= 16; /* op1_out0 = op1_out1; op1_out1 = 0; */\r
                                }\r
-                               if( eg_out4 < ENV_QUIET ) {             /* SLOT 4 */\r
-                                       smp += op_calc(ct->phase4, eg_out4, 0);\r
+                       }\r
+\r
+                       if (ct->eg_timer < (2<<EG_SH)) {\r
+                               eg_out  = ct->vol_out3; // volume_calc(&CH->SLOT[SLOT3]);\r
+                               eg_out2 = ct->vol_out2; // volume_calc(&CH->SLOT[SLOT2]);\r
+                               eg_out4 = ct->vol_out4; // volume_calc(&CH->SLOT[SLOT4]);\r
+\r
+                               if (ct->pack & 8) {\r
+                                       unsigned int add = ct->pack >> (((ct->pack&0xc0)>>6)+24);\r
+                                       if (ct->pack & (1<<(SLOT3+8))) eg_out  += add;\r
+                                       if (ct->pack & (1<<(SLOT2+8))) eg_out2 += add;\r
+                                       if (ct->pack & (1<<(SLOT4+8))) eg_out4 += add;\r
                                }\r
-                               break;\r
+\r
+                               smp = update_algo_channel(ct, eg_out, eg_out2, eg_out4);\r
                        }\r
+                       /* done calculating channel sample */\r
+\r
+disabled:\r
+                       /* update phase counters AFTER output calculations */\r
+                       ct->phase1 += ct->incr1;\r
+                       ct->phase2 += ct->incr2;\r
+                       ct->phase3 += ct->incr3;\r
+                       ct->phase4 += ct->incr4;\r
                }\r
-               /* done calculating channel sample */\r
 \r
                /* mix sample to output buffer */\r
                if (smp) {\r
@@ -1250,12 +1224,6 @@ static void chan_render_loop(chan_rend_context *ct, int *buffer, int length)
                        }\r
                        ct->algo |= 8;\r
                }\r
-\r
-               /* update phase counters AFTER output calculations */\r
-               ct->phase1 += ct->incr1;\r
-               ct->phase2 += ct->incr2;\r
-               ct->phase3 += ct->incr3;\r
-               ct->phase4 += ct->incr4;\r
        }\r
 }\r
 #else\r
@@ -1335,6 +1303,7 @@ static int chan_render(int *buffer, int length, int c, UINT32 flags) // flags: s
 \r
        crct.op1_out = crct.CH->op1_out;\r
        crct.algo = crct.CH->ALGO & 7;\r
+       crct.algo |= crct.CH->upd_cnt << 4;\r
        if (ym2612.OPN.ST.flags & ST_DAC)\r
                crct.algo |= 0x80;\r
 \r
@@ -1373,6 +1342,7 @@ static int chan_render(int *buffer, int length, int c, UINT32 flags) // flags: s
        }\r
        else\r
                ym2612.slot_mask &= ~(0xf << (c*4));\r
+       crct.CH->upd_cnt = (crct.algo >> 4) & 0x7;\r
 \r
        return (crct.algo & 8) >> 3; // had output\r
 }\r
@@ -1625,9 +1595,10 @@ static void OPNSetPres(int pres)
        int i;\r
 \r
        /* frequency base */\r
-       ym2612.OPN.ST.freqbase = (ym2612.OPN.ST.rate) ? ((double)ym2612.OPN.ST.clock / ym2612.OPN.ST.rate) / pres : 0;\r
+       double freqbase = (ym2612.OPN.ST.rate) ? ((double)ym2612.OPN.ST.clock / ym2612.OPN.ST.rate) / pres : 0;\r
 \r
-       ym2612.OPN.eg_timer_add  = (1<<EG_SH) * ym2612.OPN.ST.freqbase;\r
+       ym2612.OPN.eg_timer_add  = (1<<EG_SH) * freqbase;\r
+       ym2612.OPN.ST.freqbase = 1.0; // freqbase\r
 \r
        /* make time tables */\r
        init_timetables( dt_tab );\r
index 9d0f19a..8c7e801 100644 (file)
@@ -59,7 +59,7 @@ typedef struct
        UINT8   ssgn;\r
        UINT16  ar_ksr;         /* 0x32 ar+ksr */\r
        UINT16  vol_out;        /* 0x34 current output from EG (without LFO) */\r
-       UINT16  vol_ipol;       /* 0x36 interpolator memory */\r
+       UINT16  pad;\r
 } FM_SLOT;\r
 \r
 \r
@@ -79,7 +79,7 @@ typedef struct
 \r
        UINT8   kcode;          /* +11 key code:                        */\r
        UINT8   fn_h;           /* freq latch           */\r
-       UINT8   pad2;\r
+       UINT8   upd_cnt;        /* eg update counter */\r
        UINT32  fc;             /* fnum,blk:adjusted to sample rate */\r
        UINT32  block_fnum;     /* current blk/fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */\r
 \r
index bb74833..178972d 100644 (file)
@@ -16,7 +16,6 @@
 #include <pico/arm_features.h>
 
 @ very simple YM2612 output rate to sample rate adaption (~500k cycles @44100)
-#define INTERPOL
 #define SSG_EG
 
 .equiv SLOT1, 0
@@ -34,7 +33,6 @@
 .equiv EG_OFF, 0
 
 .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)
 @ r5=slot, r1=eg_cnt, trashes: r0,r2,r3
 @ writes output to routp, but only if vol_out changes
 .macro update_eg_phase_slot
-#if defined(INTERPOL)
-    ldrh    r0, [r5,#0x34]       @ vol_out
-#endif
     ldrb    r2, [r5,#0x17]       @ state
     add     r3, r5, #0x1c
-#if defined(INTERPOL)
-    strh    r0, [r5,#0x36]       @ vol_ipol
-#endif
     tst     r2, r2
     beq     0f                   @ EG_OFF
 
     cmp     r2, #EG_REL+1                 @   state > EG_REL &&
     cmpge   r3, #0x200                    @   volume >= 0x200?
     blt     9f
-    orr     r4, r4, #0x10                 @ ssg_update
 
     tst     r0, #0x01
     beq     1f
 
 @ r5=slot, trashes: r0,r2,r3
 .macro recalc_volout
-#if defined(INTERPOL)
-    ldrh    r0, [r5,#0x34]                @ vol_out
-#endif
     ldrb    r2, [r5,#0x30]                @ ssg
     ldrb    r3, [r5,#0x17]                @ state
-#if defined(INTERPOL)
-    strh    r0, [r5,#0x36]                @ vol_ipol
-#endif
     ldrh    r0, [r5,#0x1a]                @ volume
 
 @    and     r2, r2, #0x0c
 @ r0-r2=scratch, r3=sin_tab, r5=scratch, r6-r7=vol_out[4], r10=op1_out
 .macro upd_algo0_m
 
+    cmp     r8, #(1<<EG_SH)
+    bge     1f
+
     @ SLOT3
     make_eg_out SLOT3
     cmp     r1, #ENV_QUIET
 
 .macro upd_algo1_m
 
+    cmp     r8, #(1<<EG_SH)
+    bge     1f
+
     @ SLOT3
     make_eg_out SLOT3
     cmp     r1, #ENV_QUIET
 
 .macro upd_algo2_m
 
+    cmp     r8, #(1<<EG_SH)
+    bge     1f
+
     @ SLOT3
     make_eg_out SLOT3
     cmp     r1, #ENV_QUIET
 
 .macro upd_algo3_m
 
+    cmp     r8, #(1<<EG_SH)
+    bge     1f
+
     @ SLOT3
     make_eg_out SLOT3
     cmp     r1, #ENV_QUIET
 
 .macro upd_algo4_m
 
+    cmp     r8, #(1<<EG_SH)
+    bge     2f
+
     @ SLOT3
     make_eg_out SLOT3
     cmp     r1, #ENV_QUIET
 
 .macro upd_algo5_m
 
+    cmp     r8, #(1<<EG_SH)
+    bge     2f
+
     @ SLOT3
     make_eg_out SLOT3
     cmp     r1, #ENV_QUIET
 
 .macro upd_algo6_m
 
+    cmp     r8, #(1<<EG_SH)
+    bge     2f
+
     @ SLOT3
     make_eg_out SLOT3
     cmp     r1, #ENV_QUIET
 
 .macro upd_algo7_m
 
+    cmp     r8, #(1<<EG_SH)
+    bge     2f
+
     @ SLOT3
     make_eg_out SLOT3
     cmp     r1, #ENV_QUIET
 
 
 @ lr=context, r12=pack (stereo, ssg_enabled, 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)|dac,unused[2],ssg_update,was_update,algo[3], r5=tl_tab/slot,
+@ r0-r2=scratch, r3=sin_tab/scratch, r4=(length<<8)|dac,upd_cnt[3],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
 .global chan_render_loop @ chan_rend_context *ct, int *buffer, int length
 
@@ -688,21 +697,32 @@ chan_render_loop:
     ldr     r12, [lr, #0x4c]
     ldr     r0,  [lr, #0x50]
     mov     r11, r1
-    and     r0,  r0, #0x87
-    orr     r4,  r4, r0          @ (length<<8)|dac,unused[4],algo[3]
+    and     r0,  r0, #0xf7
+    orr     r4,  r4, r0          @ (length<<8)|dac,upd_cnt[2],unused,algo[3]
     ldr     r8, [lr, #0x44]      @ eg_timer
     ldr     r9, [lr, #0x48]      @ eg_timer_add
     ldr     r10, [lr, #0x54]     @ op1_out
 
+crl_loop:
+    subs    r4, r4, #0x100
+    bmi     crl_loop_end
+
+    mov     r0, #0
+    add     r8, r8, r9
+    subs    r8, r8, #(1<<EG_SH)
+    blt     crl_smp_loop_end
+
+
+    @ r12=lfo_ampm[31:16], r1=lfo_cnt_old, r2=lfo_cnt
+    advance_lfo_m
+
+crl_smp_loop:
     tst     r12, #8              @ lfo?
-    beq     crl_loop
+    beq     lfo_done
 
-crl_loop_lfo:
-    ldr     r1, [lr, #0x30]      @ lfo_cnt
-    ldr     r2, [lr, #0x34]      @ lfo_inc
 
-    subs    r4, r4, #0x100
-    bmi     crl_loop_end
+    ldr     r2, [lr, #0x34]      @ lfo_inc
+    ldr     r1, [lr, #0x30]      @ lfo_cnt
 
     add     r2, r2, r1
     str     r2, [lr, #0x30]
@@ -710,25 +730,15 @@ crl_loop_lfo:
     @ r12=lfo_ampm[31:16], r1=lfo_cnt_old, r2=lfo_cnt
     advance_lfo_m
 
-    add     r4, r4, #0x100
-
-crl_loop:
-    subs    r4, r4, #0x100
-    bmi     crl_loop_end
-
+lfo_done:
     ldr     r5, [lr, #0x40]      @ CH
 #if defined(SSG_EG)
     tst     r12, #0x02              @ ssg_enabled?
     beq     ssg_done
-    @ -- SSG --
-    lsl     r7, r8, #EG_SH
-    add     r7, r9, r7, lsr #EG_SH
-    subs    r7, r7, #1<<EG_SH
-    blt     ssg_done
 
+    @ -- SSG --
 ssg_loop:
     mov     r6, #4
-    bic     r4, r4, #0x10           @ ssg_update
 ssg_upd_loop:
     @ use lr as a pointer to the slot phases stored in the context
     update_ssg_eg
@@ -748,26 +758,25 @@ ssg_upd_loop:
     sub     lr, lr, #4*3
     sub     r5, r5, #SLOT_STRUCT_SIZE*3
 
-    subs    r7, r7, #1<<EG_SH
-    bge     ssg_loop
 ssg_done:
 #endif
 
     @ -- EG --
-    add     r8, r8, r9
-    cmp     r8, #EG_TIMER_OVERFLOW
-    blo     volout_upd
-    ldr     r1, [lr, #0x3c]     @ eg_cnt
-eg_loop:
-    sub     r8, r8, #EG_TIMER_OVERFLOW
+    tst     r4, #0x30
+    subnes  r4, r4, #0x10
+    bne     eg_done
+    orr     r4, r4, #0x30
+
+    ldr     r1, [lr, #0x3c]      @ eg_cnt
     add     r1, r1, #1
     cmp     r1, #4096
     movge   r1, #1
+    str     r1, [lr, #0x3c]
 
     mov     r6, #4
 eg_upd_loop:
     update_eg_phase_slot
-#if 1
+#if 0
     subs    r6, r6, #1
     addne   r5, r5, #SLOT_STRUCT_SIZE
 #else
@@ -777,96 +786,38 @@ eg_upd_loop:
     subne   r5, r5, #SLOT_STRUCT_SIZE
 #endif
     bne     eg_upd_loop
-
-    cmp     r8, #EG_TIMER_OVERFLOW
     sub     r5, r5, #SLOT_STRUCT_SIZE*3
-    bhs     eg_loop
-    str     r1, [lr, #0x3c]
-    b       eg_done
-
-volout_upd:
-#if defined(SSG_EG)
-    tst     r4, #0x10               @ ssg_update?
-    beq     eg_done
-
-    @ recalc vol_out
-    mov     r6, #4
-volout_loop:
-    recalc_volout
-#if 0
-    subs    r6, r6, #1
-    addne   r5, r5, #SLOT_STRUCT_SIZE
-#else
-    add     r5, r5, #SLOT_STRUCT_SIZE*2
-    recalc_volout
-    subs    r6, r6, #2
-    subne   r5, r5, #SLOT_STRUCT_SIZE
-#endif
-    bne     volout_loop
-    sub     r5, r5, #SLOT_STRUCT_SIZE*3
-#endif
 
 eg_done:
     @ -- disabled? --
-    and     r0, r12, #0xC
-    cmp     r0, #0xC
-    beq     crl_loop_lfo
-    cmp     r0, #0x4
-    beq     crl_loop
-
-    @ output interpolation
-#if defined(INTERPOL)
-#if 1 // possibly too expensive for slow platforms?
-    @ 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
-    bne     0f                      @ mix is vol_out
-
-    ldr     r6, [r5, #0x34]      @ vol_out, vol_ipol for all slots
-    ldr     r2, [r5, #0x34+SLOT_STRUCT_SIZE*2]
-    ldr     r7, [r5, #0x34+SLOT_STRUCT_SIZE]
-    ldr     r3, [r5, #0x34+SLOT_STRUCT_SIZE*3]
-    add     r6, r6, r6, lsl #16
-    lsr     r6, r6, #17
-    add     r2, r2, r2, lsl #16
-    lsr     r2, r2, #17
-    add     r7, r7, r7, lsl #16
-    lsr     r7, r7, #17
-    add     r3, r3, r3, lsl #16
-    lsr     r3, r3, #17
-    b       1f
-#else
-    @ 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)
-#endif
+    tst     r12, #0x4
+    mov     r0, #0
+    bne     crl_algo_done
 
-0:  ldrgeh  r6, [r5, #0x34]      @ vol_out values for all slots
-    ldrlth  r6, [r5, #0x36]      @ vol_ipol values for all slots
-    ldrgeh  r2, [r5, #0x34+SLOT_STRUCT_SIZE*2]
-    ldrlth  r2, [r5, #0x36+SLOT_STRUCT_SIZE*2]
-    ldrgeh  r7, [r5, #0x34+SLOT_STRUCT_SIZE]
-    ldrlth  r7, [r5, #0x36+SLOT_STRUCT_SIZE]
-    ldrgeh  r3, [r5, #0x34+SLOT_STRUCT_SIZE*3]
-    ldrlth  r3, [r5, #0x36+SLOT_STRUCT_SIZE*3]
+    cmp     r8, #(2<<EG_SH)      @ calculate only for operator memory, sample,
+    tstge   r12, #0xf000         @ ...feedback
+    beq     crl_algo_done
 
-#else
     ldrh    r6, [r5, #0x34]      @ vol_out values for all slots
     ldrh    r2, [r5, #0x34+SLOT_STRUCT_SIZE*2]
     ldrh    r7, [r5, #0x34+SLOT_STRUCT_SIZE]
     ldrh    r3, [r5, #0x34+SLOT_STRUCT_SIZE*3]
-#endif
-1:  orr     r6, r6, r2, lsl #16
+
+    orr     r6, r6, r2, lsl #16
     orr     r7, r7, r3, lsl #16
 
-    @ -- SLOT1 --
     PIC_LDR(r3, r2, ym_tl_tab)
 
     @ lr=context, r12=pack (stereo, ssg_enabled, disabled, lfo_enabled | pan_r, pan_l, ams[2] | AMmasks[4] | FB[4] | lfo_ampm[16])
     @ r0-r2=scratch, r3=tl_tab, r5=scratch, r6-r7=vol_out[4], r10=op1_out
+
+    @ -- SLOT1 --
     upd_slot1_m
 
     @ -- SLOT2+ --
+    cmp     r8, #(2<<EG_SH)      @ op mem or sample?
+    bge     crl_algo_done
+
     and     r0, r4, #7
     PIC_XB(,r0, lsl #2)
     nop
@@ -920,6 +871,23 @@ crl_algo7:
 
 
 crl_algo_done:
+    @ -- PHASE UPDATE --
+    add     lr, lr, #0x10
+    ldmia   lr, {r1-r3,r5-r7}
+    add     r1, r1, r6
+    add     r2, r2, r7
+    ldr     r6, [lr, #0x18]
+    ldr     r7, [lr, #0x1c]
+    add     r3, r3, r6
+    add     r5, r5, r7
+    stmia   lr, {r1-r3,r5}
+    sub     lr, lr, #0x10
+
+    subs    r8, r8, #(1<<EG_SH)
+    bge     crl_smp_loop
+
+crl_smp_loop_end:
+    add     r8, r8, #(1<<EG_SH)
     @ -- WRITE SAMPLE --
     tst     r0, r0
     beq     ctl_sample_skip
@@ -944,36 +912,20 @@ crl_algo_done:
     addeq   r11, r11, #4
     addne   r1, r0, r1
     strne   r1, [r11], #4
-    b       crl_do_phase
+    b       crl_loop
 
 ctl_sample_mono:
     ldr     r1, [r11]
     add     r1, r0, r1
     str     r1, [r11], #4
-    b       crl_do_phase
+    b       crl_loop
 
 ctl_sample_skip:
     and     r1, r12, #1
     add     r1, r1,  #1
     add     r11,r11, r1, lsl #2
-
-crl_do_phase:
-    @ -- PHASE UPDATE --
-    add     r5, lr, #0x10
-    ldmia   r5, {r0-r3,r6-r7}
-    add     r0, r0, r6
-    add     r1, r1, r7
-    ldr     r6, [r5, #0x18]
-    ldr     r7, [r5, #0x1c]
-    add     r2, r2, r6
-    add     r3, r3, r7
-    stmia   r5, {r0-r3}
-
-    tst     r12, #8
-    bne     crl_loop_lfo
     b       crl_loop
 
-
 crl_loop_end:
     str     r8,  [lr, #0x44]     @ eg_timer
     str     r12, [lr, #0x4c]     @ pack (for lfo_ampm)