From 4eb8d17acfbab58e007520f03fc66ac1e1160d1d Mon Sep 17 00:00:00 2001 From: ekeeke31 Date: Wed, 25 Feb 2009 16:53:28 +0000 Subject: [PATCH] [-]removed Gens YM2612 core support [~]fixed YM2612 context restore [+]improved YM2612 emulation accuracy (BROKEN) --- history.txt | 8 +- source/gen_input.c | 2 +- source/genesis.c | 4 +- source/ngc/config.c | 5 +- source/ngc/config.h | 1 - source/ngc/filemem.c | 2 +- source/ngc/gui/menu.c | 27 +- source/shared.h | 1 - source/sound/fm.c | 601 +++++++++++++++++++++++++++--------------- source/sound/fm.h | 7 +- source/sound/sound.c | 55 +--- source/sound/sound.h | 4 - source/state.c | 46 ++-- source/state.h | 2 + source/system.c | 2 +- source/vdp.c | 10 +- 16 files changed, 459 insertions(+), 318 deletions(-) diff --git a/history.txt b/history.txt index f4ad5fc..3f06f51 100644 --- a/history.txt +++ b/history.txt @@ -6,12 +6,18 @@ Genesis Plus GX 1.3.2 (??/??/????) (Eke-Eke) ------ * modified SN76489 cut-off frequency +* removed outdated Gens YM2612 core +* improved MAME YM2612 core accuracy: SSG-EG support, CSM Mode (based upon last Nemesis tests on real hardware) +* fixed YM2612 context restore +* modified sound update engine to fix synchronization issues (see below) * various code cleanup [Gamecube/Wii] -* improved video synchronization in original interlaced modes: fixed video issues in Sonic 2 (VS mode) +* improved sound/video synchronization in 60Hz modes * fixed some statibilty issues +* rewrote Font & GUI engines (now powered by GX hardware) +* new Interface design ! diff --git a/source/gen_input.c b/source/gen_input.c index 73c207b..409c86a 100644 --- a/source/gen_input.c +++ b/source/gen_input.c @@ -45,7 +45,7 @@ uint8 hc_256[171] = { 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF }; -/* H counter values for a 320-pixel wide display (442 pixels max.) */ +/* H counter values for a 320-pixel wide display (420 pixels max.) */ uint8 hc_320[210] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, diff --git a/source/genesis.c b/source/genesis.c index 3381718..3b116ff 100644 --- a/source/genesis.c +++ b/source/genesis.c @@ -166,7 +166,7 @@ void gen_reset (uint32 hard_reset) /* Reset CPUs */ m68k_pulse_reset (); z80_reset (); - _YM2612_Reset(); + YM2612ResetChip(); #ifdef NGC /* register SOFTRESET */ @@ -245,8 +245,8 @@ void gen_reset_w (uint32 state) } /* Reset Z80 & YM2612 */ - _YM2612_Reset(); z80_reset (); + YM2612ResetChip(); } zreset = state; diff --git a/source/ngc/config.c b/source/ngc/config.c index 3ecd128..6890598 100644 --- a/source/ngc/config.c +++ b/source/ngc/config.c @@ -23,9 +23,9 @@ #include "shared.h" #ifdef HW_RVL -#define CONFIG_VERSION "GENPLUS 1.3.1W" +#define CONFIG_VERSION "GENPLUS 1.3.2W" #else -#define CONFIG_VERSION "GENPLUS 1.3.1G" +#define CONFIG_VERSION "GENPLUS 1.3.2G" #endif void config_save() @@ -83,7 +83,6 @@ void set_config_defaults(void) config.boost = 1; config.hq_fm = 1; config.filter = 1; - config.fm_core = 0; /* system options */ config.freeze_auto = -1; diff --git a/source/ngc/config.h b/source/ngc/config.h index 56c7af7..0cbf0cf 100644 --- a/source/ngc/config.h +++ b/source/ngc/config.h @@ -36,7 +36,6 @@ typedef struct uint8 boost; uint8 filter; uint8 hq_fm; - uint8 fm_core; int8 sram_auto; int8 freeze_auto; uint8 region_detect; diff --git a/source/ngc/filemem.c b/source/ngc/filemem.c index 5bcfe4e..cb6a80b 100644 --- a/source/ngc/filemem.c +++ b/source/ngc/filemem.c @@ -44,7 +44,7 @@ static card_stat CardStatus; * Must be 32-byte aligned. * 64k SRAM + 2k Icon */ -static u8 savebuffer[0x26000] ATTRIBUTE_ALIGN (32); +static u8 savebuffer[STATE_SIZE] ATTRIBUTE_ALIGN (32); char rom_filename[MAXJOLIET]; int ManageSRAM(u8 direction, u8 device); diff --git a/source/ngc/gui/menu.c b/source/ngc/gui/menu.c index 7c928bb..a81ac6e 100644 --- a/source/ngc/gui/menu.c +++ b/source/ngc/gui/menu.c @@ -30,7 +30,7 @@ #include "Banner_bottom.h" #include "Banner_top.h" -#include "Banner_main.h" +//#include "Banner_main.h" #include "Background_main.h" #include "Main_logo.h" #include "Main_play.h" @@ -113,10 +113,10 @@ void DrawMenu (gui_butn *butn_list, int nb_butns, int selected) DrawTexture(&texture, 640-texture.width, 0, texture.width, texture.height); OpenPNGFromMemory(&texture, Main_logo); DrawTexture(&texture, 444, 28, 176, 48);*/ - OpenPNGFromMemory(&texture, Banner_main); + /*OpenPNGFromMemory(&texture, Banner_main); DrawTexture(&texture, 0, 480-texture.height, texture.width, texture.height); OpenPNGFromMemory(&texture, Main_logo); - DrawTexture(&texture, (640-texture.width)/2, 370, texture.width, texture.height); + DrawTexture(&texture, (640-texture.width)/2, 370, texture.width, texture.height);*/ /* draw selectable items */ for (i=0; i2)||(config.fm_core && (config.hq_fm>1))) config.hq_fm = 0; + if (config.hq_fm>2) config.hq_fm = 0; if (genromsize) { audio_init(48000); @@ -298,18 +297,6 @@ void soundmenu () } break; - case 5: - config.fm_core ^= 1; - config.psg_preamp = config.fm_core ? 250 : 150; - config.fm_preamp = 100; - if (genromsize) - { - if (!config.fm_core) memcpy(fm_reg,YM2612.REG,sizeof(fm_reg)); - audio_init(48000); - fm_restore(); - } - break; - case -1: quit = 1; break; diff --git a/source/shared.h b/source/shared.h index 61f0ba2..5b6170a 100644 --- a/source/shared.h +++ b/source/shared.h @@ -22,7 +22,6 @@ #include "sound.h" #include "sn76489.h" #include "fm.h" -#include "ym2612.h" #include "loadrom.h" #include "cart_hw.h" #include "eeprom.h" diff --git a/source/sound/fm.c b/source/sound/fm.c index 5cc9738..cb6ab25 100644 --- a/source/sound/fm.c +++ b/source/sound/fm.c @@ -12,17 +12,24 @@ /* ** History: ** -** 2006-2008 Eke-Eke (gamecube&wii port of Genesis Plus): -** - fixed internal FM timer emulation -** - removed unused multichip support and YMxxx support -** - fixed CH3 CSM mode (which games actually use this ?), credits to Nemesis -** - implemented Detune overflow (Ariel, Comix Zone, Shaq Fu, Spiderman & many others), credits to Nemesis -** - fixed SSG-EG support (Asterix, Bubba'n Six & many others), credits to Nemesis -** - modified EG rates, tested by Nemesis on real hardware -** - implemented LFO phase update for CH3 special mode (Warlock birds, Alladin bug sound) -** - fixed Attack Rate update (Batman & Robin intro) -** - fixed attenuation level at the start of Substain (Gynoug explosions) -** - fixed EG decay->substain transition to handle special cases, like SL=0 and Decay rate is very slow (Mega Turrican tracks 03,09...) +** 2006~2008 Eke-Eke (Genesis Plus GX): +** Credits to Nemesis (@spritesmind.net), most of those fixes came from his tests on a real Mega Drive +** +** - removed multichip support (unused) +** - added YM2612 Context external access functions +** - implemented correct LFO phase update in CH3 special mode (Warlock birds, Alladin bug sound) +** - fixed internal timers emulation +** - fixed Attack Rate update on register write (Batman & Robin intro) +** - fixed EG behavior on KEY ON when Attack Rate is maximal +** - fixed EG behavior on decay->substain transition when SL=0 (Mega Turrican tracks 03,09...) +** - fixed YM2612 initial values (after the reset) +** - implemented correct Detune overflow (Ariel, Comix Zone, Shaq Fu, Spiderman & many others) +** - implemented correct CSM mode support +** - rewrote SSG-EG emulation (Asterix, Bubba'n Six & many others) +** - modified some EG rates +** - modified address/data port behavior +** + ** ** 03-08-2003 Jarek Burczynski: ** - fixed YM2608 initial values (after the reset) @@ -492,7 +499,7 @@ typedef struct UINT8 ssg; /* SSG-EG waveform */ UINT8 ssgn; /* SSG-EG negated output */ - UINT32 key; /* 0=last key was KEY OFF, 1=KEY ON */ + UINT8 key; /* 0=last key was KEY OFF, 1=KEY ON */ /* LFO */ UINT32 AMmask; /* AM enable flag */ @@ -529,7 +536,7 @@ typedef struct UINT32 clock; /* master clock (Hz) */ UINT32 rate; /* sampling rate (Hz) */ double freqbase; /* frequency base */ - UINT8 address[2]; /* address register */ + UINT16 address; /* address register */ UINT8 status; /* status flag */ UINT32 mode; /* mode CSM / 3SLOT */ UINT8 fn_h; /* freq latch */ @@ -556,6 +563,8 @@ typedef struct UINT8 fn_h; /* freq3 latch */ UINT8 kcode[3]; /* key code */ UINT32 block_fnum[3]; /* current fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */ + UINT8 key_csm; /* 1: KEY ON event occured in CSM mode */ + } FM_3SLOT; /* OPN/A/B common state */ @@ -609,6 +618,117 @@ static INT32 LFO_PM; /* runtime LFO calculations helper */ else if ( val < min ) val = min; \ } +INLINE void FM_KEYON(FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + + if( !SLOT->key && !ym2612.OPN.SL3.key_csm) + { + /* restart Phase Generator */ + SLOT->phase = 0; + + /* reset SSG-EG inversion flag */ + SLOT->ssgn = 0; + + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->state = EG_ATT; /* phase -> Attack */ + } + else + { + /* force attenuation level to 0 */ + SLOT->volume = MIN_ATT_INDEX; + + /* directly switch to Decay (or Sustain) */ + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; + } + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) + SLOT->vol_out = ((UINT32)SLOT->volume ^ 0x1FF) + 1 + SLOT->tl; /* SSG-EG Output Inversion */ + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + SLOT->key = 1; +} + +INLINE void FM_KEYOFF(FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + + if( SLOT->key && !ym2612.OPN.SL3.key_csm) + { + if (SLOT->state>EG_REL) + { + SLOT->state = EG_REL; /* phase -> Release */ + + /* SSG-EG */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) + { + /* recalculate proper volume */ + SLOT->volume = ((UINT32)SLOT->volume ^ 0x1FF) + 1; /* Invert Attenuation */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + } + + SLOT->key = 0; +} + +INLINE void FM_KEYON_CSM(FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + + if( !SLOT->key && !ym2612.OPN.SL3.key_csm) + { + /* restart Phase Generator */ + SLOT->phase = 0; + + /* reset SSG-EG inversion flag */ + SLOT->ssgn = 0; + + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->state = EG_ATT; /* phase -> Attack */ + } + else + { + /* force attenuation level to 0 */ + SLOT->volume = MIN_ATT_INDEX; + + /* directly switch to Decay (or Sustain) */ + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; + } + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) + SLOT->vol_out = ((UINT32)SLOT->volume ^ 0x1FF) + 1 + SLOT->tl; /* SSG-EG Output Inversion */ + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } +} + +INLINE void FM_KEYOFF_CSM(FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + if( !SLOT->key ) + { + if (SLOT->state>EG_REL) + { + SLOT->state = EG_REL; /* phase -> Release */ + + /* SSG-EG */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) + { + /* recalculate proper volume */ + SLOT->volume = ((UINT32)SLOT->volume ^ 0x1FF) + 1; /* Invert Attenuation */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + } +} + /* OPN Mode Register Write */ INLINE void set_timers(int v ) { @@ -621,7 +741,21 @@ INLINE void set_timers(int v ) /* b1 = load b */ /* b0 = load a */ - if ((ym2612.OPN.ST.mode ^ v) & 0xC0) ym2612.CH[2].SLOT[SLOT1].Incr=-1; /* recalculate phase (from gens) */ + if ((ym2612.OPN.ST.mode ^ v) & 0xC0) + { + ym2612.CH[2].SLOT[SLOT1].Incr=-1; /* recalculate phase (from Gens) */ + + /* CSM mode disabled and CSM key ON active*/ + if (((v & 0xC0) != 0x80) && ym2612.OPN.SL3.key_csm) + { + /* CSM Mode Key OFF (verified by Nemesis on real hardware) */ + FM_KEYOFF_CSM(&ym2612.CH[2],SLOT1); + FM_KEYOFF_CSM(&ym2612.CH[2],SLOT2); + FM_KEYOFF_CSM(&ym2612.CH[2],SLOT3); + FM_KEYOFF_CSM(&ym2612.CH[2],SLOT4); + ym2612.OPN.SL3.key_csm = 0; + } + } /* reload Timers */ if ((v&1) & !(ym2612.OPN.ST.mode&1)) ym2612.OPN.ST.TAC = ym2612.OPN.ST.TAL; @@ -633,42 +767,6 @@ INLINE void set_timers(int v ) ym2612.OPN.ST.mode = v; } - -INLINE void FM_KEYON(FM_CH *CH , int s ) -{ - FM_SLOT *SLOT = &CH->SLOT[s]; - if( !SLOT->key ) - { - SLOT->key = 1; - SLOT->phase = 0; /* restart Phase Generator */ - SLOT->ssgn = (SLOT->ssg & 0x04) >> 1; - - if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) - { - SLOT->state = EG_ATT; /* phase -> Attack */ - } - else - { - /* directly switch to Decay */ - SLOT->volume = MIN_ATT_INDEX; - SLOT->state = EG_DEC; - } - } -} - -INLINE void FM_KEYOFF(FM_CH *CH , int s ) -{ - FM_SLOT *SLOT = &CH->SLOT[s]; - if( SLOT->key ) - { - SLOT->key = 0; - if (SLOT->state>EG_REL) - { - SLOT->state = EG_REL; /* phase -> Release */ - } - } -} - /* set algorithm connection */ INLINE void setup_connection( FM_CH *CH, int ch ) { @@ -768,6 +866,12 @@ INLINE void set_det_mul(FM_CH *CH,FM_SLOT *SLOT,int v) INLINE void set_tl(FM_CH *CH,FM_SLOT *SLOT , int v) { SLOT->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */ + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04)) && (SLOT->state > EG_REL)) + SLOT->vol_out = ((UINT32)SLOT->volume ^ 0x1FF) + 1 + SLOT->tl; /* SSG-EG Output Inversion */ + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; } /* set attack rate & key scale */ @@ -783,18 +887,21 @@ INLINE void set_ar_ksr(FM_CH *CH,FM_SLOT *SLOT,int v) CH->SLOT[SLOT1].Incr=-1; } - /* refresh Attack rate */ - if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) - { - SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; - SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; - } - else - { - SLOT->eg_sh_ar = 0; - SLOT->eg_sel_ar = 17*RATE_STEPS; - } + /* Even if it seems unnecessary, in some odd case, KSR and KC are both modified */ + /* and could result in SLOT->kc remaining unchanged. */ + /* In such case, AR values would not be recalculated despite SLOT->ar has changed */ + /* This fixes the introduction music of Batman & Robin (Eke-Eke) */ + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 18*RATE_STEPS; /* verified by Nemesis on real hardware (Attack phase is blocked) */ + } + } /* set decay rate */ INLINE void set_dr(FM_SLOT *SLOT,int v) @@ -897,142 +1004,200 @@ INLINE void advance_lfo() } } + INLINE void advance_eg_channel(FM_SLOT *SLOT) { - unsigned int swap_flag = 0; unsigned int i = 4; /* four operators per channel */ do { - /* reset SSG-EG swap flag (Eke-Eke) */ - swap_flag = 0; - switch(SLOT->state) { case EG_ATT: /* attack phase */ if (!(ym2612.OPN.eg_cnt & ((1<eg_sh_ar)-1))) { + /* update attenuation level */ SLOT->volume += (~SLOT->volume * (eg_inc[SLOT->eg_sel_ar + ((ym2612.OPN.eg_cnt>>SLOT->eg_sh_ar)&7)]))>>4; + + /* check phase transition*/ if (SLOT->volume <= MIN_ATT_INDEX) { SLOT->volume = MIN_ATT_INDEX; - SLOT->state = EG_DEC; + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; /* special case where SL=0 */ } + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) /* SSG-EG Output Inversion */ + SLOT->vol_out = ((UINT32)SLOT->volume ^ 0x1FF) + 1 + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; } break; case EG_DEC: /* decay phase */ - if ( !(ym2612.OPN.eg_cnt & ((1<eg_sh_d1r)-1) ) ) + if (!(ym2612.OPN.eg_cnt & ((1<eg_sh_d1r)-1))) { - if (SLOT->ssg&0x08) /* SSG EG type envelope selected */ - SLOT->volume += 6 * eg_inc[SLOT->eg_sel_d1r + ((ym2612.OPN.eg_cnt>>SLOT->eg_sh_d1r)&7)]; + /* SSG EG type */ + if (SLOT->ssg&0x08) + { + /* update attenuation level */ + if (SLOT->volume < 0x200) + SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d1r + ((ym2612.OPN.eg_cnt>>SLOT->eg_sh_d1r)&7)]; + + /* recalculate EG output */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) /* SSG-EG Output Inversion */ + SLOT->vol_out = ((UINT32)SLOT->volume ^ 0x1FF) + 1 + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } else + { + /* update attenuation level */ SLOT->volume += eg_inc[SLOT->eg_sel_d1r + ((ym2612.OPN.eg_cnt>>SLOT->eg_sh_d1r)&7)]; - } - /* check transition even if no volume update: this fixes the case when SL = MIN_ATT_INDEX */ - if ( SLOT->volume >= (INT32)(SLOT->sl) ) - { - SLOT->volume = (INT32)(SLOT->sl); - SLOT->state = EG_SUS; - } + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + /* check phase transition*/ + if (SLOT->volume >= (INT32)(SLOT->sl)) + SLOT->state = EG_SUS; + } break; case EG_SUS: /* sustain phase */ - if (SLOT->ssg&0x08) /* SSG EG type envelope selected */ + if (!(ym2612.OPN.eg_cnt & ((1<eg_sh_d2r)-1))) { - if ( !(ym2612.OPN.eg_cnt & ((1<eg_sh_d2r)-1) ) ) + /* SSG EG type */ + if (SLOT->ssg&0x08) { - /* Nemesis */ - SLOT->volume += 6 * eg_inc[SLOT->eg_sel_d2r + ((ym2612.OPN.eg_cnt>>SLOT->eg_sh_d2r)&7)]; + /* update attenuation level */ + if (SLOT->volume < 0x200) + SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d2r + ((ym2612.OPN.eg_cnt>>SLOT->eg_sh_d2r)&7)]; + + /* recalculate EG output */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) /* SSG-EG Output Inversion */ + SLOT->vol_out = ((UINT32)SLOT->volume ^ 0x1FF) + 1 + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; } - - if ( SLOT->volume >= ENV_QUIET) /* Alone Coder */ - { - if (SLOT->ssg&0x01) /* bit 0 = hold */ - { - if (SLOT->ssgn&1) /* have we swapped once ??? */ - { - /* yes, so do nothing, just hold current level */ - } - else - swap_flag = (SLOT->ssg&0x02) | 1 ; /* bit 1 = alternate */ - } - else - { - /* same as KEY-ON operation */ - - /* restart of the Phase Generator should be here */ - SLOT->phase = 0; - - if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) - { - SLOT->state = EG_ATT; /* phase -> Attack */ - } - else - { - /* Attack Rate is maximal: directly switch to Decay (or Substain) */ - SLOT->volume = MIN_ATT_INDEX; - SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; - } - - swap_flag = (SLOT->ssg&0x02); /* bit 1 = alternate */ - - } - } - } - else - { - if ( !(ym2612.OPN.eg_cnt & ((1<eg_sh_d2r)-1) ) ) + else { + /* update attenuation level */ SLOT->volume += eg_inc[SLOT->eg_sel_d2r + ((ym2612.OPN.eg_cnt>>SLOT->eg_sh_d2r)&7)]; + /* check phase transition*/ if ( SLOT->volume >= MAX_ATT_INDEX ) SLOT->volume = MAX_ATT_INDEX; /* do not change SLOT->state (verified on real chip) */ + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; } } break; case EG_REL: /* release phase */ - if ( !(ym2612.OPN.eg_cnt & ((1<eg_sh_rr)-1) ) ) + if (!(ym2612.OPN.eg_cnt & ((1<eg_sh_rr)-1))) { + /* SSG EG type */ if (SLOT->ssg&0x08) - /* SSG-EG affects Release phase also (Nemesis) */ - SLOT->volume += 6 * eg_inc[SLOT->eg_sel_rr + ((ym2612.OPN.eg_cnt>>SLOT->eg_sh_rr)&7)]; + { + /* update attenuation level */ + if (SLOT->volume < 0x200) + SLOT->volume += 4 * eg_inc[SLOT->eg_sel_rr + ((ym2612.OPN.eg_cnt>>SLOT->eg_sh_rr)&7)]; + } else + { + /* update attenuation level */ SLOT->volume += eg_inc[SLOT->eg_sel_rr + ((ym2612.OPN.eg_cnt>>SLOT->eg_sh_rr)&7)]; - if ( SLOT->volume >= MAX_ATT_INDEX ) - { - SLOT->volume = MAX_ATT_INDEX; - SLOT->state = EG_OFF; + /* check phase transition*/ + if (SLOT->volume >= MAX_ATT_INDEX) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } } + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } break; } - unsigned int out = (UINT32)SLOT->volume; - - /* negate output (changes come from alternate bit, init comes from attack bit) */ - if ((SLOT->ssg&0x08) && (SLOT->ssgn&2) && (SLOT->state > EG_REL)) - out ^= MAX_ATT_INDEX; - - /* we need to store the result here because we are going to change ssgn - in next instruction */ - SLOT->vol_out = out + SLOT->tl; - - /* reverse SLOT inversion flag if required */ - SLOT->ssgn ^= swap_flag; - SLOT++; i--; } while (i); - } +INLINE void update_ssg_eg_channel(FM_SLOT *SLOT) +{ + unsigned int i = 4; /* four operators per channel */ + int update_out = 0; + + do + { + /* check SSG-EG state */ + if ((SLOT->ssg & 0x08) && (SLOT->volume >= 0x200)) + { + /* swap Invert Output flag if required */ + /* if HOLD bit is set, verify we didn't already swapped once */ + if ((SLOT->ssg & 0x02) && (!(SLOT->ssg & 0x01) || !(SLOT->ssgn&4))) + { + SLOT->ssgn ^= 4; + update_out = 1; + } + + /* output is not alternated and not hold */ + if (!(SLOT->ssg & 0x02) && !(SLOT->ssg & 0x01)) + SLOT->phase = 0; /* reset Phase Generator */ + + /* update EG phase */ + if (SLOT->state < EG_ATT) + { + if ((SLOT->state != EG_REL) && (!SLOT->ssg&0x01)) + { + /* same as Key ON */ + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->state = EG_ATT; /* phase -> Attack */ + } + else + { + /* Attack Rate is maximal: directly switch to Decay */ + SLOT->volume = MIN_ATT_INDEX; + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; /* special case where SL=0 */ + update_out = 1; + } + } + else if (SLOT->state == EG_REL || (!(SLOT->ssgn ^ (SLOT->ssg & 0x04)))) + { + SLOT->volume = MAX_ATT_INDEX; + update_out = 1; + } + } + + /* recalculate EG output */ + if (update_out) + { + update_out = 0; + + if ((SLOT->state > EG_REL) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) + SLOT->vol_out = ((UINT32)SLOT->volume ^ 0x1FF) + 1 + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + + /* next slot */ + SLOT++; + i--; + } while (i); +} + #define volume_calc(OP) ((OP)->vol_out + (AM & (OP)->AMmask)) @@ -1211,7 +1376,7 @@ INLINE void refresh_fc_eg_slot(FM_SLOT *SLOT , int fc , int kc ) else { SLOT->eg_sh_ar = 0; - SLOT->eg_sel_ar = 17*RATE_STEPS; + SLOT->eg_sel_ar = 18*RATE_STEPS; /* verified by Nemesis on real hardware (Attack phase is blocked) */ } SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr]; @@ -1371,30 +1536,12 @@ static int init_tables(void) /* CSM Key Controll */ INLINE void CSMKeyControll(FM_CH *CH) { - /* all key ON/OFF (only for operator which have been OFF)*/ - if (!CH->SLOT[SLOT1].key) - { - FM_KEYON(CH,SLOT1); - FM_KEYOFF(CH,SLOT1); - } - - if (!CH->SLOT[SLOT2].key) - { - FM_KEYON(CH,SLOT2); - FM_KEYOFF(CH,SLOT2); - } - - if (!CH->SLOT[SLOT3].key) - { - FM_KEYON(CH,SLOT3); - FM_KEYOFF(CH,SLOT3); - } - - if (!CH->SLOT[SLOT4].key) - { - FM_KEYON(CH,SLOT4); - FM_KEYOFF(CH,SLOT4); - } + /* all key ON (verified by Nemesis on real hardware) */ + FM_KEYON_CSM(CH,SLOT1); + FM_KEYON_CSM(CH,SLOT2); + FM_KEYON_CSM(CH,SLOT3); + FM_KEYON_CSM(CH,SLOT4); + ym2612.OPN.SL3.key_csm = 1; } INLINE void INTERNAL_TIMER_A() @@ -1410,8 +1557,8 @@ INLINE void INTERNAL_TIMER_A() if (ym2612.OPN.ST.TAL) ym2612.OPN.ST.TAC += ym2612.OPN.ST.TAL; else ym2612.OPN.ST.TAC = ym2612.OPN.ST.TAL; - /* CSM mode total level latch and auto key on */ - if ((ym2612.OPN.ST.mode & 0xC0) == 0x80) CSMKeyControll (&(ym2612.CH[2])); + /* CSM mode auto key on */ + if ((ym2612.OPN.ST.mode & 0xC0) == 0x80) CSMKeyControll(&ym2612.CH[2]); } } } @@ -1567,8 +1714,13 @@ static void OPNWriteReg(int r, int v) break; case 0x90: /* SSG-EG */ - SLOT->ssg = v&0x0f; - SLOT->ssgn = (v&0x04)>>1; /* bit 1 in ssgn = attack */ + SLOT->ssg = v&0x0f; + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04)) && (SLOT->state > EG_REL)) + SLOT->vol_out = ((UINT32)SLOT->volume ^ 0x1FF) + 1 + SLOT->tl; /* SSG-EG Output Inversion */ + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; /* SSG-EG envelope shapes : @@ -1758,18 +1910,13 @@ void YM2612UpdateOne(int **buffer, int length) out_fm[4] = 0; out_fm[5] = 0; - /* calculate FM */ - chan_calc(&ym2612.CH[0]); - chan_calc(&ym2612.CH[1]); - chan_calc(&ym2612.CH[2]); - chan_calc(&ym2612.CH[3]); - chan_calc(&ym2612.CH[4]); - if (ym2612.dacen) - { - /* DAC Mode */ - *(ym2612.CH[5].connect4) += ym2612.dacout; - } - else chan_calc(&ym2612.CH[5]); + /* update SSG-EG enveloppe type */ + update_ssg_eg_channel(&ym2612.CH[0].SLOT[SLOT1]); + update_ssg_eg_channel(&ym2612.CH[1].SLOT[SLOT1]); + update_ssg_eg_channel(&ym2612.CH[2].SLOT[SLOT1]); + update_ssg_eg_channel(&ym2612.CH[3].SLOT[SLOT1]); + update_ssg_eg_channel(&ym2612.CH[4].SLOT[SLOT1]); + update_ssg_eg_channel(&ym2612.CH[5].SLOT[SLOT1]); /* advance envelope generator */ ym2612.OPN.eg_timer += ym2612.OPN.eg_timer_add; @@ -1786,6 +1933,19 @@ void YM2612UpdateOne(int **buffer, int length) advance_eg_channel(&ym2612.CH[5].SLOT[SLOT1]); } + /* calculate FM */ + chan_calc(&ym2612.CH[0]); + chan_calc(&ym2612.CH[1]); + chan_calc(&ym2612.CH[2]); + chan_calc(&ym2612.CH[3]); + chan_calc(&ym2612.CH[4]); + if (ym2612.dacen) + { + /* DAC Mode */ + *(ym2612.CH[5].connect4) += ym2612.dacout; + } + else chan_calc(&ym2612.CH[5]); + lt = ((out_fm[0]>>0) & ym2612.OPN.pan[0]); rt = ((out_fm[0]>>0) & ym2612.OPN.pan[1]); lt += ((out_fm[1]>>0) & ym2612.OPN.pan[2]); @@ -1815,8 +1975,23 @@ void YM2612UpdateOne(int **buffer, int length) *bufR++ = rt; } + /* CSM mode: if CSM Key ON has occured, CSM Key OFF need to be sent */ + /* only if Timer A does not overflow again (i.e CSM Key ON not set again) */ + ym2612.OPN.SL3.key_csm <<= 1; + /* timer A control */ INTERNAL_TIMER_A(); + + /* CSM Mode Key ON still disabled */ + if (ym2612.OPN.SL3.key_csm & 2) + { + /* CSM Mode Key OFF (verified by Nemesis on real hardware) */ + FM_KEYOFF_CSM(&ym2612.CH[2],SLOT1); + FM_KEYOFF_CSM(&ym2612.CH[2],SLOT2); + FM_KEYOFF_CSM(&ym2612.CH[2],SLOT3); + FM_KEYOFF_CSM(&ym2612.CH[2],SLOT4); + ym2612.OPN.SL3.key_csm = 0; + } } INTERNAL_TIMER_B(length); @@ -1845,27 +2020,26 @@ int YM2612ResetChip(void) ym2612.OPN.eg_timer = 0; ym2612.OPN.eg_cnt = 0; ym2612.OPN.ST.status = 0; - ym2612.OPN.ST.mode = 0; - ym2612.OPN.ST.TA = 0; - ym2612.OPN.ST.TAL = 0; - ym2612.OPN.ST.TAC = 0; - ym2612.OPN.ST.TB = 0; - ym2612.OPN.ST.TBL = 0; - ym2612.OPN.ST.TBC = 0; - + ym2612.OPN.ST.mode = 0; + ym2612.OPN.ST.TAC = 0; + ym2612.OPN.ST.TBC = 0; + OPNWriteMode(0x26,0x00); + OPNWriteMode(0x25,0x00); + OPNWriteMode(0x24,0x00); reset_channels(&ym2612.CH[0] , 6 ); for(i = 0xb6 ; i >= 0xb4 ; i-- ) { - OPNWriteReg(i ,0xc0); - OPNWriteReg(i|0x100,0xc0); + /*OPNWriteReg(i ,0xc0); + OPNWriteReg(i|0x100,0xc0);*/ + OPNWriteReg(i ,0); + OPNWriteReg(i|0x100,0); } for(i = 0xb2 ; i >= 0x30 ; i-- ) { OPNWriteReg(i ,0); OPNWriteReg(i|0x100,0); } - for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteMode(i,0); /* fixed (Eke-Eke) */ /* DAC mode clear */ ym2612.dacen = 0; @@ -1878,22 +2052,24 @@ int YM2612ResetChip(void) /* n = number */ /* a = address */ /* v = value */ -int YM2612Write(unsigned char a, unsigned char v) +void YM2612Write(unsigned int a, unsigned int v) { - int addr; - v &= 0xff; /* adjust to 8 bit bus */ switch( a&3 ) { case 0: /* address port 0 */ - ym2612.OPN.ST.address[0] = v; + ym2612.OPN.ST.address = v; break; - case 1: /* data port 0 */ - addr = ym2612.OPN.ST.address[0]; - fm_reg[0][addr] = v; - switch( addr & 0xf0 ) + case 2: /* address port 1 */ + ym2612.OPN.ST.address = v | 0x100; + break; + + default: /* data port */ + { + int addr = ym2612.OPN.ST.address; + switch( addr & 0x1f0 ) { case 0x20: /* 0x20-0x2f Mode */ switch( addr ) @@ -1915,22 +2091,21 @@ int YM2612Write(unsigned char a, unsigned char v) OPNWriteReg(addr,v); } break; - - case 2: /* address port 1 */ - ym2612.OPN.ST.address[1] = v; - break; - - case 3: /* data port 1 */ - addr = ym2612.OPN.ST.address[1]; - fm_reg[1][addr] = v; - OPNWriteReg(addr | 0x100,v); - break; + } } - - return 0; } -int YM2612Read(void) +unsigned int YM2612Read(void) { - return ym2612.OPN.ST.status; + return ym2612.OPN.ST.status & 0xff; +} + +unsigned char *YM2612GetContextPtr(void) +{ + return (unsigned char *)&ym2612; +} + +unsigned int YM2612GetContextSize(void) +{ + return sizeof(YM2612); } diff --git a/source/sound/fm.h b/source/sound/fm.h index 9cfddd0..911e1cb 100644 --- a/source/sound/fm.h +++ b/source/sound/fm.h @@ -22,8 +22,9 @@ extern int YM2612Init(int baseclock, int rate); extern int YM2612ResetChip(void); extern void YM2612UpdateOne(int **buffer, int length); -extern int YM2612Write(unsigned char a, unsigned char v); -extern int YM2612Read(void); - +extern void YM2612Write(unsigned int a, unsigned int v); +extern unsigned int YM2612Read(void); +extern unsigned char *YM2612GetContextPtr(void); +extern unsigned int YM2612GetContextSize(void); #endif /* _H_FM_FM_ */ diff --git a/source/sound/sound.c b/source/sound/sound.c index 4d3537e..e90040a 100644 --- a/source/sound/sound.c +++ b/source/sound/sound.c @@ -23,18 +23,9 @@ #include "shared.h" -/* generic functions */ -int (*_YM2612_Write)(unsigned char adr, unsigned char data); -int (*_YM2612_Read)(void); -void (*_YM2612_Update)(int **buf, int length); -int (*_YM2612_Reset)(void); - /* cycle-accurate samples */ static int m68cycles_per_sample[2]; -/* YM2612 register arrays */ -int fm_reg[2][0x100]; - /* return the number of samples that should have been rendered so far */ static inline uint32 fm_sample_cnt(uint8 is_z80) { @@ -56,7 +47,7 @@ static inline void fm_update() int *tempBuffer[2]; tempBuffer[0] = snd.fm.buffer[0] + snd.fm.lastStage; tempBuffer[1] = snd.fm.buffer[1] + snd.fm.lastStage; - _YM2612_Update(tempBuffer, snd.fm.curStage - snd.fm.lastStage); + YM2612UpdateOne(tempBuffer, snd.fm.curStage - snd.fm.lastStage); snd.fm.lastStage = snd.fm.curStage; } } @@ -82,7 +73,7 @@ void sound_init(int rate) m68cycles_per_sample[1] = m68cycles_per_sample[0]; /* YM2612 is emulated at original frequency (VLCK/144) */ - if (config.hq_fm && !config.fm_core) + if (config.hq_fm) { m68cycles_per_sample[0] = 144; } @@ -91,22 +82,7 @@ void sound_init(int rate) SN76489_Init(0, (int)zclk, rate); SN76489_Config(0, MUTE_ALLON, VOL_FULL, FB_SEGAVDP, SRW_SEGAVDP, 0); - if (config.fm_core) - { - _YM2612_Write = YM2612_Write; - _YM2612_Read = YM2612_Read; - _YM2612_Update = YM2612_Update; - _YM2612_Reset = YM2612_Reset; - YM2612_Init((int)vclk, rate, config.hq_fm); - } - else - { - _YM2612_Write = YM2612Write; - _YM2612_Read = YM2612Read; - _YM2612_Update = YM2612UpdateOne; - _YM2612_Reset = YM2612ResetChip; - YM2612Init ((int)vclk, rate); - } + YM2612Init ((int)vclk, rate); } void sound_update(int fm_len, int psg_len) @@ -127,21 +103,16 @@ void sound_update(int fm_len, int psg_len) } /* YM2612 control */ -/* restore FM registers */ + +/* restore FM context */ void fm_restore(void) { - int i; - - _YM2612_Reset(); - - /* feed all the registers and update internal state */ - for(i = 0; i < 0x100; i++) - { - _YM2612_Write(0, i); - _YM2612_Write(1, fm_reg[0][i]); - _YM2612_Write(2, i); - _YM2612_Write(3, fm_reg[1][i]); - } + unsigned char *temp = malloc(YM2612GetContextSize()); + if (!temp) return; + memcpy(temp, YM2612GetContextPtr(), YM2612GetContextSize()); + YM2612ResetChip(); + memcpy(YM2612GetContextPtr(), temp, YM2612GetContextSize()); + free(temp); } /* write FM chip */ @@ -152,7 +123,7 @@ void fm_write(unsigned int cpu, unsigned int address, unsigned int data) snd.fm.curStage = fm_sample_cnt(cpu); fm_update(); } - _YM2612_Write(address & 3, data); + YM2612Write(address, data); } /* read FM status */ @@ -160,7 +131,7 @@ unsigned int fm_read(unsigned int cpu, unsigned int address) { snd.fm.curStage = fm_sample_cnt(cpu); fm_update(); - return (_YM2612_Read() & 0xff); + return YM2612Read(); } diff --git a/source/sound/sound.h b/source/sound/sound.h index 5131def..dbaa654 100644 --- a/source/sound/sound.h +++ b/source/sound/sound.h @@ -24,9 +24,6 @@ #ifndef _SOUND_H_ #define _SOUND_H_ -/* Global variables */ -extern int fm_reg[2][0x100]; - /* Function prototypes */ extern void sound_init(int rate); extern void sound_update(int fm_len, int psg_len); @@ -34,6 +31,5 @@ extern void fm_restore(void); extern void fm_write(unsigned int cpu, unsigned int address, unsigned int data); extern unsigned int fm_read(unsigned int cpu, unsigned int address); extern void psg_write(unsigned int cpu, unsigned int data); -extern int (*_YM2612_Reset)(void); #endif /* _SOUND_H_ */ diff --git a/source/state.c b/source/state.c index fc58890..4cc00c0 100644 --- a/source/state.c +++ b/source/state.c @@ -22,30 +22,27 @@ #include "shared.h" -static unsigned char state[0x24000]; -static unsigned int bufferptr; - -static inline void load_param(void *param, unsigned int size) -{ - memcpy(param, &state[bufferptr], size); +#define load_param(param, size) \ + memcpy(param, &state[bufferptr], size); \ bufferptr+= size; -} -static inline void save_param(void *param, unsigned int size) -{ - memcpy(&state[bufferptr], param, size); +#define save_param(param, size) \ + memcpy(&state[bufferptr], param, size); \ bufferptr+= size; -} void state_load(unsigned char *buffer) { - /* reset buffer pointer */ - bufferptr = 0; + /* allocate memory */ + unsigned char *state = malloc(STATE_SIZE); + if (state == NULL) return; + + /* buffer size */ + int bufferptr = 0; /* uncompress savestate */ unsigned long inbytes, outbytes; memcpy(&inbytes, buffer, 4); - outbytes = 0x24000; + outbytes = STATE_SIZE; uncompress ((Bytef *)state, &outbytes, (Bytef *)(buffer + 4), inbytes); /* reset system */ @@ -82,8 +79,7 @@ void state_load(unsigned char *buffer) vdp_restore(temp_reg); // FM - load_param(fm_reg,sizeof(fm_reg)); - fm_restore(); + load_param(YM2612GetContextPtr(),YM2612GetContextSize()); // PSG load_param(SN76489_GetContextPtr (0),SN76489_GetContextSize ()); @@ -113,12 +109,19 @@ void state_load(unsigned char *buffer) // Z80 load_param(&Z80, sizeof(Z80_Regs)); + + /* Free memory */ + free(state); } int state_save(unsigned char *buffer) { - /* reset buffer pointer */ - bufferptr = 0; + /* allocate memory */ + unsigned char *state = malloc(STATE_SIZE); + if (state == NULL) return 0; + + /* buffer size */ + int bufferptr = 0; // GENESIS save_param(work_ram, sizeof(work_ram)); @@ -147,7 +150,7 @@ int state_save(unsigned char *buffer) save_param(&irq_status, sizeof(irq_status)); // FM - save_param(fm_reg,sizeof(fm_reg)); + save_param(YM2612GetContextPtr(),YM2612GetContextSize()); // PSG save_param(SN76489_GetContextPtr (0),SN76489_GetContextSize ()); @@ -180,10 +183,13 @@ int state_save(unsigned char *buffer) /* compress state file */ unsigned long inbytes = bufferptr; - unsigned long outbytes = 0x26000; + unsigned long outbytes = STATE_SIZE; compress2 ((Bytef *)(buffer + 4), &outbytes, (Bytef *)state, inbytes, 9); memcpy(buffer, &outbytes, 4); + /* Free memory */ + free(state); + /* return total size */ return (outbytes + 4); } diff --git a/source/state.h b/source/state.h index 841aeb0..35f86b6 100644 --- a/source/state.h +++ b/source/state.h @@ -23,6 +23,8 @@ #ifndef _STATE_H_ #define _STATE_H_ +#define STATE_SIZE 0x28000 + /* Function prototypes */ extern void state_load(unsigned char *buffer); extern int state_save(unsigned char *buffer); diff --git a/source/system.c b/source/system.c index de190f4..a7dbca9 100644 --- a/source/system.c +++ b/source/system.c @@ -169,7 +169,7 @@ int audio_init (int rate) #endif /* SRC */ - if (config.hq_fm && !config.fm_core) + if (config.hq_fm) { /* SRC ratio (YM2612 original samplerate is VCLK/144) */ src_data.src_ratio = ((double)rate * 144.0) / ((double) vdp_rate * (double)m68cycles_per_line * (double)lines_per_frame); diff --git a/source/vdp.c b/source/vdp.c index abe3a29..f15a505 100644 --- a/source/vdp.c +++ b/source/vdp.c @@ -497,7 +497,7 @@ static inline void data_write (unsigned int data) switch (code & 0x0F) { case 0x01: /* VRAM */ - + /* Byte-swap data if A0 is set */ if (addr & 1) data = (data >> 8) | (data << 8); @@ -833,11 +833,11 @@ void vdp_data_w(unsigned int data) fifo_update(); if (fifo_write_cnt == 0) { - /* reset cycle counter */ - fifo_lastwrite = count_m68k; + /* reset cycle counter */ + fifo_lastwrite = count_m68k; - /* FIFO is not empty anymore */ - status &= 0xFDFF; + /* FIFO is not empty anymore */ + status &= 0xFDFF; } /* increase write counter */