[-]removed Gens YM2612 core support

[~]fixed YM2612 context restore 
[+]improved YM2612 emulation accuracy (BROKEN)
This commit is contained in:
ekeeke31 2009-02-25 16:53:28 +00:00
parent 6f65ac3f15
commit 4eb8d17acf
16 changed files with 459 additions and 318 deletions

View File

@ -6,12 +6,18 @@ Genesis Plus GX 1.3.2 (??/??/????) (Eke-Eke)
------ ------
* modified SN76489 cut-off frequency * 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 * various code cleanup
[Gamecube/Wii] [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 * fixed some statibilty issues
* rewrote Font & GUI engines (now powered by GX hardware)
* new Interface design !

View File

@ -45,7 +45,7 @@ uint8 hc_256[171] = {
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF 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] = { uint8 hc_320[210] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 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, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,

View File

@ -166,7 +166,7 @@ void gen_reset (uint32 hard_reset)
/* Reset CPUs */ /* Reset CPUs */
m68k_pulse_reset (); m68k_pulse_reset ();
z80_reset (); z80_reset ();
_YM2612_Reset(); YM2612ResetChip();
#ifdef NGC #ifdef NGC
/* register SOFTRESET */ /* register SOFTRESET */
@ -245,8 +245,8 @@ void gen_reset_w (uint32 state)
} }
/* Reset Z80 & YM2612 */ /* Reset Z80 & YM2612 */
_YM2612_Reset();
z80_reset (); z80_reset ();
YM2612ResetChip();
} }
zreset = state; zreset = state;

View File

@ -23,9 +23,9 @@
#include "shared.h" #include "shared.h"
#ifdef HW_RVL #ifdef HW_RVL
#define CONFIG_VERSION "GENPLUS 1.3.1W" #define CONFIG_VERSION "GENPLUS 1.3.2W"
#else #else
#define CONFIG_VERSION "GENPLUS 1.3.1G" #define CONFIG_VERSION "GENPLUS 1.3.2G"
#endif #endif
void config_save() void config_save()
@ -83,7 +83,6 @@ void set_config_defaults(void)
config.boost = 1; config.boost = 1;
config.hq_fm = 1; config.hq_fm = 1;
config.filter = 1; config.filter = 1;
config.fm_core = 0;
/* system options */ /* system options */
config.freeze_auto = -1; config.freeze_auto = -1;

View File

@ -36,7 +36,6 @@ typedef struct
uint8 boost; uint8 boost;
uint8 filter; uint8 filter;
uint8 hq_fm; uint8 hq_fm;
uint8 fm_core;
int8 sram_auto; int8 sram_auto;
int8 freeze_auto; int8 freeze_auto;
uint8 region_detect; uint8 region_detect;

View File

@ -44,7 +44,7 @@ static card_stat CardStatus;
* Must be 32-byte aligned. * Must be 32-byte aligned.
* 64k SRAM + 2k Icon * 64k SRAM + 2k Icon
*/ */
static u8 savebuffer[0x26000] ATTRIBUTE_ALIGN (32); static u8 savebuffer[STATE_SIZE] ATTRIBUTE_ALIGN (32);
char rom_filename[MAXJOLIET]; char rom_filename[MAXJOLIET];
int ManageSRAM(u8 direction, u8 device); int ManageSRAM(u8 direction, u8 device);

View File

