mirror of
https://github.com/ekeeke/Genesis-Plus-GX.git
synced 2024-09-10 16:53:15 +02:00
[-]removed Gens YM2612 core support
[~]fixed YM2612 context restore [+]improved YM2612 emulation accuracy (BROKEN)
This commit is contained in:
parent
6f65ac3f15
commit
4eb8d17acf
@ -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 !
|
||||
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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_ */
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
10
source/vdp.c
10
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 */
|
||||
|
Loading…
Reference in New Issue
Block a user