[-]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
* 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 !

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
};
/* 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,

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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; i<nb_butns; i++)
@ -243,8 +243,8 @@ void soundmenu ()
int ret;
int quit = 0;
int prevmenu = menu;
int count = 6;
char items[6][25];
int count = 5;
char items[5][25];
strcpy (menutitle, "Press B to return");
@ -256,9 +256,8 @@ void soundmenu ()
sprintf (items[2], "Volume Boost: %dX", config.boost);
sprintf (items[3], "LowPass Filter: %s", config.filter ? " ON":"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");
sprintf (items[5], "FM core: %s", config.fm_core ? "GENS" : "MAME");
ret = domenu (&items[0], count, 1);
switch (ret)
@ -290,7 +289,7 @@ void soundmenu ()
case 4:
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)
{
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;

View File

@ -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"

View File

@ -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<<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;
/* 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<<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 */
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<<SLOT->eg_sh_d2r)-1)))
{
if ( !(ym2612.OPN.eg_cnt & ((1<<SLOT->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<<SLOT->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<<SLOT->eg_sh_rr)-1) ) )
if (!(ym2612.OPN.eg_cnt & ((1<<SLOT->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);
}

View File

@ -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_ */

View File

@ -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();
}

View File

@ -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_ */

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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 */