@ -30,7 +30,7 @@
#include "Banner_bottom.h" #include "Banner_bottom.h"
#include "Banner_top.h" #include "Banner_top.h"
#include "Banner_main.h" //#include "Banner_main.h"
#include "Background_main.h" #include "Background_main.h"
#include "Main_logo.h" #include "Main_logo.h"
#include "Main_play.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); DrawTexture(&texture, 640-texture.width, 0, texture.width, texture.height);
OpenPNGFromMemory(&texture, Main_logo); OpenPNGFromMemory(&texture, Main_logo);
DrawTexture(&texture, 444, 28, 176, 48);*/ DrawTexture(&texture, 444, 28, 176, 48);*/
OpenPNGFromMemory(&texture, Banner_main); /*OpenPNGFromMemory(&texture, Banner_main);
DrawTexture(&texture, 0, 480-texture.height, texture.width, texture.height); DrawTexture(&texture, 0, 480-texture.height, texture.width, texture.height);
OpenPNGFromMemory(&texture, Main_logo); 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 */ /* draw selectable items */
for (i=0; i<nb_butns; i++) for (i=0; i<nb_butns; i++)
@ -243,8 +243,8 @@ void soundmenu ()
int ret; int ret;
int quit = 0; int quit = 0;
int prevmenu = menu; int prevmenu = menu;
int count = 6; int count = 5;
char items[6][25]; char items[5][25];
strcpy (menutitle, "Press B to return"); strcpy (menutitle, "Press B to return");
@ -256,9 +256,8 @@ void soundmenu ()
sprintf (items[2], "Volume Boost: %dX", config.boost); sprintf (items[2], "Volume Boost: %dX", config.boost);
sprintf (items[3], "LowPass Filter: %s", config.filter ? " ON":"OFF"); sprintf (items[3], "LowPass Filter: %s", config.filter ? " ON":"OFF");
if (config.hq_fm == 0) sprintf (items[4], "HQ YM2612: OFF"); if (config.hq_fm == 0) sprintf (items[4], "HQ YM2612: OFF");
else if ((config.hq_fm == 1) || config.fm_core) sprintf (items[4], "HQ YM2612: LINEAR"); else if (config.hq_fm == 1) sprintf (items[4], "HQ YM2612: LINEAR");
else sprintf (items[4], "HQ YM2612: SINC"); else sprintf (items[4], "HQ YM2612: SINC");
sprintf (items[5], "FM core: %s", config.fm_core ? "GENS" : "MAME");
ret = domenu (&items[0], count, 1); ret = domenu (&items[0], count, 1);
switch (ret) switch (ret)
@ -290,7 +289,7 @@ void soundmenu ()
case 4: case 4:
config.hq_fm ++; config.hq_fm ++;
if ((config.hq_fm>2)||(config.fm_core && (config.hq_fm>1))) config.hq_fm = 0; if (config.hq_fm>2) config.hq_fm = 0;
if (genromsize) if (genromsize)
{ {
audio_init(48000); audio_init(48000);
@ -298,18 +297,6 @@ void soundmenu ()
} }
break; 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: case -1:
quit = 1; quit = 1;
break; break;

View File

@ -22,7 +22,6 @@
#include "sound.h" #include "sound.h"
#include "sn76489.h" #include "sn76489.h"
#include "fm.h" #include "fm.h"
#include "ym2612.h"
#include "loadrom.h" #include "loadrom.h"
#include "cart_hw.h" #include "cart_hw.h"
#include "eeprom.h" #include "eeprom.h"

View File

