mirror of
https://github.com/ekeeke/Genesis-Plus-GX.git
synced 2024-11-09 04:15:14 +01: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
|
* 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 !
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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"
|
||||||
|
@ -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,7 +887,10 @@ 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 */
|
||||||
|
/* 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*/)
|
if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/)
|
||||||
{
|
{
|
||||||
SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
|
SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
|
||||||
@ -792,7 +899,7 @@ INLINE void set_ar_ksr(FM_CH *CH,FM_SLOT *SLOT,int v)
|
|||||||
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) */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -897,143 +1004,201 @@ 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
|
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)];
|
SLOT->volume += eg_inc[SLOT->eg_sel_d1r + ((ym2612.OPN.eg_cnt>>SLOT->eg_sh_d1r)&7)];
|
||||||
|
|
||||||
|
/* recalculate EG output */
|
||||||
|
SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check transition even if no volume update: this fixes the case when SL = MIN_ATT_INDEX */
|
/* check phase transition*/
|
||||||
if ( SLOT->volume >= (INT32)(SLOT->sl) )
|
if (SLOT->volume >= (INT32)(SLOT->sl))
|
||||||
{
|
|
||||||
SLOT->volume = (INT32)(SLOT->sl);
|
|
||||||
SLOT->state = EG_SUS;
|
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)];
|
||||||
|
|
||||||
if ( SLOT->volume >= ENV_QUIET) /* Alone Coder */
|
/* recalculate EG output */
|
||||||
{
|
if (SLOT->ssgn ^ (SLOT->ssg&0x04)) /* SSG-EG Output Inversion */
|
||||||
if (SLOT->ssg&0x01) /* bit 0 = hold */
|
SLOT->vol_out = ((UINT32)SLOT->volume ^ 0x1FF) + 1 + SLOT->tl;
|
||||||
{
|
|
||||||
if (SLOT->ssgn&1) /* have we swapped once ??? */
|
|
||||||
{
|
|
||||||
/* yes, so do nothing, just hold current level */
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
swap_flag = (SLOT->ssg&0x02) | 1 ; /* bit 1 = alternate */
|
SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* same as KEY-ON operation */
|
/* update attenuation level */
|
||||||
|
SLOT->volume += eg_inc[SLOT->eg_sel_d2r + ((ym2612.OPN.eg_cnt>>SLOT->eg_sh_d2r)&7)];
|
||||||
|
|
||||||
/* restart of the Phase Generator should be here */
|
/* check phase transition*/
|
||||||
SLOT->phase = 0;
|
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)))
|
||||||
|
{
|
||||||
|
/* SSG EG type */
|
||||||
|
if (SLOT->ssg&0x08)
|
||||||
|
{
|
||||||
|
/* 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)];
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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*/)
|
if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/)
|
||||||
{
|
{
|
||||||
SLOT->state = EG_ATT; /* phase -> Attack */
|
SLOT->state = EG_ATT; /* phase -> Attack */
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Attack Rate is maximal: directly switch to Decay (or Substain) */
|
/* Attack Rate is maximal: directly switch to Decay */
|
||||||
SLOT->volume = MIN_ATT_INDEX;
|
SLOT->volume = MIN_ATT_INDEX;
|
||||||
SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC;
|
SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; /* special case where SL=0 */
|
||||||
}
|
update_out = 1;
|
||||||
|
|
||||||
swap_flag = (SLOT->ssg&0x02); /* bit 1 = alternate */
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
else if (SLOT->state == EG_REL || (!(SLOT->ssgn ^ (SLOT->ssg & 0x04))))
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( !(ym2612.OPN.eg_cnt & ((1<<SLOT->eg_sh_d2r)-1) ) )
|
|
||||||
{
|
|
||||||
SLOT->volume += eg_inc[SLOT->eg_sel_d2r + ((ym2612.OPN.eg_cnt>>SLOT->eg_sh_d2r)&7)];
|
|
||||||
|
|
||||||
if ( SLOT->volume >= MAX_ATT_INDEX )
|
|
||||||
SLOT->volume = MAX_ATT_INDEX;
|
|
||||||
/* do not change SLOT->state (verified on real chip) */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EG_REL: /* release phase */
|
|
||||||
if ( !(ym2612.OPN.eg_cnt & ((1<<SLOT->eg_sh_rr)-1) ) )
|
|
||||||
{
|
|
||||||
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)];
|
|
||||||
else
|
|
||||||
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->volume = MAX_ATT_INDEX;
|
||||||
SLOT->state = EG_OFF;
|
update_out = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int out = (UINT32)SLOT->volume;
|
/* next slot */
|
||||||
|
|
||||||
/* 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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define volume_calc(OP) ((OP)->vol_out + (AM & (OP)->AMmask))
|
#define volume_calc(OP) ((OP)->vol_out + (AM & (OP)->AMmask))
|
||||||
|
|
||||||
INLINE void update_phase_lfo_slot(FM_SLOT *SLOT , INT32 pms, UINT32 block_fnum)
|
INLINE void update_phase_lfo_slot(FM_SLOT *SLOT , INT32 pms, UINT32 block_fnum)
|
||||||
@ -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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1568,7 +1715,12 @@ static void OPNWriteReg(int r, int v)
|
|||||||
|
|
||||||
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);
|
||||||
@ -1846,26 +2021,25 @@ int YM2612ResetChip(void)
|
|||||||
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.TAL = 0;
|
|
||||||
ym2612.OPN.ST.TAC = 0;
|
ym2612.OPN.ST.TAC = 0;
|
||||||
ym2612.OPN.ST.TB = 0;
|
|
||||||
ym2612.OPN.ST.TBL = 0;
|
|
||||||
ym2612.OPN.ST.TBC = 0;
|
ym2612.OPN.ST.TBC = 0;
|
||||||
|
OPNWriteMode(0x26,0x00);
|
||||||
|
OPNWriteMode(0x25,0x00);
|
||||||
|
OPNWriteMode(0x24,0x00);
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
@ -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_ */
|
||||||
|
@ -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)
|
|
||||||
{
|
|
||||||
_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)
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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_ */
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user