mirror of
https://github.com/ekeeke/Genesis-Plus-GX.git
synced 2025-01-12 19:29:07 +01:00
improved YM2612 emulation
This commit is contained in:
parent
0c3c721bd0
commit
fbbd1d6057
@ -5,7 +5,7 @@ CURRENT:
|
|||||||
---------
|
---------
|
||||||
[Genesis]
|
[Genesis]
|
||||||
- YM2612(MAME): improved Enveloppe Generator accuracy: fix tracks #3 and #9 in Mega Turrican
|
- YM2612(MAME): improved Enveloppe Generator accuracy: fix tracks #3 and #9 in Mega Turrican
|
||||||
|
- YM2612(MAME): fixed SSG-EG emulation bug: fix Level 1 music in Alisia Dragoon
|
||||||
|
|
||||||
[NGC/Wii]
|
[NGC/Wii]
|
||||||
- improved menu scrolling using Wiimote D-PAD
|
- improved menu scrolling using Wiimote D-PAD
|
||||||
|
@ -18,12 +18,12 @@
|
|||||||
** - fixed CH3 CSM mode (credits to Nemesis)
|
** - fixed CH3 CSM mode (credits to Nemesis)
|
||||||
** - implemented PG overflow, aka "detune bug" (Ariel, Comix Zone, Shaq Fu, Spiderman,...), credits to Nemesis
|
** - implemented PG overflow, aka "detune bug" (Ariel, Comix Zone, Shaq Fu, Spiderman,...), credits to Nemesis
|
||||||
** - fixed SSG-EG support, credits to Nemesis
|
** - fixed SSG-EG support, credits to Nemesis
|
||||||
** - modified EG rates and frequency, tested by Nemesis on real hardware
|
** - modified EG rates, tested by Nemesis on real hardware
|
||||||
** - fixed EG attenuation level on KEY ON (Ecco 2 splash sound)
|
** - fixed EG attenuation level on KEY ON (Ecco 2 splash sound)
|
||||||
** - fixed LFO phase update for CH3 special mode (Warlock, Alladin), thanks to AamirM
|
** - fixed LFO phase update for CH3 special mode (Warlock, Alladin), thanks to AamirM
|
||||||
** - fixed Attack rate refresh (fix Batman&Robin introduction)$
|
** - fixed Attack rate refresh (fix Batman&Robin introduction)$
|
||||||
** - fixed attenuation level when starting Substain Phase (Gynoug ?)
|
** - fixed attenuation level at the start of Substain (Gynoug ?)
|
||||||
** - fixed Enveloppe Generator updates in some specific cases (AR maximal and/or Susbstain Level minimal)
|
** - fixed EG updates in some specific cases (AR maximal and/or Susbstain Level minimal)
|
||||||
**
|
**
|
||||||
** 03-08-2003 Jarek Burczynski:
|
** 03-08-2003 Jarek Burczynski:
|
||||||
** - fixed YM2608 initial values (after the reset)
|
** - fixed YM2608 initial values (after the reset)
|
||||||
@ -654,7 +654,7 @@ INLINE void FM_KEYON(FM_CH *CH , int s )
|
|||||||
SLOT->key = 1;
|
SLOT->key = 1;
|
||||||
SLOT->phase = 0; /* restart Phase Generator */
|
SLOT->phase = 0; /* restart Phase Generator */
|
||||||
SLOT->ssgn = (SLOT->ssg & 0x04) >> 1;
|
SLOT->ssgn = (SLOT->ssg & 0x04) >> 1;
|
||||||
|
|
||||||
if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/)
|
if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/)
|
||||||
{
|
{
|
||||||
SLOT->state = EG_ATT; /* phase -> Attack */
|
SLOT->state = EG_ATT; /* phase -> Attack */
|
||||||
@ -928,10 +928,7 @@ INLINE void advance_eg_channel(FM_SLOT *SLOT)
|
|||||||
case EG_ATT: /* attack phase */
|
case EG_ATT: /* attack phase */
|
||||||
if (!(ym2612.OPN.eg_cnt & ((1<<SLOT->eg_sh_ar)-1)))
|
if (!(ym2612.OPN.eg_cnt & ((1<<SLOT->eg_sh_ar)-1)))
|
||||||
{
|
{
|
||||||
SLOT->volume += (~SLOT->volume *
|
SLOT->volume += (~SLOT->volume * (eg_inc[SLOT->eg_sel_ar + ((ym2612.OPN.eg_cnt>>SLOT->eg_sh_ar)&7)]))>>4;
|
||||||
(eg_inc[SLOT->eg_sel_ar + ((ym2612.OPN.eg_cnt>>SLOT->eg_sh_ar)&7)])
|
|
||||||
)>>4;
|
|
||||||
|
|
||||||
if (SLOT->volume <= MIN_ATT_INDEX)
|
if (SLOT->volume <= MIN_ATT_INDEX)
|
||||||
{
|
{
|
||||||
SLOT->volume = MIN_ATT_INDEX;
|
SLOT->volume = MIN_ATT_INDEX;
|
||||||
@ -1053,7 +1050,7 @@ INLINE void advance_eg_channel(FM_SLOT *SLOT)
|
|||||||
out = (UINT32)SLOT->volume;
|
out = (UINT32)SLOT->volume;
|
||||||
|
|
||||||
/* negate output (changes come from alternate bit, init comes from attack bit) */
|
/* negate output (changes come from alternate bit, init comes from attack bit) */
|
||||||
if ((SLOT->ssg&0x08) && (SLOT->ssgn&2))
|
if ((SLOT->ssg&0x08) && (SLOT->ssgn&2) && (SLOT->state > EG_REL))
|
||||||
out ^= MAX_ATT_INDEX;
|
out ^= MAX_ATT_INDEX;
|
||||||
|
|
||||||
/* we need to store the result here because we are going to change ssgn
|
/* we need to store the result here because we are going to change ssgn
|
||||||
@ -1206,10 +1203,10 @@ INLINE void chan_calc(FM_CH *CH)
|
|||||||
/* add support for 3 slot mode */
|
/* add support for 3 slot mode */
|
||||||
if ((ym2612.OPN.ST.mode & 0xC0) && (CH == &ym2612.CH[2]))
|
if ((ym2612.OPN.ST.mode & 0xC0) && (CH == &ym2612.CH[2]))
|
||||||
{
|
{
|
||||||
update_phase_lfo_slot(&ym2612.CH[2].SLOT[SLOT1], ym2612.CH[2].pms, ym2612.OPN.SL3.block_fnum[1]);
|
update_phase_lfo_slot(&ym2612.CH[2].SLOT[SLOT1], CH->pms, ym2612.OPN.SL3.block_fnum[1]);
|
||||||
update_phase_lfo_slot(&ym2612.CH[2].SLOT[SLOT2], ym2612.CH[2].pms, ym2612.OPN.SL3.block_fnum[2]);
|
update_phase_lfo_slot(&ym2612.CH[2].SLOT[SLOT2], CH->pms, ym2612.OPN.SL3.block_fnum[2]);
|
||||||
update_phase_lfo_slot(&ym2612.CH[2].SLOT[SLOT3], ym2612.CH[2].pms, ym2612.OPN.SL3.block_fnum[0]);
|
update_phase_lfo_slot(&ym2612.CH[2].SLOT[SLOT3], CH->pms, ym2612.OPN.SL3.block_fnum[0]);
|
||||||
update_phase_lfo_slot(&ym2612.CH[2].SLOT[SLOT4], ym2612.CH[2].pms, ym2612.CH[2].block_fnum);
|
update_phase_lfo_slot(&ym2612.CH[2].SLOT[SLOT4], CH->pms, CH->block_fnum);
|
||||||
}
|
}
|
||||||
else update_phase_lfo_channel(CH);
|
else update_phase_lfo_channel(CH);
|
||||||
}
|
}
|
||||||
@ -1404,22 +1401,33 @@ static int init_tables(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* CSM Key Controll */
|
/* CSM Key Controll (correct implementation, credits to Nemesis) */
|
||||||
INLINE void CSMKeyControll(FM_CH *CH)
|
INLINE void CSMKeyControll(FM_CH *CH)
|
||||||
{
|
{
|
||||||
/* correct implementation (credits to Nemesis) */
|
/* all key on/off */
|
||||||
|
if (CH->SLOT[SLOT1].state == EG_REL)
|
||||||
|
{
|
||||||
|
FM_KEYON(CH,SLOT1);
|
||||||
|
FM_KEYOFF(CH,SLOT1);
|
||||||
|
}
|
||||||
|
|
||||||
/* all key on */
|
if (CH->SLOT[SLOT2].state == EG_REL)
|
||||||
FM_KEYON(CH,SLOT1);
|
{
|
||||||
FM_KEYON(CH,SLOT2);
|
FM_KEYON(CH,SLOT2);
|
||||||
FM_KEYON(CH,SLOT3);
|
FM_KEYOFF(CH,SLOT2);
|
||||||
FM_KEYON(CH,SLOT4);
|
}
|
||||||
|
|
||||||
/* all key off */
|
if (CH->SLOT[SLOT3].state == EG_REL)
|
||||||
FM_KEYOFF(CH,SLOT1);
|
{
|
||||||
FM_KEYOFF(CH,SLOT2);
|
FM_KEYON(CH,SLOT3);
|
||||||
FM_KEYOFF(CH,SLOT3);
|
FM_KEYOFF(CH,SLOT3);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CH->SLOT[SLOT4].state == EG_REL)
|
||||||
|
{
|
||||||
|
FM_KEYON(CH,SLOT4);
|
||||||
FM_KEYOFF(CH,SLOT4);
|
FM_KEYOFF(CH,SLOT4);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void INTERNAL_TIMER_A()
|
static void INTERNAL_TIMER_A()
|
||||||
@ -1466,13 +1474,13 @@ static void OPNSetPres(int pres)
|
|||||||
/* YM2612 running at original frequency (~53 kHz) */
|
/* YM2612 running at original frequency (~53 kHz) */
|
||||||
if (config.hq_fm) ym2612.OPN.ST.freqbase = 1.0;
|
if (config.hq_fm) ym2612.OPN.ST.freqbase = 1.0;
|
||||||
|
|
||||||
|
ym2612.OPN.eg_timer_add = (UINT32)((1<<EG_SH) * ym2612.OPN.ST.freqbase);
|
||||||
|
ym2612.OPN.eg_timer_overflow = ( 3 ) * (1<<EG_SH);
|
||||||
|
|
||||||
/* timer increment in usecs (timers are incremented after each updated samples) */
|
/* timer increment in usecs (timers are incremented after each updated samples) */
|
||||||
ym2612.OPN.ST.TimerBase = (int) (ym2612.OPN.ST.freqbase * 4096.0);
|
ym2612.OPN.ST.TimerBase = (int) (ym2612.OPN.ST.freqbase * 4096.0);
|
||||||
|
|
||||||
ym2612.OPN.eg_timer_add = (UINT32)((1<<EG_SH) * ym2612.OPN.ST.freqbase);
|
|
||||||
|
|
||||||
//ym2612.OPN.eg_timer_overflow = ( 3 ) * (1<<EG_SH);
|
|
||||||
ym2612.OPN.eg_timer_overflow = (351 * (1<<EG_SH)) / 144; /* correct frequency (Nemesis: tested on real HW) */
|
|
||||||
|
|
||||||
/* make time tables */
|
/* make time tables */
|
||||||
init_timetables(dt_tab);
|
init_timetables(dt_tab);
|
||||||
@ -1785,21 +1793,6 @@ void YM2612UpdateOne(int **buffer, int length)
|
|||||||
out_fm[4] = 0;
|
out_fm[4] = 0;
|
||||||
out_fm[5] = 0;
|
out_fm[5] = 0;
|
||||||
|
|
||||||
/* advance envelope generator */
|
|
||||||
ym2612.OPN.eg_timer += ym2612.OPN.eg_timer_add;
|
|
||||||
while (ym2612.OPN.eg_timer >= ym2612.OPN.eg_timer_overflow)
|
|
||||||
{
|
|
||||||
ym2612.OPN.eg_timer -= ym2612.OPN.eg_timer_overflow;
|
|
||||||
ym2612.OPN.eg_cnt++;
|
|
||||||
|
|
||||||
advance_eg_channel(&ym2612.CH[0].SLOT[SLOT1]);
|
|
||||||
advance_eg_channel(&ym2612.CH[1].SLOT[SLOT1]);
|
|
||||||
advance_eg_channel(&ym2612.CH[2].SLOT[SLOT1]);
|
|
||||||
advance_eg_channel(&ym2612.CH[3].SLOT[SLOT1]);
|
|
||||||
advance_eg_channel(&ym2612.CH[4].SLOT[SLOT1]);
|
|
||||||
advance_eg_channel(&ym2612.CH[5].SLOT[SLOT1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* calculate FM */
|
/* calculate FM */
|
||||||
chan_calc(&ym2612.CH[0]);
|
chan_calc(&ym2612.CH[0]);
|
||||||
chan_calc(&ym2612.CH[1]);
|
chan_calc(&ym2612.CH[1]);
|
||||||
@ -1814,6 +1807,21 @@ void YM2612UpdateOne(int **buffer, int length)
|
|||||||
}
|
}
|
||||||
else chan_calc(&ym2612.CH[5]);
|
else chan_calc(&ym2612.CH[5]);
|
||||||
|
|
||||||
|
/* advance envelope generator */
|
||||||
|
ym2612.OPN.eg_timer += ym2612.OPN.eg_timer_add;
|
||||||
|
while (ym2612.OPN.eg_timer >= ym2612.OPN.eg_timer_overflow)
|
||||||
|
{
|
||||||
|
ym2612.OPN.eg_timer -= ym2612.OPN.eg_timer_overflow;
|
||||||
|
ym2612.OPN.eg_cnt++;
|
||||||
|
|
||||||
|
advance_eg_channel(&ym2612.CH[0].SLOT[SLOT1]);
|
||||||
|
advance_eg_channel(&ym2612.CH[1].SLOT[SLOT1]);
|
||||||
|
advance_eg_channel(&ym2612.CH[2].SLOT[SLOT1]);
|
||||||
|
advance_eg_channel(&ym2612.CH[3].SLOT[SLOT1]);
|
||||||
|
advance_eg_channel(&ym2612.CH[4].SLOT[SLOT1]);
|
||||||
|
advance_eg_channel(&ym2612.CH[5].SLOT[SLOT1]);
|
||||||
|
}
|
||||||
|
|
||||||
lt = ((out_fm[0]>>0) & ym2612.OPN.pan[0]);
|
lt = ((out_fm[0]>>0) & ym2612.OPN.pan[0]);
|
||||||
rt = ((out_fm[0]>>0) & ym2612.OPN.pan[1]);
|
rt = ((out_fm[0]>>0) & ym2612.OPN.pan[1]);
|
||||||
lt += ((out_fm[1]>>0) & ym2612.OPN.pan[2]);
|
lt += ((out_fm[1]>>0) & ym2612.OPN.pan[2]);
|
||||||
|
@ -218,9 +218,12 @@ void fm_restore(void)
|
|||||||
/* write FM chip */
|
/* write FM chip */
|
||||||
void fm_write(unsigned int cpu, unsigned int address, unsigned int data)
|
void fm_write(unsigned int cpu, unsigned int address, unsigned int data)
|
||||||
{
|
{
|
||||||
|
if (address & 1)
|
||||||
|
{
|
||||||
snd.fm.curStage = fm_sample_cnt(cpu);
|
snd.fm.curStage = fm_sample_cnt(cpu);
|
||||||
fm_update();
|
fm_update();
|
||||||
_YM2612_Write(address & 3, data);
|
}
|
||||||
|
_YM2612_Write(address & 3, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* read FM status */
|
/* read FM status */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user