@ -12,17 +12,24 @@
/* /*
** History: ** History:
** **
** 2006-2008 Eke-Eke (gamecube&wii port of Genesis Plus): ** 2006~2008 Eke-Eke (Genesis Plus GX):
** - fixed internal FM timer emulation ** Credits to Nemesis (@spritesmind.net), most of those fixes came from his tests on a real Mega Drive
** - removed unused multichip support and YMxxx support **
** - fixed CH3 CSM mode (which games actually use this ?), credits to Nemesis ** - removed multichip support (unused)
** - implemented Detune overflow (Ariel, Comix Zone, Shaq Fu, Spiderman & many others), credits to Nemesis ** - added YM2612 Context external access functions
** - fixed SSG-EG support (Asterix, Bubba'n Six & many others), credits to Nemesis ** - implemented correct LFO phase update in CH3 special mode (Warlock birds, Alladin bug sound)
** - modified EG rates, tested by Nemesis on real hardware ** - fixed internal timers emulation
** - implemented LFO phase update for CH3 special mode (Warlock birds, Alladin bug sound) ** - fixed Attack Rate update on register write (Batman & Robin intro)
** - fixed Attack Rate update (Batman & Robin intro) ** - fixed EG behavior on KEY ON when Attack Rate is maximal
** - fixed attenuation level at the start of Substain (Gynoug explosions) ** - fixed EG behavior on decay->substain transition when SL=0 (Mega Turrican tracks 03,09...)
** - fixed EG decay->substain transition to handle special cases, like SL=0 and Decay rate is very slow (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: ** 03-08-2003 Jarek Burczynski:
** - fixed YM2608 initial values (after the reset) ** - fixed YM2608 initial values (after the reset)
@ -492,7 +499,7 @@ typedef struct
UINT8 ssg; /* SSG-EG waveform */ UINT8 ssg; /* SSG-EG waveform */
UINT8 ssgn; /* SSG-EG negated output */ 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 */ /* LFO */
UINT32 AMmask; /* AM enable flag */ UINT32 AMmask; /* AM enable flag */
@ -529,7 +536,7 @@ typedef struct
UINT32 clock; /* master clock (Hz) */ UINT32 clock; /* master clock (Hz) */
UINT32 rate; /* sampling rate (Hz) */ UINT32 rate; /* sampling rate (Hz) */
double freqbase; /* frequency base */ double freqbase; /* frequency base */
UINT8 address[2]; /* address register */ UINT16 address; /* address register */
UINT8 status; /* status flag */ UINT8 status; /* status flag */
UINT32 mode; /* mode CSM / 3SLOT */ UINT32 mode; /* mode CSM / 3SLOT */
UINT8 fn_h; /* freq latch */ UINT8 fn_h; /* freq latch */
@ -556,6 +563,8 @@ typedef struct
UINT8 fn_h; /* freq3 latch */ UINT8 fn_h; /* freq3 latch */
UINT8 kcode[3]; /* key code */ 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) */ 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; } FM_3SLOT;
/* OPN/A/B common state */ /* OPN/A/B common state */
@ -609,6 +618,117 @@ static INT32 LFO_PM; /* runtime LFO calculations helper */
else if ( val < min ) val = min; \ 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 */ /* OPN Mode Register Write */
INLINE void set_timers(int v ) INLINE void set_timers(int v )
{ {
@ -621,7 +741,21 @@ INLINE void set_timers(int v )
/* b1 = load b */ /* b1 = load b */
/* b0 = load a */ /* 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 */ /* reload Timers */
if ((v&1) & !(ym2612.OPN.ST.mode&1)) ym2612.OPN.ST.TAC = ym2612.OPN.ST.TAL; 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; 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 */ /* set algorithm connection */
INLINE void setup_connection( FM_CH *CH, int ch ) 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) INLINE void set_tl(FM_CH *CH,FM_SLOT *SLOT , int v)
{ {
SLOT->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */ 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 */ /* 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; CH->SLOT[SLOT1].Incr=-1;
} }
/* refresh Attack rate */ /* Even if it seems unnecessary, in some odd case, KSR and KC are both modified */
if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) /* and could result in SLOT->kc remaining unchanged. */
{ /* In such case, AR values would not be recalculated despite SLOT->ar has changed */
SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; /* This fixes the introduction music of Batman & Robin (Eke-Eke) */
SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/)
} {
else SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
{ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
SLOT->eg_sh_ar = 0;
SLOT->eg_sel_ar = 17*RATE_STEPS;
}
} }
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 */ /* set decay rate */
INLINE void set_dr(FM_SLOT *SLOT,int v) 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) INLINE void advance_eg_channel(FM_SLOT *SLOT)
{ {
unsigned int swap_flag = 0;
unsigned int i = 4; /* four operators per channel */ unsigned int i = 4; /* four operators per channel */
do do
{ {
/* reset SSG-EG swap flag (Eke-Eke) */
swap_flag = 0;
switch(SLOT->state) switch(SLOT->state)
{ {
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)))
{ {
/* update attenuation level */
SLOT->volume += (~SLOT->volume * (eg_inc[SLOT->eg_sel_ar + ((ym2612.OPN.eg_cnt>>SLOT->eg_sh_ar)&7)]))>>4; 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) if (SLOT->volume <= MIN_ATT_INDEX)
{ {
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; break;
case EG_DEC: /* decay phase */ case EG_DEC: /* decay phase */
if ( !(ym2612.OPN.eg_cnt & ((1<<SLOT->eg_sh_d1r)-1) ) ) if (!(ym2612.OPN.eg_cnt & ((1<<SLOT->eg_sh_d1r)-1)))
{ {
if (SLOT->ssg&0x08) /* SSG EG type envelope selected */ /* SSG EG type */
SLOT->volume += 6 * eg_inc[SLOT->eg_sel_d1r + ((ym2612.OPN.eg_cnt>>SLOT->eg_sh_d1r)&7)]; 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 else
{
/* update attenuation level */
SLOT->volume += eg_inc[SLOT->eg_sel_d1r + ((ym2612.OPN.eg_cnt>>SLOT->eg_sh_d1r)&7)]; 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 */ /* recalculate EG output */
if ( SLOT->volume >= (INT32)(SLOT->sl) ) SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
{ }
SLOT->volume = (INT32)(SLOT->sl);
SLOT->state = EG_SUS;
}
/* check phase transition*/
if (SLOT->volume >= (INT32)(SLOT->sl))
SLOT->state = EG_SUS;
}
break; break;
case EG_SUS: /* sustain phase */ case EG_SUS: /* sustain phase */
if (SLOT->ssg&0x08) /* SSG EG type envelope selected */ if (!(ym2612.OPN.eg_cnt & ((1<<SLOT->eg_sh_d2r)-1)))
{ {
if ( !(ym2612.OPN.eg_cnt & ((1<<SLOT->eg_sh_d2r)-1) ) ) /* SSG EG type */
if (SLOT->ssg&0x08)
{ {
/* Nemesis */ /* update attenuation level */
SLOT->volume += 6 * eg_inc[SLOT->eg_sel_d2r + ((ym2612.OPN.eg_cnt>>SLOT->eg_sh_d2r)&7)]; 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;
} }
else
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<<SLOT->eg_sh_d2r)-1) ) )
{ {
/* update attenuation level */
SLOT->volume += eg_inc[SLOT->eg_sel_d2r + ((ym2612.OPN.eg_cnt>>SLOT->eg_sh_d2r)&7)]; 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 ) if ( SLOT->volume >= MAX_ATT_INDEX )
SLOT->volume = MAX_ATT_INDEX; SLOT->volume = MAX_ATT_INDEX;
/* do not change SLOT->state (verified on real chip) */ /* do not change SLOT->state (verified on real chip) */
/* recalculate EG output */
SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
} }
} }
break; break;
case EG_REL: /* release phase */ case EG_REL: /* release phase */
if ( !(ym2612.OPN.eg_cnt & ((1<<SLOT->eg_sh_rr)-1) ) ) if (!(ym2612.OPN.eg_cnt & ((1<<SLOT->eg_sh_rr)-1)))
{ {
/* SSG EG type */
if (SLOT->ssg&0x08) 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 else
{
/* update attenuation level */
SLOT->volume += eg_inc[SLOT->eg_sel_rr + ((ym2612.OPN.eg_cnt>>SLOT->eg_sh_rr)&7)]; SLOT->volume += eg_inc[SLOT->eg_sel_rr + ((ym2612.OPN.eg_cnt>>SLOT->eg_sh_rr)&7)];
if ( SLOT->volume >= MAX_ATT_INDEX ) /* check phase transition*/
{ if (SLOT->volume >= MAX_ATT_INDEX)
SLOT->volume = MAX_ATT_INDEX; {
SLOT->state = EG_OFF; SLOT->volume = MAX_ATT_INDEX;
SLOT->state = EG_OFF;
}
} }
/* recalculate EG output */
SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
} }
break; 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++; SLOT++;
i--; i--;
} while (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)) #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 else
{ {
SLOT->eg_sh_ar = 0; 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]; SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr];
@ -1371,30 +1536,12 @@ static int init_tables(void)
/* CSM Key Controll */ /* CSM Key Controll */
INLINE void CSMKeyControll(FM_CH *CH) INLINE void CSMKeyControll(FM_CH *CH)
{ {
/* all key ON/OFF (only for operator which have been OFF)*/ /* all key ON (verified by Nemesis on real hardware) */
if (!CH->SLOT[SLOT1].key) FM_KEYON_CSM(CH,SLOT1);
{ FM_KEYON_CSM(CH,SLOT2);
FM_KEYON(CH,SLOT1); FM_KEYON_CSM(CH,SLOT3);
FM_KEYOFF(CH,SLOT1); FM_KEYON_CSM(CH,SLOT4);
} ym2612.OPN.SL3.key_csm = 1;
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);
}
} }
INLINE void INTERNAL_TIMER_A() 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; if (ym2612.OPN.ST.TAL) ym2612.OPN.ST.TAC += ym2612.OPN.ST.TAL;
else 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 */ /* CSM mode auto key on */
if ((ym2612.OPN.ST.mode & 0xC0) == 0x80) CSMKeyControll (&(ym2612.CH[2])); if ((ym2612.OPN.ST.mode & 0xC0) == 0x80) CSMKeyControll(&ym2612.CH[2]);
} }
} }
} }
@ -1567,8 +1714,13 @@ static void OPNWriteReg(int r, int v)
break; break;
case 0x90: /* SSG-EG */ case 0x90: /* SSG-EG */
SLOT->ssg = v&0x0f; SLOT->ssg = v&0x0f;
SLOT->ssgn = (v&0x04)>>1; /* bit 1 in ssgn = attack */
/* 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 : /* SSG-EG envelope shapes :
@ -1758,18 +1910,13 @@ void YM2612UpdateOne(int **buffer, int length)
out_fm[4] = 0; out_fm[4] = 0;
out_fm[5] = 0; out_fm[5] = 0;
/* calculate FM */ /* update SSG-EG enveloppe type */
chan_calc(&ym2612.CH[0]); update_ssg_eg_channel(&ym2612.CH[0].SLOT[SLOT1]);
chan_calc(&ym2612.CH[1]); update_ssg_eg_channel(&ym2612.CH[1].SLOT[SLOT1]);
chan_calc(&ym2612.CH[2]); update_ssg_eg_channel(&ym2612.CH[2].SLOT[SLOT1]);
chan_calc(&ym2612.CH[3]); update_ssg_eg_channel(&ym2612.CH[3].SLOT[SLOT1]);
chan_calc(&ym2612.CH[4]); update_ssg_eg_channel(&ym2612.CH[4].SLOT[SLOT1]);
if (ym2612.dacen) update_ssg_eg_channel(&ym2612.CH[5].SLOT[SLOT1]);
{
/* DAC Mode */
*(ym2612.CH[5].connect4) += ym2612.dacout;
}
else chan_calc(&ym2612.CH[5]);
/* advance envelope generator */ /* advance envelope generator */
ym2612.OPN.eg_timer += ym2612.OPN.eg_timer_add; 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]); 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]); 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]);
@ -1815,8 +1975,23 @@ void YM2612UpdateOne(int **buffer, int length)
*bufR++ = rt; *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 */ /* timer A control */
INTERNAL_TIMER_A(); 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); INTERNAL_TIMER_B(length);
@ -1845,27 +2020,26 @@ int YM2612ResetChip(void)
ym2612.OPN.eg_timer = 0; ym2612.OPN.eg_timer = 0;
ym2612.OPN.eg_cnt = 0; ym2612.OPN.eg_cnt = 0;
ym2612.OPN.ST.status = 0; ym2612.OPN.ST.status = 0;
ym2612.OPN.ST.mode = 0; ym2612.OPN.ST.mode = 0;
ym2612.OPN.ST.TA = 0; ym2612.OPN.ST.TAC = 0;
ym2612.OPN.ST.TAL = 0; ym2612.OPN.ST.TBC = 0;
ym2612.OPN.ST.TAC = 0; OPNWriteMode(0x26,0x00);
ym2612.OPN.ST.TB = 0; OPNWriteMode(0x25,0x00);
ym2612.OPN.ST.TBL = 0; OPNWriteMode(0x24,0x00);
ym2612.OPN.ST.TBC = 0;
reset_channels(&ym2612.CH[0] , 6 ); reset_channels(&ym2612.CH[0] , 6 );
for(i = 0xb6 ; i >= 0xb4 ; i-- ) for(i = 0xb6 ; i >= 0xb4 ; i-- )
{ {
OPNWriteReg(i ,0xc0); /*OPNWriteReg(i ,0xc0);
OPNWriteReg(i|0x100,0xc0); OPNWriteReg(i|0x100,0xc0);*/
OPNWriteReg(i ,0);
OPNWriteReg(i|0x100,0);
} }
for(i = 0xb2 ; i >= 0x30 ; i-- ) for(i = 0xb2 ; i >= 0x30 ; i-- )
{ {
OPNWriteReg(i ,0); OPNWriteReg(i ,0);
OPNWriteReg(i|0x100,0); OPNWriteReg(i|0x100,0);
} }
for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteMode(i,0); /* fixed (Eke-Eke) */
/* DAC mode clear */ /* DAC mode clear */
ym2612.dacen = 0; ym2612.dacen = 0;
@ -1878,22 +2052,24 @@ int YM2612ResetChip(void)
/* n = number */ /* n = number */
/* a = address */ /* a = address */
/* v = value */ /* 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 */ v &= 0xff; /* adjust to 8 bit bus */
switch( a&3 ) switch( a&3 )
{ {
case 0: /* address port 0 */ case 0: /* address port 0 */
ym2612.OPN.ST.address[0] = v; ym2612.OPN.ST.address = v;
break; break;
case 1: /* data port 0 */ case 2: /* address port 1 */
addr = ym2612.OPN.ST.address[0]; ym2612.OPN.ST.address = v | 0x100;
fm_reg[0][addr] = v; break;
switch( addr & 0xf0 )
default: /* data port */
{
int addr = ym2612.OPN.ST.address;
switch( addr & 0x1f0 )
{ {
case 0x20: /* 0x20-0x2f Mode */ case 0x20: /* 0x20-0x2f Mode */
switch( addr ) switch( addr )
@ -1915,22 +2091,21 @@ int YM2612Write(unsigned char a, unsigned char v)
OPNWriteReg(addr,v); OPNWriteReg(addr,v);
} }
break; 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);
} }

