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
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;
}\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
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
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
#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
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;
}
}
- // 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);
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);
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;
#ifdef PICO_32X
p32x_sync_sh2s(cycles);
#endif
+
+ // get samples from sound chips
+ if (PicoIn.sndOut)
+ PsndGetSamples(y);
+
timers_cycle();
pv->hint_cnt = hint;
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
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
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
}
}
- // 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)
#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
ldmfd sp!, {r4-r11,lr}
bx lr
+#endif /* __GP2X__ */
+
.global mix_reset @ void
mix_reset:
ldr r0, =filter
bx lr
.data
- DCfilt r4, r10
- DCfilt r5, r11
filter:
.ds 8
-#endif /* __GP2X__ */
-
@ vim:filetype=armasm
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
{\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
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
}\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
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
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
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
\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
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
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
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
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
** 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
\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
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
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
{\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
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
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
{\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
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
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
{\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
{\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
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
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
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
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
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
};\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
#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
/*
* 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.
.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
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
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
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:
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)