#define POPT_EN_PWM (1<<21)\r
#define POPT_PWM_IRQ_OPT (1<<22)\r
#define POPT_DIS_FM_SSGEG (1<<23)\r
+#define POPT_EN_FM_LADDER (1<<24) //x00 0000\r
\r
#define PAHW_MCD (1<<0)\r
#define PAHW_32X (1<<1)\r
ym2612_pack_state();\r
memcpy(state, YM2612GetRegs(), 0x204);\r
}\r
- YM2612Init(Pico.m.pal ? OSC_PAL/7 : OSC_NTSC/7, PicoIn.sndRate, !(PicoIn.opt&POPT_DIS_FM_SSGEG));\r
+ YM2612Init(Pico.m.pal ? OSC_PAL/7 : OSC_NTSC/7, PicoIn.sndRate,\r
+ ((PicoIn.opt&POPT_DIS_FM_SSGEG) ? 0 : ST_SSG) |\r
+ ((PicoIn.opt&POPT_EN_FM_LADDER) ? ST_LADDER : 0));\r
if (preserve_state) {\r
// feed it back it's own registers, just like after loading state\r
memcpy(YM2612GetRegs(), state, 0x204);\r
\r
\r
#if !defined(_ASM_YM2612_C) || defined(EXTERNAL_YM2612)\r
+#include <limits.h>\r
+static int clip(int n) \r
+{\r
+ unsigned b = 14, s = n < 0;\r
+ if (s + (n>>(b-1))) n = (int)(s + INT_MAX) >> (8*sizeof(int)-b);\r
+ return n;\r
+}\r
+\r
static void chan_render_loop(chan_rend_context *ct, int *buffer, int length)\r
{\r
int scounter; /* sample counter */\r
\r
/* mix sample to output buffer */\r
if (smp) {\r
+ smp = clip(smp); /* saturate to 14 bit */\r
if (ct->pack & 1) { /* stereo */\r
if (ct->pack & 0x20) /* L */ /* TODO: check correctness */\r
buffer[scounter*2] += smp;\r
crct.lfo_inc = ym2612.OPN.lfo_inc;\r
}\r
\r
-static void chan_render_finish(void)\r
+static void chan_render_finish(int *buffer, unsigned short length, int active_chans)\r
{\r
ym2612.OPN.eg_cnt = crct.eg_cnt;\r
ym2612.OPN.eg_timer = crct.eg_timer;\r
g_lfo_ampm = crct.pack >> 16; // need_save\r
ym2612.OPN.lfo_cnt = crct.lfo_cnt;\r
+\r
+ /* apply ladder effect. NB only works if buffer was empty beforehand! */\r
+ if (active_chans && (ym2612.OPN.ST.flags & ST_LADDER)) {\r
+ length <<= crct.pack & 1;\r
+ while (length--) {\r
+ *buffer -= (*buffer < 0)*4 << 5;\r
+ buffer++;\r
+ }\r
+ }\r
}\r
\r
static UINT32 update_lfo_phase(FM_SLOT *SLOT, UINT32 block_fnum)\r
BIT_IF(flags, 1, (ym2612.ssg_mask & 0xf00000) && (ym2612.OPN.ST.flags & 1));\r
if (ym2612.slot_mask & 0xf00000) active_chs |= chan_render(buffer, length, 5, flags|((pan&0xc00)>>6)|(!!ym2612.dacen<<2)) << 5;\r
#undef BIT_IF\r
- chan_render_finish();\r
+ chan_render_finish(buffer, length, active_chs);\r
\r
return active_chs; // 1 if buffer updated\r
}\r
\r
\r
/* initialize YM2612 emulator */\r
-void YM2612Init_(int clock, int rate, int ssg)\r
+void YM2612Init_(int clock, int rate, int flags)\r
{\r
memset(&ym2612, 0, sizeof(ym2612));\r
init_tables();\r
\r
ym2612.OPN.ST.clock = clock;\r
ym2612.OPN.ST.rate = rate;\r
- ym2612.OPN.ST.flags = (ssg ? 1:0);\r
+ ym2612.OPN.ST.flags = flags;\r
\r
OPNSetPres( 6*24 );\r
\r
INT32 dt_tab[8][32];/* DeTune table */\r
} FM_ST;\r
\r
+#define ST_SSG 1\r
+#define ST_LADDER 2\r
+\r
/***********************************************************/\r
/* OPN unit */\r
/***********************************************************/\r
extern YM2612 ym2612;\r
#endif\r
\r
-void YM2612Init_(int baseclock, int rate, int ssg);\r
+void YM2612Init_(int baseclock, int rate, int flags);\r
void YM2612ResetChip_(void);\r
int YM2612UpdateOne_(int *buffer, int length, int stereo, int is_buf_empty);\r
\r
#else\r
/* GP2X specific */\r
#include <platform/gp2x/940ctl.h>\r
-#define YM2612Init(baseclock,rate,ssg) do { \\r
- if (PicoIn.opt&POPT_EXT_FM) YM2612Init_940(baseclock, rate, ssg); \\r
- else YM2612Init_(baseclock, rate, ssg); \\r
+#define YM2612Init(baseclock,rate,flags) do { \\r
+ if (PicoIn.opt&POPT_EXT_FM) YM2612Init_940(baseclock, rate, flags); \\r
+ else YM2612Init_(baseclock, rate, flags); \\r
} while (0)\r
#define YM2612ResetChip() do { \\r
if (PicoIn.opt&POPT_EXT_FM) YM2612ResetChip_940(); \\r
tst r0, r0
beq ctl_sample_skip
orr r4, r4, #8 @ have_output
+ lsr r1, r0, #31 @ clip (saturate) sample to 14 bit
+ adds r2, r1, r0, asr #13
+ subne r0, r1, #0x80000001
+ asrne r0, r0, #18
tst r12, #1
beq ctl_sample_mono
mee_onoff ("Emulate Z80", MA_OPT2_ENABLE_Z80, PicoIn.opt, POPT_EN_Z80),
mee_onoff ("Emulate YM2612 (FM)", MA_OPT2_ENABLE_YM2612, PicoIn.opt, POPT_EN_FM),
mee_onoff ("Disable YM2612 SSG-EG", MA_OPT2_DISABLE_YM_SSG,PicoIn.opt, POPT_DIS_FM_SSGEG),
+ mee_onoff ("Enable YM2612 ladder effect",MA_OPT2_DISABLE_YM_LAD,PicoIn.opt, POPT_EN_FM_LADDER),
mee_onoff ("Emulate SN76496 (PSG)", MA_OPT2_ENABLE_SN76496,PicoIn.opt, POPT_EN_PSG),
mee_onoff ("Emulate Game Gear LCD", MA_OPT2_ENABLE_GGLCD ,PicoIn.opt, POPT_EN_GG_LCD),
mee_onoff ("Disable idle loop patching",MA_OPT2_NO_IDLE_LOOPS,PicoIn.opt, POPT_DIS_IDLE_DET),
MA_OPT2_ENABLE_Z80,
MA_OPT2_ENABLE_YM2612,
MA_OPT2_DISABLE_YM_SSG,
+ MA_OPT2_DISABLE_YM_LAD,
MA_OPT2_ENABLE_SN76496,
MA_OPT2_ENABLE_YM2413,
MA_OPT2_ENABLE_GGLCD,