View File

@ -22,8 +22,9 @@
extern int YM2612Init(int baseclock, int rate); extern int YM2612Init(int baseclock, int rate);
extern int YM2612ResetChip(void); extern int YM2612ResetChip(void);
extern void YM2612UpdateOne(int **buffer, int length); extern void YM2612UpdateOne(int **buffer, int length);
extern int YM2612Write(unsigned char a, unsigned char v); extern void YM2612Write(unsigned int a, unsigned int v);
extern int YM2612Read(void); extern unsigned int YM2612Read(void);
extern unsigned char *YM2612GetContextPtr(void);
extern unsigned int YM2612GetContextSize(void);
#endif /* _H_FM_FM_ */ #endif /* _H_FM_FM_ */

View File

@ -23,18 +23,9 @@
#include "shared.h" #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 */ /* cycle-accurate samples */
static int m68cycles_per_sample[2]; 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 */ /* return the number of samples that should have been rendered so far */
static inline uint32 fm_sample_cnt(uint8 is_z80) static inline uint32 fm_sample_cnt(uint8 is_z80)
{ {
@ -56,7 +47,7 @@ static inline void fm_update()
int *tempBuffer[2]; int *tempBuffer[2];
tempBuffer[0] = snd.fm.buffer[0] + snd.fm.lastStage; tempBuffer[0] = snd.fm.buffer[0] + snd.fm.lastStage;
tempBuffer[1] = snd.fm.buffer[1] + 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; snd.fm.lastStage = snd.fm.curStage;
} }
} }
@ -82,7 +73,7 @@ void sound_init(int rate)
m68cycles_per_sample[1] = m68cycles_per_sample[0]; m68cycles_per_sample[1] = m68cycles_per_sample[0];
/* YM2612 is emulated at original frequency (VLCK/144) */ /* YM2612 is emulated at original frequency (VLCK/144) */
if (config.hq_fm && !config.fm_core) if (config.hq_fm)
{ {
m68cycles_per_sample[0] = 144; m68cycles_per_sample[0] = 144;
} }
@ -91,22 +82,7 @@ void sound_init(int rate)
SN76489_Init(0, (int)zclk, rate); SN76489_Init(0, (int)zclk, rate);
SN76489_Config(0, MUTE_ALLON, VOL_FULL, FB_SEGAVDP, SRW_SEGAVDP, 0); SN76489_Config(0, MUTE_ALLON, VOL_FULL, FB_SEGAVDP, SRW_SEGAVDP, 0);
if (config.fm_core) YM2612Init ((int)vclk, rate);
{
_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);
}
} }
void sound_update(int fm_len, int psg_len) void sound_update(int fm_len, int psg_len)
@ -127,21 +103,16 @@ void sound_update(int fm_len, int psg_len)
} }
/* YM2612 control */ /* YM2612 control */
/* restore FM registers */
/* restore FM context */
void fm_restore(void) void fm_restore(void)
{ {
int i; unsigned char *temp = malloc(YM2612GetContextSize());
if (!temp) return;
_YM2612_Reset(); memcpy(temp, YM2612GetContextPtr(), YM2612GetContextSize());
YM2612ResetChip();
/* feed all the registers and update internal state */ memcpy(YM2612GetContextPtr(), temp, YM2612GetContextSize());
for(i = 0; i < 0x100; i++) free(temp);
{
_YM2612_Write(0, i);
_YM2612_Write(1, fm_reg[0][i]);
_YM2612_Write(2, i);
_YM2612_Write(3, fm_reg[1][i]);
}
} }
/* write FM chip */ /* 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); snd.fm.curStage = fm_sample_cnt(cpu);
fm_update(); fm_update();
} }
_YM2612_Write(address & 3, data); YM2612Write(address, data);
} }
/* read FM status */ /* read FM status */
@ -160,7 +131,7 @@ unsigned int fm_read(unsigned int cpu, unsigned int address)
{ {
snd.fm.curStage = fm_sample_cnt(cpu); snd.fm.curStage = fm_sample_cnt(cpu);
fm_update(); fm_update();
return (_YM2612_Read() & 0xff); return YM2612Read();
} }

View File

@ -24,9 +24,6 @@
#ifndef _SOUND_H_ #ifndef _SOUND_H_
#define _SOUND_H_ #define _SOUND_H_
/* Global variables */
extern int fm_reg[2][0x100];
/* Function prototypes */ /* Function prototypes */
extern void sound_init(int rate); extern void sound_init(int rate);
extern void sound_update(int fm_len, int psg_len); 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 void fm_write(unsigned int cpu, unsigned int address, unsigned int data);
extern unsigned int fm_read(unsigned int cpu, unsigned int address); extern unsigned int fm_read(unsigned int cpu, unsigned int address);
extern void psg_write(unsigned int cpu, unsigned int data); extern void psg_write(unsigned int cpu, unsigned int data);
extern int (*_YM2612_Reset)(void);
#endif /* _SOUND_H_ */ #endif /* _SOUND_H_ */

View File

@ -22,30 +22,27 @@
#include "shared.h" #include "shared.h"
static unsigned char state[0x24000]; #define load_param(param, size) \
static unsigned int bufferptr; memcpy(param, &state[bufferptr], size); \
static inline void load_param(void *param, unsigned int size)
{
memcpy(param, &state[bufferptr], size);
bufferptr+= size; bufferptr+= size;
}
static inline void save_param(void *param, unsigned int size) #define save_param(param, size) \
{ memcpy(&state[bufferptr], param, size); \
memcpy(&state[bufferptr], param, size);
bufferptr+= size; bufferptr+= size;
}
void state_load(unsigned char *buffer) void state_load(unsigned char *buffer)
{ {
/* reset buffer pointer */ /* allocate memory */
bufferptr = 0; unsigned char *state = malloc(STATE_SIZE);
if (state == NULL) return;
/* buffer size */
int bufferptr = 0;
/* uncompress savestate */ /* uncompress savestate */
unsigned long inbytes, outbytes; unsigned long inbytes, outbytes;
memcpy(&inbytes, buffer, 4); memcpy(&inbytes, buffer, 4);
outbytes = 0x24000; outbytes = STATE_SIZE;
uncompress ((Bytef *)state, &outbytes, (Bytef *)(buffer + 4), inbytes); uncompress ((Bytef *)state, &outbytes, (Bytef *)(buffer + 4), inbytes);
/* reset system */ /* reset system */
@ -82,8 +79,7 @@ void state_load(unsigned char *buffer)
vdp_restore(temp_reg); vdp_restore(temp_reg);
// FM // FM
load_param(fm_reg,sizeof(fm_reg)); load_param(YM2612GetContextPtr(),YM2612GetContextSize());
fm_restore();
// PSG // PSG
load_param(SN76489_GetContextPtr (0),SN76489_GetContextSize ()); load_param(SN76489_GetContextPtr (0),SN76489_GetContextSize ());
@ -113,12 +109,19 @@ void state_load(unsigned char *buffer)
// Z80 // Z80
load_param(&Z80, sizeof(Z80_Regs)); load_param(&Z80, sizeof(Z80_Regs));
/* Free memory */
free(state);
} }
int state_save(unsigned char *buffer) int state_save(unsigned char *buffer)
{ {
/* reset buffer pointer */ /* allocate memory */
bufferptr = 0; unsigned char *state = malloc(STATE_SIZE);
if (state == NULL) return 0;
/* buffer size */
int bufferptr = 0;
// GENESIS // GENESIS
save_param(work_ram, sizeof(work_ram)); save_param(work_ram, sizeof(work_ram));
@ -147,7 +150,7 @@ int state_save(unsigned char *buffer)
save_param(&irq_status, sizeof(irq_status)); save_param(&irq_status, sizeof(irq_status));
// FM // FM
save_param(fm_reg,sizeof(fm_reg)); save_param(YM2612GetContextPtr(),YM2612GetContextSize());
// PSG // PSG
save_param(SN76489_GetContextPtr (0),SN76489_GetContextSize ()); save_param(SN76489_GetContextPtr (0),SN76489_GetContextSize ());
@ -180,10 +183,13 @@ int state_save(unsigned char *buffer)
/* compress state file */ /* compress state file */
unsigned long inbytes = bufferptr; unsigned long inbytes = bufferptr;
unsigned long outbytes = 0x26000; unsigned long outbytes = STATE_SIZE;
compress2 ((Bytef *)(buffer + 4), &outbytes, (Bytef *)state, inbytes, 9); compress2 ((Bytef *)(buffer + 4), &outbytes, (Bytef *)state, inbytes, 9);
memcpy(buffer, &outbytes, 4); memcpy(buffer, &outbytes, 4);
/* Free memory */
free(state);
/* return total size */ /* return total size */
return (outbytes + 4); return (outbytes + 4);
} }

View File

@ -23,6 +23,8 @@
#ifndef _STATE_H_ #ifndef _STATE_H_
#define _STATE_H_ #define _STATE_H_
#define STATE_SIZE 0x28000
/* Function prototypes */ /* Function prototypes */
extern void state_load(unsigned char *buffer); extern void state_load(unsigned char *buffer);
extern int state_save(unsigned char *buffer); extern int state_save(unsigned char *buffer);

View File

@ -169,7 +169,7 @@ int audio_init (int rate)
#endif #endif
/* SRC */ /* SRC */
if (config.hq_fm && !config.fm_core) if (config.hq_fm)
{ {
/* SRC ratio (YM2612 original samplerate is VCLK/144) */ /* 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); src_data.src_ratio = ((double)rate * 144.0) / ((double) vdp_rate * (double)m68cycles_per_line * (double)lines_per_frame);

View File

@ -497,7 +497,7 @@ static inline void data_write (unsigned int data)
switch (code & 0x0F) switch (code & 0x0F)
{ {
case 0x01: /* VRAM */ case 0x01: /* VRAM */
/* Byte-swap data if A0 is set */ /* Byte-swap data if A0 is set */
if (addr & 1) data = (data >> 8) | (data << 8); if (addr & 1) data = (data >> 8) | (data << 8);
@ -833,11 +833,11 @@ void vdp_data_w(unsigned int data)
fifo_update(); fifo_update();
if (fifo_write_cnt == 0) if (fifo_write_cnt == 0)
{ {
/* reset cycle counter */ /* reset cycle counter */
fifo_lastwrite = count_m68k; fifo_lastwrite = count_m68k;
/* FIFO is not empty anymore */ /* FIFO is not empty anymore */
status &= 0xFDFF; status &= 0xFDFF;
} }
/* increase write counter */ /* increase write counter */