diff --git a/scripts/src/emu.lua b/scripts/src/emu.lua index eded02ed3681d..039d0e4296691 100644 --- a/scripts/src/emu.lua +++ b/scripts/src/emu.lua @@ -255,6 +255,8 @@ files { MAME_DIR .. "src/devices/sound/flt_rc.h", MAME_DIR .. "src/emu/sound/wavwrite.c", MAME_DIR .. "src/emu/sound/wavwrite.h", + MAME_DIR .. "src/emu/sound/vgmwrite.c", + MAME_DIR .. "src/emu/sound/vgmwrite.h", MAME_DIR .. "src/devices/sound/samples.c", MAME_DIR .. "src/devices/sound/samples.h", MAME_DIR .. "src/emu/drivers/empty.c", diff --git a/src/devices/sound/2203intf.c b/src/devices/sound/2203intf.c index 279ae52cea44d..50bcb84255b55 100644 --- a/src/devices/sound/2203intf.c +++ b/src/devices/sound/2203intf.c @@ -133,7 +133,7 @@ void ym2203_device::device_start() m_stream = machine().sound().stream_alloc(*this,0,1,rate, stream_update_delegate(FUNC(ym2203_device::stream_generate),this)); /* Initialize FM emurator */ - m_chip = ym2203_init(this,this,clock(),rate,timer_handler,IRQHandler,&psgintf); + m_chip = ym2203_init(this,this,clock(),rate,timer_handler,IRQHandler,&psgintf,ay8910_device::m_flags); assert_always(m_chip != NULL, "Error creating YM2203 chip"); } diff --git a/src/devices/sound/2608intf.c b/src/devices/sound/2608intf.c index 4a2c25597bb62..4d1345f81859b 100644 --- a/src/devices/sound/2608intf.c +++ b/src/devices/sound/2608intf.c @@ -150,7 +150,7 @@ void ym2608_device::device_start() /* initialize YM2608 */ m_chip = ym2608_init(this,this,clock(),rate, pcmbufa,pcmsizea, - timer_handler,IRQHandler,&psgintf); + timer_handler,IRQHandler,&psgintf,ay8910_device::m_flags); assert_always(m_chip != NULL, "Error creating YM2608 chip"); } diff --git a/src/devices/sound/ay8910.c b/src/devices/sound/ay8910.c index 9e8d397ff7afe..1c8cc4d924cf0 100644 --- a/src/devices/sound/ay8910.c +++ b/src/devices/sound/ay8910.c @@ -231,6 +231,12 @@ has twice the steps, happening twice as fast. #include "emu.h" #include "ay8910.h" +#include "sound/vgmwrite.h" + +extern const device_type YM2203; +extern const device_type YM2608; +extern const device_type YM2610; +extern const device_type YM2610B; /************************************* * @@ -583,6 +589,8 @@ UINT16 ay8910_device::mix_3D() void ay8910_device::ay8910_write_reg(int r, int v) { + vgm_write(vgm_idx, 0x00, r, v); + //if (r >= 11 && r <= 13 ) printf("%d %x %02x\n", PSG->index, r, v); m_regs[r] = v; @@ -878,6 +886,7 @@ void ay8910_device::ay8910_statesave() void ay8910_device::device_start() { int master_clock = clock(); + UINT8 chp_tp_vgm; if (m_ioports < 1 && !(m_port_a_read_cb.isnull() && m_port_a_write_cb.isnull())) fatalerror("Device '%s' is a %s and has no port A!", tag(), name()); @@ -910,6 +919,34 @@ void ay8910_device::device_start() ay_set_clock(master_clock); ay8910_statesave(); + + if (type() == AY8910) chp_tp_vgm = 0x00; + else if (type() == AY8912) chp_tp_vgm = 0x01; + else if (type() == AY8913) chp_tp_vgm = 0x02; + else if (type() == AY8930) chp_tp_vgm = 0x03; + else if (type() == AY8914) chp_tp_vgm = 0x04; + else if (type() == YM2149) chp_tp_vgm = 0x10; + else if (type() == YM3439) chp_tp_vgm = 0x11; + else if (type() == YMZ284) chp_tp_vgm = 0x12; + else if (type() == YMZ294) chp_tp_vgm = 0x13; + else if (type() == YM2203) chp_tp_vgm = 0x20; + else if (type() == YM2608) chp_tp_vgm = 0x21; + else if (type() == YM2610 || type() == YM2610B) chp_tp_vgm = 0x22; + else chp_tp_vgm = 0xFF; + + if (! (chp_tp_vgm & 0x20)) + { + vgm_idx = vgm_open(VGMC_AY8910, clock()); + vgm_header_set(vgm_idx, 0x00, chp_tp_vgm); + vgm_header_set(vgm_idx, 0x01, m_flags); + vgm_header_set(vgm_idx, 0x10, m_res_load[0]); + vgm_header_set(vgm_idx, 0x11, m_res_load[1]); + vgm_header_set(vgm_idx, 0x12, m_res_load[2]); + } + else + { + vgm_idx = 0xFFFF; + } } @@ -1126,6 +1163,7 @@ const device_type AY8910 = &device_creator; ay8910_device::ay8910_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : device_t(mconfig, AY8910, "AY-3-8910A", tag, owner, clock, "ay8910", __FILE__), device_sound_interface(mconfig, *this), + m_flags(AY8910_LEGACY_OUTPUT), m_type(PSG_TYPE_AY), m_streams(3), m_ioports(2), @@ -1148,7 +1186,6 @@ ay8910_device::ay8910_device(const machine_config &mconfig, const char *tag, dev m_zero_is_off(1), m_par(&ay8910_param), m_par_env(&ay8910_param), - m_flags(AY8910_LEGACY_OUTPUT), m_port_a_read_cb(*this), m_port_b_read_cb(*this), m_port_a_write_cb(*this), @@ -1168,6 +1205,7 @@ ay8910_device::ay8910_device(const machine_config &mconfig, device_type type, co psg_type_t psg_type, int streams, int ioports, const char *shortname, const char *source) : device_t(mconfig, type, name, tag, owner, clock, shortname, source), device_sound_interface(mconfig, *this), + m_flags(AY8910_LEGACY_OUTPUT), m_type(psg_type), m_streams(streams), m_ioports(ioports), @@ -1190,7 +1228,6 @@ ay8910_device::ay8910_device(const machine_config &mconfig, device_type type, co m_zero_is_off( psg_type == PSG_TYPE_AY ? 1 : 0), m_par( psg_type == PSG_TYPE_AY ? &ay8910_param : &ym2149_param), m_par_env( psg_type == PSG_TYPE_AY ? &ay8910_param : &ym2149_param_env), - m_flags(AY8910_LEGACY_OUTPUT), m_port_a_read_cb(*this), m_port_b_read_cb(*this), m_port_a_write_cb(*this), diff --git a/src/devices/sound/ay8910.h b/src/devices/sound/ay8910.h index 30ef634b766d4..2e7b9fc45c9a7 100644 --- a/src/devices/sound/ay8910.h +++ b/src/devices/sound/ay8910.h @@ -152,6 +152,7 @@ class ay8910_device : public device_t, // device-level overrides virtual void device_start(); virtual void device_reset(); + int m_flags; /* Flags */ // sound stream update overrides virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples); @@ -191,12 +192,12 @@ class ay8910_device : public device_t, INT32 m_vol_table[AY8910_NUM_CHANNELS][16]; INT32 m_env_table[AY8910_NUM_CHANNELS][32]; INT32 m_vol3d_table[8*32*32*32]; - int m_flags; /* Flags */ int m_res_load[3]; /* Load on channel in ohms */ devcb_read8 m_port_a_read_cb; devcb_read8 m_port_b_read_cb; devcb_write8 m_port_a_write_cb; devcb_write8 m_port_b_write_cb; + UINT16 vgm_idx; /* VGM index */ }; extern const device_type AY8910; diff --git a/src/devices/sound/c140.c b/src/devices/sound/c140.c index 6649f53616ae0..1b45bc8f31234 100644 --- a/src/devices/sound/c140.c +++ b/src/devices/sound/c140.c @@ -46,6 +46,7 @@ Unmapped registers: #include "emu.h" +#include "sound/vgmwrite.h" #include "c140.h" struct voice_registers @@ -135,6 +136,18 @@ void c140_device::device_start() m_mixer_buffer_left = auto_alloc_array(machine(), INT16, 2 * m_sample_rate); m_mixer_buffer_right = m_mixer_buffer_left + m_sample_rate; + if (m_pRom != NULL) + { + m_vgm_idx = vgm_open(VGMC_C140, m_baserate); + vgm_header_set(m_vgm_idx, 0x01, m_banking_type); + vgm_write_large_data(m_vgm_idx, 0x01, region()->bytes(), 0x00, 0x00, m_pRom); + } + else + { + logerror("VGM Warning: C140 wants to use dynamic memory (i.e. RAM) - disabled C140 logging!\n"); + m_vgm_idx = 0xFFFF; + } + save_item(NAME(m_REG)); for (int i = 0; i < C140_MAX_VOICE; i++) @@ -373,6 +386,8 @@ WRITE8_MEMBER( c140_device::c140_w ) offset&=0x1ff; + vgm_write(m_vgm_idx, 0x00, offset, data); + // mirror the bank registers on the 219, fixes bkrtmaq (and probably xday2 based on notes in the HLE) if ((offset >= 0x1f8) && (m_banking_type == C140_TYPE_ASIC219)) { @@ -432,6 +447,9 @@ WRITE8_MEMBER( c140_device::c140_w ) void c140_device::set_base(void *base) { m_pRom = (INT8 *)base; + + //logerror("VGM Warning: C140 uses Dynamic Memory!\n"); + //vgm_write_large_data(m_vgm_idx, 0x01, region()->bytes(), 0x00, 0x00, m_pRom); } diff --git a/src/devices/sound/c140.h b/src/devices/sound/c140.h index 13b6bfbcc7c8a..2232736928f92 100644 --- a/src/devices/sound/c140.h +++ b/src/devices/sound/c140.h @@ -116,6 +116,8 @@ class c140_device : public device_t, INT16 m_pcmtbl[8]; //2000.06.26 CAB C140_VOICE m_voi[C140_MAX_VOICE]; + + UINT16 m_vgm_idx; // VGM index }; extern const device_type C140; diff --git a/src/devices/sound/c6280.c b/src/devices/sound/c6280.c index d484b8281c004..50c7e03b73c82 100644 --- a/src/devices/sound/c6280.c +++ b/src/devices/sound/c6280.c @@ -39,6 +39,7 @@ */ #include "emu.h" +#include "sound/vgmwrite.h" #include "c6280.h" /* only needed for io_buffer */ @@ -149,6 +150,8 @@ WRITE8_MEMBER( c6280_device::c6280_w ) { m_cpudevice->io_set_buffer(data); + vgm_write(m_vgm_idx, 0x00, offset, data); + channel *chan = &m_channel[m_select]; /* Update stream */ @@ -245,6 +248,7 @@ c6280_device::c6280_device(const machine_config &mconfig, const char *tag, devic void c6280_device::device_start() { int rate = clock() / 16; + m_vgm_idx = vgm_open(VGMC_C6280, clock()); /* Create stereo stream */ m_stream = machine().sound().stream_alloc(*this, 0, 2, rate); diff --git a/src/devices/sound/c6280.h b/src/devices/sound/c6280.h index af53962a83e53..867cb44d461c9 100644 --- a/src/devices/sound/c6280.h +++ b/src/devices/sound/c6280.h @@ -50,6 +50,8 @@ class c6280_device : public device_t, INT16 m_volume_table[32]; UINT32 m_noise_freq_tab[32]; UINT32 m_wave_freq_tab[4096]; + + UINT16 m_vgm_idx; }; extern const device_type C6280; diff --git a/src/devices/sound/fm.c b/src/devices/sound/fm.c index 3782790409f08..073854cf4db48 100644 --- a/src/devices/sound/fm.c +++ b/src/devices/sound/fm.c @@ -114,9 +114,12 @@ /************************************************************************/ #include "emu.h" +#include "sound/vgmwrite.h" #include "fm.h" +extern const device_type YM2610B; // YM2610B mode detection + /* include external DELTA-T unit (when needed) */ #if (BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B) #include "ymdeltat.h" @@ -2093,6 +2096,7 @@ struct YM2203 UINT8 REGS[256]; /* registers */ FM_OPN OPN; /* OPN state */ FM_CH CH[3]; /* channel state */ + UINT16 vgm_idx; /* VGM index */ }; /* Generate samples for one of the YM2203s */ @@ -2258,7 +2262,8 @@ static void YM2203_save_state(YM2203 *F2203, device_t *device) 'rate' is sampling rate */ void * ym2203_init(void *param, device_t *device, int clock, int rate, - FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg) + FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg, + int psg_flags) { YM2203 *F2203; @@ -2285,6 +2290,10 @@ void * ym2203_init(void *param, device_t *device, int clock, int rate, #ifdef __SAVE_H__ YM2203_save_state(F2203, device); #endif + + F2203->vgm_idx = vgm_open(VGMC_YM2203, F2203->OPN.ST.clock); + vgm_header_set(F2203->vgm_idx, 0x01, psg_flags); + return F2203; } @@ -2312,11 +2321,15 @@ int ym2203_write(void *chip,int a,UINT8 v) /* prescaler select : 2d,2e,2f */ if( v >= 0x2d && v <= 0x2f ) + { + vgm_write(F2203->vgm_idx, 0x00, v, 1); OPNPrescaler_w(OPN , v , 1); + } } else { /* data port */ int addr = OPN->ST.address; + vgm_write(F2203->vgm_idx, 0x00, addr, v); F2203->REGS[addr] = v; switch( addr & 0xf0 ) { @@ -2422,6 +2435,8 @@ struct YM2610 UINT8 flagmask; /* YM2608 only */ UINT8 irqmask; /* YM2608 only */ + + UINT16 vgm_idx; /* VGM index */ }; /* here is the virtual YM2608 */ @@ -2934,7 +2949,8 @@ static void YM2608_deltat_status_reset(void *chip, UINT8 changebits) /* YM2608(OPNA) */ void * ym2608_init(void *param, device_t *device, int clock, int rate, void *pcmrom,int pcmsize, - FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg) + FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg, + int psg_flags) { YM2608 *F2608; @@ -2982,6 +2998,11 @@ void * ym2608_init(void *param, device_t *device, int clock, int rate, #ifdef __SAVE_H__ YM2608_save_state(F2608, device); #endif + + F2608->vgm_idx = vgm_open(VGMC_YM2608, F2608->OPN.ST.clock); + vgm_header_set(F2608->vgm_idx, 0x01, psg_flags); + vgm_write_large_data(F2608->vgm_idx, 0x01, F2608->deltaT.memory_size, 0x00, 0x00, F2608->deltaT.memory); + return F2608; } @@ -3097,6 +3118,7 @@ int ym2608_write(void *chip, int a,UINT8 v) /* prescaler selecter : 2d,2e,2f */ if( v >= 0x2d && v <= 0x2f ) { + vgm_write(F2608->vgm_idx, 0x00, v, 2); OPNPrescaler_w(OPN , v , 2); F2608->deltaT.freqbase = OPN->ST.freqbase; } @@ -3107,6 +3129,7 @@ int ym2608_write(void *chip, int a,UINT8 v) break; /* verified on real YM2608 */ addr = OPN->ST.address; + vgm_write(F2608->vgm_idx, 0x00, addr, v); F2608->REGS[addr] = v; switch(addr & 0xf0) { @@ -3145,6 +3168,7 @@ int ym2608_write(void *chip, int a,UINT8 v) break; /* verified on real YM2608 */ addr = OPN->ST.address; + vgm_write(F2608->vgm_idx, 0x01, addr, v); F2608->REGS[addr | 0x100] = v; ym2608_update_req(OPN->ST.param); switch( addr & 0xf0 ) @@ -3618,6 +3642,7 @@ void *ym2610_init(void *param, device_t *device, int clock, int rate, { YM2610 *F2610; + UINT8 mode_b; /* allocate extend state space */ F2610 = auto_alloc_clear(device->machine(), YM2610); @@ -3655,6 +3680,13 @@ void *ym2610_init(void *param, device_t *device, int clock, int rate, #ifdef __SAVE_H__ YM2610_save_state(F2610, device); #endif + + F2610->vgm_idx = vgm_open(VGMC_YM2610, F2610->OPN.ST.clock); + mode_b = (device->type() == YM2610B); + vgm_header_set(F2610->vgm_idx, 0x00, mode_b); // set YM2610B mode + vgm_write_large_data(F2610->vgm_idx, 0x01, F2610->pcm_size, 0x00, 0x00, F2610->pcmbuf); + vgm_write_large_data(F2610->vgm_idx, 0x02, F2610->deltaT.memory_size, 0x00, 0x00, F2610->deltaT.memory); + return F2610; } @@ -3775,6 +3807,7 @@ int ym2610_write(void *chip, int a, UINT8 v) break; /* verified on real YM2608 */ addr = OPN->ST.address; + vgm_write(F2610->vgm_idx, 0x00, addr, v); F2610->REGS[addr] = v; switch(addr & 0xf0) { @@ -3844,6 +3877,7 @@ int ym2610_write(void *chip, int a, UINT8 v) ym2610_update_req(OPN->ST.param); addr = OPN->ST.address; + vgm_write(F2610->vgm_idx, 0x01, addr, v); F2610->REGS[addr | 0x100] = v; if( addr < 0x30 ) /* 100-12f : ADPCM A section */ diff --git a/src/devices/sound/fm.h b/src/devices/sound/fm.h index 878f753b599e6..c11bb03c7b65c 100644 --- a/src/devices/sound/fm.h +++ b/src/devices/sound/fm.h @@ -110,7 +110,8 @@ typedef void (*FM_IRQHANDLER)(void *param,int irq); ** return 0 = success */ void * ym2203_init(void *param, device_t *device, int baseclock, int rate, - FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg); + FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg, + int psg_flags); /* ** shutdown the YM2203 emulators @@ -154,7 +155,8 @@ void ym2203_postload(void *chip); /* -------------------- YM2608(OPNA) Interface -------------------- */ void * ym2608_init(void *param, device_t *device, int baseclock, int rate, void *pcmroma,int pcmsizea, - FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg); + FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg, + int psg_flags); void ym2608_shutdown(void *chip); void ym2608_reset_chip(void *chip); void ym2608_update_one(void *chip, FMSAMPLE **buffer, int length); diff --git a/src/devices/sound/fm2612.c b/src/devices/sound/fm2612.c index 034b07a85950b..1f7ef98404018 100644 --- a/src/devices/sound/fm2612.c +++ b/src/devices/sound/fm2612.c @@ -135,6 +135,7 @@ /************************************************************************/ #include "emu.h" +#include "sound/vgmwrite.h" #include "fm.h" /* shared function building option */ @@ -693,6 +694,8 @@ struct YM2612 /* dac output (YM2612) */ int dacen; INT32 dacout; + + UINT16 vgm_idx; /* VGM index */ }; /* log output level */ @@ -2390,6 +2393,9 @@ void * ym2612_init(void *param, device_t *device, int clock, int rate, #ifdef __SAVE_H__ YM2612_save_state(F2612, device); #endif + + F2612->vgm_idx = vgm_open(VGMC_YM2612, F2612->OPN.ST.clock); + return F2612; } @@ -2472,6 +2478,7 @@ int ym2612_write(void *chip, int a, UINT8 v) break; /* verified on real YM2608 */ addr = F2612->OPN.ST.address; + vgm_write(F2612->vgm_idx, 0x00, addr, v); F2612->REGS[addr] = v; switch( addr & 0xf0 ) { @@ -2509,6 +2516,7 @@ int ym2612_write(void *chip, int a, UINT8 v) break; /* verified on real YM2608 */ addr = F2612->OPN.ST.address; + vgm_write(F2612->vgm_idx, 0x01, addr, v); F2612->REGS[addr | 0x100] = v; ym2612_update_req(F2612->OPN.ST.param); OPNWriteReg(&(F2612->OPN),addr | 0x100,v); diff --git a/src/devices/sound/fmopl.c b/src/devices/sound/fmopl.c index 4932cde31fa41..adc407c7ed5bc 100644 --- a/src/devices/sound/fmopl.c +++ b/src/devices/sound/fmopl.c @@ -70,6 +70,7 @@ Revision History: */ #include "emu.h" +#include "sound/vgmwrite.h" #include "ymdeltat.h" #include "fmopl.h" @@ -320,6 +321,7 @@ struct FM_OPL UINT32 rate; /* sampling rate (Hz) */ double freqbase; /* frequency base */ attotime TimerBase; /* Timer base time (==sampling time)*/ + UINT16 vgm_idx; /* VGM index */ device_t *device; signed int phase_modulation; /* phase modulation input (SLOT 2) */ @@ -2040,6 +2042,7 @@ static int OPLWrite(FM_OPL *OPL,int a,int v) else { /* data port */ if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0); + vgm_write(OPL->vgm_idx, 0x00, OPL->address, v); OPLWriteReg(OPL,OPL->address,v); } return OPL->status>>7; @@ -2161,6 +2164,7 @@ void * ym3812_init(device_t *device, UINT32 clock, UINT32 rate) { OPL_save_state(YM3812, device); ym3812_reset_chip(YM3812); + YM3812->vgm_idx = vgm_open(VGMC_YM3812, clock); } return YM3812; } @@ -2289,6 +2293,7 @@ void *ym3526_init(device_t *device, UINT32 clock, UINT32 rate) { OPL_save_state(YM3526, device); ym3526_reset_chip(YM3526); + YM3526->vgm_idx = vgm_open(VGMC_YM3526, clock); } return YM3526; } @@ -2437,6 +2442,7 @@ void *y8950_init(device_t *device, UINT32 clock, UINT32 rate) /* reset */ OPL_save_state(Y8950, device); y8950_reset_chip(Y8950); + Y8950->vgm_idx = vgm_open(VGMC_Y8950, clock); } return Y8950; @@ -2492,6 +2498,7 @@ void y8950_set_delta_t_memory(void *chip, void * deltat_mem_ptr, int deltat_mem_ FM_OPL *OPL = (FM_OPL *)chip; OPL->deltat->memory = (UINT8 *)(deltat_mem_ptr); OPL->deltat->memory_size = deltat_mem_size; + vgm_write_large_data(OPL->vgm_idx, 0x01, OPL->deltat->memory_size, 0x00, 0x00, OPL->deltat->memory); } /* diff --git a/src/devices/sound/k051649.c b/src/devices/sound/k051649.c index d69b9f2544591..4fffd7fa4431e 100644 --- a/src/devices/sound/k051649.c +++ b/src/devices/sound/k051649.c @@ -25,6 +25,7 @@ ***************************************************************************/ #include "emu.h" +#include "sound/vgmwrite.h" #include "k051649.h" #define FREQ_BITS 16 @@ -73,6 +74,8 @@ void k051649_device::device_start() // build the mixer table make_mixer_table(5); + + m_vgm_idx = vgm_open(VGMC_K051649, m_mclock); } @@ -152,6 +155,8 @@ void k051649_device::sound_stream_update(sound_stream &stream, stream_sample_t * WRITE8_MEMBER( k051649_device::k051649_waveform_w ) { + vgm_write(m_vgm_idx, 0x00, offset, data); + // waveram is read-only? if (m_test & 0x40 || (m_test & 0x80 && offset >= 0x60)) return; @@ -187,6 +192,8 @@ READ8_MEMBER ( k051649_device::k051649_waveform_r ) WRITE8_MEMBER( k051649_device::k052539_waveform_w ) { + vgm_write(m_vgm_idx, 0x04, offset, data); + // waveram is read-only? if (m_test & 0x40) return; @@ -211,6 +218,7 @@ READ8_MEMBER ( k051649_device::k052539_waveform_r ) WRITE8_MEMBER( k051649_device::k051649_volume_w ) { m_stream->update(); + vgm_write(m_vgm_idx, 0x02, offset, data); m_channel_list[offset&0x7].volume=data&0xf; } @@ -218,6 +226,7 @@ WRITE8_MEMBER( k051649_device::k051649_volume_w ) WRITE8_MEMBER( k051649_device::k051649_frequency_w ) { int freq_hi = offset & 1; + vgm_write(m_vgm_idx, 0x01, offset, data); offset >>= 1; m_stream->update(); @@ -241,6 +250,8 @@ WRITE8_MEMBER( k051649_device::k051649_keyonoff_w ) int i; m_stream->update(); + vgm_write(m_vgm_idx, 0x03, offset, data); + for (i = 0; i < 5; i++) { m_channel_list[i].key=data&1; @@ -251,6 +262,7 @@ WRITE8_MEMBER( k051649_device::k051649_keyonoff_w ) WRITE8_MEMBER( k051649_device::k051649_test_w ) { + vgm_write(m_vgm_idx, 0x05, offset, data); m_test = data; } diff --git a/src/devices/sound/k051649.h b/src/devices/sound/k051649.h index ee4ffcbfb8e83..fe0169a1418cb 100644 --- a/src/devices/sound/k051649.h +++ b/src/devices/sound/k051649.h @@ -87,6 +87,8 @@ class k051649_device : public device_t, /* chip registers */ UINT8 m_test; + + UINT16 m_vgm_idx; /* VGM index */ }; extern const device_type K051649; diff --git a/src/devices/sound/k053260.c b/src/devices/sound/k053260.c index 8605400082902..a9118a32a5836 100644 --- a/src/devices/sound/k053260.c +++ b/src/devices/sound/k053260.c @@ -53,6 +53,7 @@ *********************************************************/ #include "emu.h" +#include "sound/vgmwrite.h" #include "k053260.h" #define LOG 0 @@ -97,6 +98,9 @@ void k053260_device::device_start() m_rom = ROM->base(); m_rom_size = ROM->bytes(); + m_vgm_idx = vgm_open(VGMC_K053260, clock()); + vgm_write_large_data(m_vgm_idx, 0x01, m_rom_size, 0x00, 0x00, m_rom); + m_stream = stream_alloc( 0, 2, clock() / CLOCKS_PER_SAMPLE ); /* register with the save state system */ @@ -169,6 +173,7 @@ READ8_MEMBER( k053260_device::read ) WRITE8_MEMBER( k053260_device::write ) { offset &= 0x3f; + vgm_write(m_vgm_idx, 0x00, offset, data); m_stream->update(); diff --git a/src/devices/sound/k053260.h b/src/devices/sound/k053260.h index 4a6e6353d08a1..2eafa6223d5b0 100644 --- a/src/devices/sound/k053260.h +++ b/src/devices/sound/k053260.h @@ -60,6 +60,7 @@ class k053260_device : public device_t, sound_stream * m_stream; UINT8 * m_rom; UINT32 m_rom_size; + UINT16 m_vgm_idx; // live state UINT8 m_portdata[4]; diff --git a/src/devices/sound/k054539.c b/src/devices/sound/k054539.c index 965ed5b9b589d..e98266407a291 100644 --- a/src/devices/sound/k054539.c +++ b/src/devices/sound/k054539.c @@ -10,6 +10,7 @@ *********************************************************/ #include "emu.h" +#include "sound/vgmwrite.h" #include "k054539.h" const device_type K054539 = &device_creator; @@ -67,6 +68,7 @@ k054539_device::k054539_device(const machine_config &mconfig, const char *tag, d void k054539_device::init_flags(int _flags) { + vgm_header_set(vgm_idx, 0x01, _flags); flags = _flags; } @@ -322,6 +324,10 @@ void k054539_device::init_chip() stream = stream_alloc(0, 2, clock() / 384); + vgm_idx = vgm_open(VGMC_K054539, clock()); + vgm_header_set(vgm_idx, 0x01, flags); + vgm_write_large_data(vgm_idx, 0x01, rom_size, 0x00, 0x00, rom); + save_item(NAME(regs)); save_pointer(NAME(ram), 0x4000); save_item(NAME(cur_ptr)); @@ -329,6 +335,8 @@ void k054539_device::init_chip() WRITE8_MEMBER(k054539_device::write) { + vgm_write(vgm_idx, 0x00, offset, data); + if(0) { int voice, reg; diff --git a/src/devices/sound/k054539.h b/src/devices/sound/k054539.h index d6fa2dc73a19a..58b97f9a56298 100644 --- a/src/devices/sound/k054539.h +++ b/src/devices/sound/k054539.h @@ -106,6 +106,8 @@ class k054539_device : public device_t, channel channels[8]; sound_stream *stream; + + UINT16 vgm_idx; emu_timer *m_timer; UINT32 m_timer_state; diff --git a/src/devices/sound/multipcm.c b/src/devices/sound/multipcm.c index 69c164bad2c5f..3c10fa19ab557 100644 --- a/src/devices/sound/multipcm.c +++ b/src/devices/sound/multipcm.c @@ -34,6 +34,7 @@ */ #include "emu.h" +#include "sound/vgmwrite.h" #include "multipcm.h" //???? @@ -358,6 +359,7 @@ READ8_MEMBER( multipcm_device::read ) WRITE8_MEMBER( multipcm_device::write ) { + vgm_write(m_vgm_idx, 0x00, offset, data); switch(offset) { case 0: //Data write @@ -377,6 +379,15 @@ WRITE8_MEMBER( multipcm_device::write ) void multipcm_device::set_bank(UINT32 leftoffs, UINT32 rightoffs) { + if (leftoffs == rightoffs) + { + vgm_write(m_vgm_idx, 0x01, leftoffs >> 16, 0x01 | 0x02); + } + else + { + vgm_write(m_vgm_idx, 0x01, leftoffs >> 16, 0x01); + vgm_write(m_vgm_idx, 0x01, rightoffs >> 16, 0x02); + } m_BankL = leftoffs; m_BankR = rightoffs; } @@ -550,6 +561,9 @@ void multipcm_device::device_start() m_Samples[i].AM=ptSample[11]; } + m_vgm_idx = vgm_open(VGMC_MULTIPCM, clock()); + //FIXME: vgm_write_large_data(m_vgm_idx, 0x01, region()->bytes(), 0x00, 0x00, *region()); + save_item(NAME(m_CurSlot)); save_item(NAME(m_Address)); save_item(NAME(m_BankL)); diff --git a/src/devices/sound/multipcm.h b/src/devices/sound/multipcm.h index 18854902ef1df..da44385b41eee 100644 --- a/src/devices/sound/multipcm.h +++ b/src/devices/sound/multipcm.h @@ -96,6 +96,7 @@ class multipcm_device : public device_t, //I include these in the chip because they depend on the chip clock unsigned int m_ARStep[0x40], m_DRStep[0x40]; //Envelope step table unsigned int m_FNS_Table[0x400]; //Frequency step table + UINT16 m_vgm_idx; // VGM index void EG_Calc(SLOT *slot); void LFO_ComputeStep(LFO_t *LFO,UINT32 LFOF,UINT32 LFOS,int ALFO); diff --git a/src/devices/sound/nes_apu.c b/src/devices/sound/nes_apu.c index cd2dfa8e4cbb5..8a3c12542b36f 100644 --- a/src/devices/sound/nes_apu.c +++ b/src/devices/sound/nes_apu.c @@ -47,6 +47,7 @@ *****************************************************************************/ #include "emu.h" +#include "sound/vgmwrite.h" #include "nes_apu.h" #include "cpu/m6502/n2a03.h" @@ -161,6 +162,8 @@ void nesapu_device::device_start() m_stream = machine().sound().stream_alloc(*this, 0, 1, rate); + m_vgm_idx = vgm_open(VGMC_NESAPU, clock()); + /* register for save */ for (int i = 0; i < 2; i++) { @@ -421,7 +424,7 @@ int8 nesapu_device::apu_noise(noise_t *chan) } /* RESET DPCM PARAMETERS */ -INLINE void apu_dpcmreset(dpcm_t *chan) +INLINE void apu_dpcmreset(UINT16 vgm_idx, dpcm_t *chan) { chan->address = 0xC000 + (uint16) (chan->regs[2] << 6); chan->length = (uint16) (chan->regs[3] << 4) + 1; @@ -429,6 +432,7 @@ INLINE void apu_dpcmreset(dpcm_t *chan) chan->irq_occurred = FALSE; chan->enabled = TRUE; /* Fixed * Proper DPCM channel ENABLE/DISABLE flag behaviour*/ chan->vol = 0; /* Fixed * DPCM DAC resets itself when restarted */ + vgm_write_large_data(vgm_idx, 0x01, 0x10000, chan->address, chan->length, chan->memory->get_read_ptr(0xC000)); } /* OUTPUT DPCM WAVE SAMPLE (VALUES FROM -64 to +63) */ @@ -457,7 +461,7 @@ int8 nesapu_device::apu_dpcm(dpcm_t *chan) chan->enabled = FALSE; /* Fixed * Proper DPCM channel ENABLE/DISABLE flag behaviour*/ chan->vol=0; /* Fixed * DPCM DAC resets itself when restarted */ if (chan->regs[0] & 0x40) - apu_dpcmreset(chan); + apu_dpcmreset(m_vgm_idx, chan); else { if (chan->regs[0] & 0x80) /* IRQ Generator */ @@ -626,7 +630,7 @@ inline void nesapu_device::apu_regwrite(int address, uint8 value) case APU_WRE2: m_APU.dpcm.regs[2] = value; - //apu_dpcmreset(m_APU.dpcm); + //apu_dpcmreset(m_vgm_idx, m_APU.dpcm); break; case APU_WRE3: @@ -682,7 +686,7 @@ inline void nesapu_device::apu_regwrite(int address, uint8 value) if (FALSE == m_APU.dpcm.enabled) { m_APU.dpcm.enabled = TRUE; - apu_dpcmreset(&m_APU.dpcm); + apu_dpcmreset(m_vgm_idx, &m_APU.dpcm); } } else @@ -735,6 +739,7 @@ inline uint8 nesapu_device::apu_read(int address) inline void nesapu_device::apu_write(int address, uint8 value) { m_APU.regs[address]=value; + vgm_write(m_vgm_idx, 0x00, address, value); m_stream->update(); apu_regwrite(address,value); } diff --git a/src/devices/sound/nes_apu.h b/src/devices/sound/nes_apu.h index 48823246e392a..d9f79cda85eac 100644 --- a/src/devices/sound/nes_apu.h +++ b/src/devices/sound/nes_apu.h @@ -73,6 +73,7 @@ class nesapu_device : public device_t, uint32 m_vbl_times[0x20]; /* VBL durations in samples */ uint32 m_sync_times1[SYNCS_MAX1]; /* Samples per sync table */ uint32 m_sync_times2[SYNCS_MAX2]; /* Samples per sync table */ + UINT16 m_vgm_idx; /* VGM index */ sound_stream *m_stream; const char *m_cpu_tag; diff --git a/src/devices/sound/okim6258.c b/src/devices/sound/okim6258.c index d26810e33d59e..020d3307069e4 100644 --- a/src/devices/sound/okim6258.c +++ b/src/devices/sound/okim6258.c @@ -12,6 +12,7 @@ #include "emu.h" +#include "sound/vgmwrite.h" #include "okim6258.h" #define COMMAND_STOP (1 << 0) @@ -116,10 +117,17 @@ void okim6258_device::device_start() m_divider = dividers[m_start_divider]; - m_stream = stream_alloc(0, 1, clock()/m_divider); + //m_stream = stream_alloc(0, 1, clock()/m_divider); + m_stream = stream_alloc(0, 2, clock()/m_divider); m_signal = -2; m_step = 0; + m_pan = 0x00; + + m_vgm_idx = vgm_open(VGMC_OKIM6258, m_master_clock); + vgm_header_set(m_vgm_idx, 0x01, m_divider); + vgm_header_set(m_vgm_idx, 0x02, m_adpcm_type); + vgm_header_set(m_vgm_idx, 0x03, m_output_bits); okim6258_state_save_register(); } @@ -136,6 +144,8 @@ void okim6258_device::device_reset() m_signal = -2; m_step = 0; m_status = 0; + + m_pan = 0x00; } @@ -145,9 +155,12 @@ void okim6258_device::device_reset() void okim6258_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) { - stream_sample_t *buffer = outputs[0]; + //stream_sample_t *buffer = outputs[0]; + stream_sample_t *bufL = outputs[0]; + stream_sample_t *bufR = outputs[1]; memset(outputs[0], 0, samples * sizeof(*outputs[0])); + memset(outputs[1], 0, samples * sizeof(*outputs[1])); if (m_status & STATUS_PLAYING) { @@ -163,7 +176,9 @@ void okim6258_device::sound_stream_update(sound_stream &stream, stream_sample_t nibble_shift ^= 4; - *buffer++ = sample; + //*buffer++ = sample; + *bufL++ = (m_pan & 0x02) ? 0 : sample; + *bufR++ = (m_pan & 0x01) ? 0 : sample; samples--; } @@ -174,7 +189,11 @@ void okim6258_device::sound_stream_update(sound_stream &stream, stream_sample_t { /* Fill with 0 */ while (samples--) - *buffer++ = 0; + { + //*buffer++ = 0; + *bufL++ = 0; + *bufR++ = 0; + } } } @@ -233,6 +252,8 @@ void okim6258_device::set_divider(int val) { int divider = dividers[val]; + vgm_write(m_vgm_idx, 0x00, 0x0C, val); + m_divider = dividers[val]; m_stream->set_sample_rate(m_master_clock / divider); } @@ -246,6 +267,11 @@ void okim6258_device::set_divider(int val) void okim6258_device::set_clock(int val) { + vgm_write(m_vgm_idx, 0x00, 0x08, (val >> 0) & 0xFF); + vgm_write(m_vgm_idx, 0x00, 0x09, (val >> 8) & 0xFF); + vgm_write(m_vgm_idx, 0x00, 0x0A, (val >> 16) & 0xFF); + vgm_write(m_vgm_idx, 0x00, 0x0B, (val >> 24) & 0xFF); + m_master_clock = val; m_stream->set_sample_rate(m_master_clock / m_divider); } @@ -287,6 +313,8 @@ WRITE8_MEMBER( okim6258_device::okim6258_data_w ) /* update the stream */ m_stream->update(); + vgm_write(m_vgm_idx, 0x00, 0x01, data); + m_data_in = data; m_nibble_shift = 0; } @@ -302,6 +330,8 @@ WRITE8_MEMBER( okim6258_device::okim6258_ctrl_w ) { m_stream->update(); + vgm_write(m_vgm_idx, 0x00, 0x00, data); + if (data & COMMAND_STOP) { m_status &= ~(STATUS_PLAYING | STATUS_RECORDING); @@ -335,3 +365,13 @@ WRITE8_MEMBER( okim6258_device::okim6258_ctrl_w ) m_status &= ~STATUS_RECORDING; } } + +WRITE8_MEMBER( okim6258_device::okim6258_pan_w ) +{ + /* update the stream */ + m_stream->update(); + + vgm_write(m_vgm_idx, 0x00, 0x02, data); + + m_pan = data; +} diff --git a/src/devices/sound/okim6258.h b/src/devices/sound/okim6258.h index c4cfed1c82c87..246f0ee8f7b9b 100644 --- a/src/devices/sound/okim6258.h +++ b/src/devices/sound/okim6258.h @@ -65,6 +65,7 @@ class okim6258_device : public device_t, DECLARE_READ8_MEMBER( okim6258_status_r ); DECLARE_WRITE8_MEMBER( okim6258_data_w ); DECLARE_WRITE8_MEMBER( okim6258_ctrl_w ); + DECLARE_WRITE8_MEMBER( okim6258_pan_w ); public: void set_divider(int val); @@ -89,8 +90,10 @@ class okim6258_device : public device_t, UINT8 m_output_bits; /* D/A precision is 10-bits but 12-bit data can be output serially to an external DAC */ + UINT8 m_pan; INT32 m_signal; INT32 m_step; + UINT16 m_vgm_idx; /* VGM index */ }; extern const device_type OKIM6258; diff --git a/src/devices/sound/okim6295.c b/src/devices/sound/okim6295.c index fc9f35e3a58c6..d8d662ecc77b2 100644 --- a/src/devices/sound/okim6295.c +++ b/src/devices/sound/okim6295.c @@ -38,6 +38,7 @@ ***************************************************************************/ #include "emu.h" +#include "sound/vgmwrite.h" #include "okim6295.h" @@ -126,6 +127,10 @@ void okim6295_device::device_start() int divisor = m_pin7_state ? 132 : 165; m_stream = machine().sound().stream_alloc(*this, 0, 1, clock() / divisor); + m_vgm_idx = vgm_open(VGMC_OKIM6295, clock()); + vgm_header_set(m_vgm_idx, 0x00, m_pin7_state); + vgm_write_large_data(m_vgm_idx, 0x01, m_region->bytes(), 0x00, 0x00, m_region->base()); + save_item(NAME(m_command)); save_item(NAME(m_bank_offs)); save_item(NAME(m_pin7_state)); @@ -174,6 +179,13 @@ void okim6295_device::device_post_load() void okim6295_device::device_clock_changed() { int divisor = m_pin7_state ? 132 : 165; + int val = clock() | (m_pin7_state << 31); + + vgm_write(m_vgm_idx, 0x00, 0x08, (val >> 0) & 0xFF); + vgm_write(m_vgm_idx, 0x00, 0x09, (val >> 8) & 0xFF); + vgm_write(m_vgm_idx, 0x00, 0x0A, (val >> 16) & 0xFF); + vgm_write(m_vgm_idx, 0x00, 0x0B, (val >> 24) & 0xFF); + m_stream->set_sample_rate(clock() / divisor); } @@ -218,6 +230,8 @@ void okim6295_device::set_bank_base(offs_t base, bool bDontUpdateStream) m_stream->update(); } + vgm_write(m_vgm_idx, 0x00, 0x0F, (base >> 18) & 0xFF); + // if we are setting a non-zero base, and we have no bank, allocate one if (!m_bank_installed && base != 0) { @@ -281,6 +295,8 @@ READ8_MEMBER( okim6295_device::read ) void okim6295_device::write_command(UINT8 command) { + vgm_write(m_vgm_idx, 0x00, 0x00, command); + // if a command is pending, process the second half if (m_command != -1) { diff --git a/src/devices/sound/okim6295.h b/src/devices/sound/okim6295.h index 33c8962767ad6..6b0eaaaec20e9 100644 --- a/src/devices/sound/okim6295.h +++ b/src/devices/sound/okim6295.h @@ -116,6 +116,8 @@ class okim6295_device : public device_t, direct_read_data * m_direct; static const UINT8 s_volume_table[16]; + + UINT16 m_vgm_idx; }; diff --git a/src/devices/sound/pokey.c b/src/devices/sound/pokey.c index 89279c1618488..c71d9bc2217db 100644 --- a/src/devices/sound/pokey.c +++ b/src/devices/sound/pokey.c @@ -58,6 +58,7 @@ *****************************************************************************/ #include "emu.h" +#include "sound/vgmwrite.h" #include "pokey.h" #include "debugger.h" @@ -346,6 +347,8 @@ void pokey_device::device_start() state_add(IRQEN_C, "IRQEN", m_IRQEN); state_add(SKCTL_C, "SKCTL", m_SKCTL); + m_vgm_idx = vgm_open(VGMC_POKEY, clock()); + // set our instruction counter m_icountptr = &m_icount; @@ -899,6 +902,8 @@ UINT8 pokey_device::read(offs_t offset) void pokey_device::write(offs_t offset, UINT8 data) { + vgm_write(m_vgm_idx, 0x00, offset, data); + synchronize(SYNC_WRITE, (offset<<8) | data); } diff --git a/src/devices/sound/pokey.h b/src/devices/sound/pokey.h index 96c9f6722468b..b1ae0bd753fde 100644 --- a/src/devices/sound/pokey.h +++ b/src/devices/sound/pokey.h @@ -372,6 +372,8 @@ class pokey_device : public device_t, UINT32 m_poly9[0x1ff]; UINT32 m_poly17[0x1ffff]; UINT32 m_voltab[0x10000]; + + UINT16 m_vgm_idx; /* VGM index */ }; diff --git a/src/devices/sound/qsound.c b/src/devices/sound/qsound.c index c519da454a5e2..bce4a1252e65e 100644 --- a/src/devices/sound/qsound.c +++ b/src/devices/sound/qsound.c @@ -24,6 +24,7 @@ ***************************************************************************/ #include "emu.h" +#include "sound/vgmwrite.h" #include "qsound.h" // device type definition @@ -120,6 +121,9 @@ void qsound_device::device_start() for (int adr = 0x80; adr < 0x90; adr++) write_data(adr, 0x120); + m_vgm_idx = vgm_open(VGMC_QSOUND, clock()); + vgm_write_large_data(m_vgm_idx, 0x01, m_sample_rom_length, 0x00, 0x00, m_sample_rom); + // state save for (int i = 0; i < 16; i++) { @@ -204,6 +208,7 @@ WRITE8_MEMBER(qsound_device::qsound_w) break; case 2: + vgm_write(m_vgm_idx, 0x00, m_data, data); m_stream->update(); write_data(data, m_data); break; diff --git a/src/devices/sound/qsound.h b/src/devices/sound/qsound.h index 208fdac41e990..a4847304f6248 100644 --- a/src/devices/sound/qsound.h +++ b/src/devices/sound/qsound.h @@ -70,6 +70,8 @@ class qsound_device : public device_t, int m_pan_table[33]; // pan volume table UINT16 m_data; // register latch data sound_stream *m_stream; // audio stream + UINT32 m_sample_rom_length; + UINT16 m_vgm_idx; // VGM index inline INT8 read_sample(UINT32 offset) { return m_sample_rom[offset & m_sample_rom.mask()]; } void write_data(UINT8 address, UINT16 data); diff --git a/src/devices/sound/rf5c68.c b/src/devices/sound/rf5c68.c index af99edd197a19..19a6295fdacc6 100644 --- a/src/devices/sound/rf5c68.c +++ b/src/devices/sound/rf5c68.c @@ -5,6 +5,7 @@ /*********************************************************/ #include "emu.h" +#include "sound/vgmwrite.h" #include "rf5c68.h" @@ -29,6 +30,7 @@ rf5c68_device::rf5c68_device(const machine_config &mconfig, const char *tag, dev m_enable(0) { memset(m_data, 0, sizeof(UINT8)*0x10000); + m_vgm_idx = vgm_open(VGMC_RF5C68, clock); } @@ -160,6 +162,8 @@ WRITE8_MEMBER( rf5c68_device::rf5c68_w ) /* force the stream to update first */ m_stream->update(); + vgm_write(m_vgm_idx, 0x00, offset & 0xFF, data); + /* switch off the address */ switch (offset) { @@ -229,5 +233,6 @@ READ8_MEMBER( rf5c68_device::rf5c68_mem_r ) WRITE8_MEMBER( rf5c68_device::rf5c68_mem_w ) { + vgm_write(m_vgm_idx, 0x01, offset & 0xFFFF, data); m_data[m_wbank * 0x1000 + offset] = data; } diff --git a/src/devices/sound/rf5c68.h b/src/devices/sound/rf5c68.h index 8dafb114f21a9..754a9f1ff79bc 100644 --- a/src/devices/sound/rf5c68.h +++ b/src/devices/sound/rf5c68.h @@ -86,11 +86,11 @@ class rf5c68_device : public device_t, UINT8 m_wbank; UINT8 m_enable; UINT8 m_data[0x10000]; + UINT16 m_vgm_idx; rf5c68_sample_end_cb_delegate m_sample_end_cb; }; extern const device_type RF5C68; - #endif /* __RF5C68_H__ */ diff --git a/src/devices/sound/scsp.c b/src/devices/sound/scsp.c index df2a5ef014313..d5a09db3ad1a7 100644 --- a/src/devices/sound/scsp.c +++ b/src/devices/sound/scsp.c @@ -32,7 +32,7 @@ #include "emu.h" #include "sound/cdda.h" #include "scsp.h" - +#include "sound/vgmwrite.h" #define ICLIP16(x) (x<-32768)?-32768:((x>32767)?32767:x) @@ -514,10 +514,15 @@ void scsp_device::init() m_Master=0; } + vgm_idx = vgm_open(VGMC_SCSP, clock() ? clock() : (44100*512)); + m_SCSPRAM = region()->base(); if (m_SCSPRAM) { m_SCSPRAM_LENGTH = region()->bytes(); + vgm_write_large_data(vgm_idx, 0x02, m_SCSPRAM_LENGTH, 0x00, 0x00, m_SCSPRAM); + if (m_roffset) + logerror("SCSP Ram Offset: %06X\n", m_roffset); m_DSP.SCSPRAM = (UINT16 *)m_SCSPRAM; m_DSP.SCSPRAM_LENGTH = m_SCSPRAM_LENGTH/2; m_SCSPRAM += m_roffset; @@ -722,14 +727,15 @@ void scsp_device::UpdateReg(address_space &space, int reg) case 0x19: if(m_Master) { - UINT32 time; + double time; m_TimPris[0]=1<<((m_udata.data[0x18/2]>>8)&0x7); m_TimCnt[0]=(m_udata.data[0x18/2]&0xff)<<8; if ((m_udata.data[0x18/2]&0xff) != 255) { - time = (44100 / m_TimPris[0]) / (255-(m_udata.data[0x18/2]&0xff)); + time = (44100.0 / m_TimPris[0]) / (255-(m_udata.data[0x18/2]&0xff)); + if (time) { m_timerA->adjust(attotime::from_hz(time)); @@ -741,14 +747,14 @@ void scsp_device::UpdateReg(address_space &space, int reg) case 0x1b: if(m_Master) { - UINT32 time; + double time; m_TimPris[1]=1<<((m_udata.data[0x1A/2]>>8)&0x7); m_TimCnt[1]=(m_udata.data[0x1A/2]&0xff)<<8; if ((m_udata.data[0x1A/2]&0xff) != 255) { - time = (44100 / m_TimPris[1]) / (255-(m_udata.data[0x1A/2]&0xff)); + time = (44100.0 / m_TimPris[1]) / (255-(m_udata.data[0x1A/2]&0xff)); if (time) { m_timerB->adjust(attotime::from_hz(time)); @@ -760,14 +766,14 @@ void scsp_device::UpdateReg(address_space &space, int reg) case 0x1D: if(m_Master) { - UINT32 time; + double time; m_TimPris[2]=1<<((m_udata.data[0x1C/2]>>8)&0x7); m_TimCnt[2]=(m_udata.data[0x1C/2]&0xff)<<8; if ((m_udata.data[0x1C/2]&0xff) != 255) { - time = (44100 / m_TimPris[2]) / (255-(m_udata.data[0x1C/2]&0xff)); + time = (44100.0 / m_TimPris[2]) / (255-(m_udata.data[0x1C/2]&0xff)); if (time) { m_timerC->adjust(attotime::from_hz(time)); @@ -1297,6 +1303,7 @@ void scsp_device::exec_dma(address_space &space) logerror("SCSP: DMA transfer START\n" "DMEA: %04x DRGA: %04x DTLG: %04x\n" "DGATE: %d DDIR: %d\n",m_dma.dmea,m_dma.drga,m_dma.dtlg,m_dma.dgate ? 1 : 0,m_dma.ddir ? 1 : 0); + vgm_write_large_data(vgm_idx, 0x01, m_SCSPRAM_LENGTH, m_dma.dmea, m_dma.dtlg, m_SCSPRAM + m_dma.dmea); /* Copy the dma values in a temp storage for resuming later */ /* (DMA *can't* overwrite its parameters). */ @@ -1408,11 +1415,51 @@ WRITE16_MEMBER( scsp_device::write ) m_stream->update(); + //logerror("SCSP write: Offset %04X, MemMask %04X Data %04X\n", offset, mem_mask, data); + if (mem_mask & 0xFF00) + vgm_write(vgm_idx, 0x00, (offset << 1) | 0x00, (data & 0xFF00) >> 8); + if (mem_mask & 0x00FF) + vgm_write(vgm_idx, 0x00, (offset << 1) | 0x01, (data & 0x00FF) >> 0); + tmp = r16(space, offset*2); COMBINE_DATA(&tmp); w16(space,offset*2, tmp); } +/*READ16_MEMBER( scsp_device::scsp_mem_r ) +{ + scsp_state *scsp = get_safe_token(device); + UINT16* ScspRam = (UINT16*)scsp->SCSPRAM; + + //logerror("Trying to read RAM Offset %06X, Mem Mask %04X\n", offset, mem_mask); + offset &= 0x3FFFF; + return ScspRam[offset] & mem_mask; +}*/ + +#define ADDR_HIGH(x) (((x) >> 16) & 0x07) +#define ADDR_LOW(x) ((x) & 0xFFFF) +WRITE16_MEMBER( scsp_device::scsp_mem_w ) +{ + UINT16* ScspRam = (UINT16*)m_SCSPRAM; + UINT16 tmp; + + offset &= 0x3FFFF; + tmp = ScspRam[offset]; + COMBINE_DATA(&tmp); + ScspRam[offset] = tmp; + + offset <<= 1; + if (offset >= 0x08000) // don't log the RAM areas used by the audio cpu + { + if (ACCESSING_BITS_8_15) + vgm_write(vgm_idx, 0x80 | ADDR_HIGH(offset), ADDR_LOW(offset) | 0x00, (data & 0xFF00) >> 8); + if (ACCESSING_BITS_0_7) + vgm_write(vgm_idx, 0x80 | ADDR_HIGH(offset), ADDR_LOW(offset) | 0x01, (data & 0x00FF) >> 0); + } + + return; +} + WRITE16_MEMBER( scsp_device::midi_in ) { // printf("scsp_midi_in: %02x\n", data); diff --git a/src/devices/sound/scsp.h b/src/devices/sound/scsp.h index bc104a85b6834..0541143440d9d 100644 --- a/src/devices/sound/scsp.h +++ b/src/devices/sound/scsp.h @@ -87,6 +87,9 @@ class scsp_device : public device_t, DECLARE_READ16_MEMBER( read ); DECLARE_WRITE16_MEMBER( write ); + //DECLARE_READ16_MEMBER( scsp_mem_r ); + DECLARE_WRITE16_MEMBER( scsp_mem_w ); + // MIDI I/O access (used for comms on Model 2/3) DECLARE_WRITE16_MEMBER( midi_in ); DECLARE_READ16_MEMBER( midi_out_r ); @@ -104,6 +107,7 @@ class scsp_device : public device_t, int m_roffset; /* offset in the region */ devcb_write8 m_irq_cb; /* irq callback */ devcb_write_line m_main_irq_cb; + UINT16 vgm_idx; /* VGM index */ union { diff --git a/src/devices/sound/segapcm.c b/src/devices/sound/segapcm.c index 50e875bc47445..bcd47ca7a1719 100644 --- a/src/devices/sound/segapcm.c +++ b/src/devices/sound/segapcm.c @@ -5,6 +5,7 @@ /*********************************************************/ #include "emu.h" +#include "sound/vgmwrite.h" #include "segapcm.h" @@ -53,6 +54,10 @@ void segapcm_device::device_start() m_stream = stream_alloc(0, 2, clock() / 128); + m_vgm_idx = vgm_open(VGMC_SEGAPCM, clock()); + vgm_header_set(m_vgm_idx, 0x01, m_bank); + //FIXME: vgm_write_large_data(m_vgm_idx, 0x01, len, 0x00, 0x00, m_rom); + save_item(NAME(m_low)); save_pointer(NAME(m_ram), 0x800); } @@ -140,6 +145,7 @@ void segapcm_device::sound_stream_update(sound_stream &stream, stream_sample_t * WRITE8_MEMBER( segapcm_device::sega_pcm_w ) { m_stream->update(); + vgm_write(m_vgm_idx, 0x00, offset & 0xFFFF, data); m_ram[offset & 0x07ff] = data; } diff --git a/src/devices/sound/segapcm.h b/src/devices/sound/segapcm.h index df7110bb7b2ce..dcbd52c8c0039 100644 --- a/src/devices/sound/segapcm.h +++ b/src/devices/sound/segapcm.h @@ -62,6 +62,7 @@ class segapcm_device : public device_t, int m_bankshift; int m_bankmask; sound_stream* m_stream; + UINT16 m_vgm_idx; }; extern const device_type SEGAPCM; diff --git a/src/devices/sound/sn76496.c b/src/devices/sound/sn76496.c index 2f65392174de5..aba30e6645574 100644 --- a/src/devices/sound/sn76496.c +++ b/src/devices/sound/sn76496.c @@ -125,6 +125,7 @@ ***************************************************************************/ #include "emu.h" +#include "sound/vgmwrite.h" #include "sn76496.h" #define MAX_OUTPUT 0x7fff @@ -190,6 +191,15 @@ void sn76496_base_device::device_start() m_sound = machine().sound().stream_alloc(*this, 0, (m_stereo? 2:1), sample_rate); + m_vgm_idx = vgm_open(VGMC_SN76496, clock()); + vgm_header_set(m_vgm_idx, 0x01, m_feedback_mask); + vgm_header_set(m_vgm_idx, 0x02, m_whitenoise_tap1); + vgm_header_set(m_vgm_idx, 0x03, m_whitenoise_tap2); + vgm_header_set(m_vgm_idx, 0x04, m_negate); + vgm_header_set(m_vgm_idx, 0x05, m_stereo); + vgm_header_set(m_vgm_idx, 0x06, m_clock_divider); +//FIXME: vgm_header_set(m_vgm_idx, 0x07, m_freq0_is_max); + for (i = 0; i < 4; i++) m_volume[i] = 0; m_last_register = m_sega_style_psg?3:0; // Sega VDP PSG defaults to selected period reg for 2nd channel @@ -242,6 +252,7 @@ void sn76496_base_device::device_start() WRITE8_MEMBER( sn76496_base_device::stereo_w ) { m_sound->update(); + vgm_write(m_vgm_idx, 0x01, data, 0x00); if (m_stereo) m_stereo_mask = data; else fatalerror("sn76496_base_device: Call to stereo write with mono chip!\n"); } @@ -257,6 +268,8 @@ void sn76496_base_device::write(UINT8 data) // 'sample', i.e. it equals the clock divider exactly m_cycles_to_ready = 1; + vgm_write(m_vgm_idx, 0x00, data, 0x00); + if (data & 0x80) { r = (data & 0x70) >> 4; diff --git a/src/devices/sound/sn76496.h b/src/devices/sound/sn76496.h index f374b4d1293c5..c091a7e1638a2 100644 --- a/src/devices/sound/sn76496.h +++ b/src/devices/sound/sn76496.h @@ -49,6 +49,7 @@ class sn76496_base_device : public device_t, public device_sound_interface devcb_write_line m_ready_handler; sound_stream* m_sound; + UINT16 m_vgm_idx; // VGM index const INT32 m_feedback_mask; // mask for feedback const INT32 m_whitenoise_tap1; // mask for white noise tap 1 (higher one, usually bit 14) diff --git a/src/devices/sound/t6w28.c b/src/devices/sound/t6w28.c index 25303ec4f1b37..ead220b373050 100644 --- a/src/devices/sound/t6w28.c +++ b/src/devices/sound/t6w28.c @@ -32,6 +32,7 @@ Offset 0: ***************************************************************************/ #include "emu.h" +#include "sound/vgmwrite.h" #include "t6w28.h" @@ -49,6 +50,8 @@ WRITE8_MEMBER( t6w28_device::write ) offset &= 1; + vgm_write(m_vgm_idx, offset, data, 0x00); + if (data & 0x80) { r = (data & 0x70) >> 4; @@ -112,7 +115,9 @@ void t6w28_device::sound_stream_update(sound_stream &stream, stream_sample_t **i /* If the volume is 0, increase the counter */ - for (i = 0;i < 8;i++) + /* Note by Valley Bell: This just introduces a bug, where a tone gets muted + when the left channel has volume 0.*/ + for (i = 0;0 && i < 8;i++) { if (m_volume[i] == 0) { @@ -330,6 +335,15 @@ void t6w28_device::device_start() m_whitenoise_taps = 0x06; m_whitenoise_invert = FALSE; + m_vgm_idx = vgm_open(VGMC_T6W28, clock()); + vgm_header_set(m_vgm_idx, 0x01, m_feedback_mask); + vgm_header_set(m_vgm_idx, 0x02, 0x02); // m_whitenoise_tap1 + vgm_header_set(m_vgm_idx, 0x03, 0x04); // m_whitenoise_tap2 + vgm_header_set(m_vgm_idx, 0x04, 0x00); // m_negate + vgm_header_set(m_vgm_idx, 0x05, 0x00); // m_stereo + vgm_header_set(m_vgm_idx, 0x06, 0x08); // m_clock_divider + vgm_header_set(m_vgm_idx, 0x07, 0x00); // m_freq0_is_max + save_item(NAME(m_register)); save_item(NAME(m_last_register)); save_item(NAME(m_volume)); diff --git a/src/devices/sound/t6w28.h b/src/devices/sound/t6w28.h index 7bee03bbc6bda..b49d13825bdc0 100644 --- a/src/devices/sound/t6w28.h +++ b/src/devices/sound/t6w28.h @@ -39,6 +39,7 @@ class t6w28_device : public device_t, INT32 m_count[8]; INT32 m_output[8]; bool m_enabled; + UINT16 m_vgm_idx; }; extern const device_type T6W28; diff --git a/src/devices/sound/upd7759.c b/src/devices/sound/upd7759.c index 4b7d4e13a18b4..5c11ec17bd262 100644 --- a/src/devices/sound/upd7759.c +++ b/src/devices/sound/upd7759.c @@ -124,6 +124,7 @@ *************************************************************/ #include "emu.h" +#include "sound/vgmwrite.h" #include "upd7759.h" @@ -254,6 +255,22 @@ void upd7759_device::device_start() /* toggle the reset line to finish the reset */ device_reset(); + m_vgm_idx = vgm_open(VGMC_UPD7759, clock()); + //vgm_header_set(m_vgm_idx, 0x01, m_sample_offset_shift); // set uPD7759/56 mode + if (m_rombase == NULL) + { + /* slave mode */ + if (m_vgm_idx != 0xFFFF) + logerror("Warning! Logging UPD7759 in Slave Mode!\n"); + vgm_header_set(m_vgm_idx, 0x00, 0x01); + } + else + { + /* master/standalone mode */ + vgm_header_set(m_vgm_idx, 0x00, 0x00); + vgm_write_large_data(m_vgm_idx, 0x01, region()->bytes(), 0x00, 0x00, m_rombase); + } + save_item(NAME(m_pos)); save_item(NAME(m_step)); @@ -327,6 +344,22 @@ void upd7756_device::device_start() /* toggle the reset line to finish the reset */ device_reset(); + m_vgm_idx = vgm_open(VGMC_UPD7759, clock()); + //vgm_header_set(m_vgm_idx, 0x01, m_sample_offset_shift); // set uPD7759/56 mode + if (m_rombase == NULL) + { + /* slave mode */ + if (m_vgm_idx != 0xFFFF) + logerror("Warning! Logging UPD7759 in Slave Mode!\n"); + vgm_header_set(m_vgm_idx, 0x00, 0x01); + } + else + { + /* master/standalone mode */ + vgm_header_set(m_vgm_idx, 0x00, 0x00); + vgm_write_large_data(m_vgm_idx, 0x01, region()->bytes(), 0x00, 0x00, m_rombase); + } + save_item(NAME(m_pos)); save_item(NAME(m_step)); @@ -738,6 +771,9 @@ WRITE_LINE_MEMBER( upd775x_device::reset_w ) /* update the stream first */ m_channel->update(); +//TODO: Not sure what to to here when dumping a VGM file: +//vgm_write(m_vgm_idx, 0x00, 0x00, data); //"data" is vestigial from the original patch against 0.152 (MAME0152_VGM_2014-07-04.diff) + /* on the falling edge, reset everything */ if (oldreset && !m_reset) device_reset(); @@ -754,6 +790,9 @@ WRITE_LINE_MEMBER( upd7759_device::start_w ) /* update the stream first */ m_channel->update(); +//TODO: Not sure what to to here when dumping a VGM file: +// vgm_write(m_vgm_idx, 0x00, 0x01, data); //"data" is vestigial from the original patch against 0.152 (MAME0152_VGM_2014-07-04.diff) + /* on the rising edge, if we're idle, start going, but not if we're held in reset */ if (m_state == STATE_IDLE && !oldstart && m_start && m_reset) { @@ -773,6 +812,9 @@ WRITE_LINE_MEMBER( upd7756_device::start_w ) logerror("upd7759_start_w: %d->%d\n", oldstart, m_start); +//TODO: Not sure what to to here when dumping a VGM file: +// vgm_write(m_vgm_idx, 0x00, 0x01, data); //"data" is vestigial from the original patch against 0.152 (MAME0152_VGM_2014-07-04.diff) + /* update the stream first */ m_channel->update(); @@ -786,6 +828,8 @@ WRITE_LINE_MEMBER( upd7756_device::start_w ) WRITE8_MEMBER( upd775x_device::port_w ) { + vgm_write(m_vgm_idx, 0x00, 0x02, data); + /* update the FIFO value */ m_fifo_in = data; } @@ -803,6 +847,8 @@ void upd775x_device::set_bank_base(UINT32 base) assert(m_rombase != NULL); m_rom = m_rombase + base; m_romoffset = base; + + vgm_write(m_vgm_idx, 0x00, 0x03, base / 0x20000); } //------------------------------------------------- diff --git a/src/devices/sound/upd7759.h b/src/devices/sound/upd7759.h index 94286e7cd571d..6578d37bacccc 100644 --- a/src/devices/sound/upd7759.h +++ b/src/devices/sound/upd7759.h @@ -102,6 +102,8 @@ class upd775x_device : public device_t, devcb_write_line m_drqcallback; + UINT16 m_vgm_idx; /* VGM index */ + void update_adpcm(int data); virtual void advance_state(); }; diff --git a/src/devices/sound/ym2151.c b/src/devices/sound/ym2151.c index 804cbc5a6fd1b..f51436a3d417f 100644 --- a/src/devices/sound/ym2151.c +++ b/src/devices/sound/ym2151.c @@ -7,6 +7,7 @@ ******************************************************************************/ #include "emu.h" +#include "sound/vgmwrite.h" #include "ym2151.h" @@ -170,6 +171,8 @@ struct YM2151 device_t *device; unsigned int clock; /* chip clock in Hz (passed from 2151intf.c) */ unsigned int sampfreq; /* sampling frequency in Hz (passed from 2151intf.c) */ + + UINT16 vgm_idx; /* VGM index */ }; @@ -1060,6 +1063,7 @@ void ym2151_write_reg(void *_chip, int r, int v) fputc( (unsigned char)v, cymfile ); } + vgm_write(chip->vgm_idx, 0x00, r, v); switch(r & 0xe0) { @@ -1551,6 +1555,7 @@ void * ym2151_init(device_t *device, int clock, int rate) logerror("Could not create file 2151_.cym\n"); } + PSG->vgm_idx = vgm_open(VGMC_YM2151, clock); return PSG; } diff --git a/src/emu/emuopts.c b/src/emu/emuopts.c index be8efdd3a8c13..2a66ecf82af08 100644 --- a/src/emu/emuopts.c +++ b/src/emu/emuopts.c @@ -65,6 +65,7 @@ const options_entry emu_options::s_option_entries[] = { OPTION_DUMMYWRITE, "0", OPTION_BOOLEAN, "indicates if a snapshot should be created if each frame" }, #endif { OPTION_WAVWRITE, NULL, OPTION_STRING, "optional filename to write a WAV file of the current session" }, + { OPTION_VGMWRITE, "0", OPTION_INTEGER, "enable to write a VGM of the current session (name is based on romname)" }, { OPTION_SNAPNAME, "%g/%i", OPTION_STRING, "override of the default snapshot/movie naming; %g == gamename, %i == index" }, { OPTION_SNAPSIZE, "auto", OPTION_STRING, "specify snapshot/movie resolution (x) or 'auto' to use minimal size " }, { OPTION_SNAPVIEW, "internal", OPTION_STRING, "specify snapshot/movie view or 'internal' to use internal pixel-aspect views" }, diff --git a/src/emu/emuopts.h b/src/emu/emuopts.h index 45a4dab160f91..60ea8e7eb473e 100644 --- a/src/emu/emuopts.h +++ b/src/emu/emuopts.h @@ -77,6 +77,7 @@ enum #define OPTION_DUMMYWRITE "dummywrite" #endif #define OPTION_WAVWRITE "wavwrite" +#define OPTION_VGMWRITE "vgmwrite" #define OPTION_SNAPNAME "snapname" #define OPTION_SNAPSIZE "snapsize" #define OPTION_SNAPVIEW "snapview" @@ -254,6 +255,7 @@ class emu_options : public core_options bool dummy_write() const { return bool_value(OPTION_DUMMYWRITE); } #endif const char *wav_write() const { return value(OPTION_WAVWRITE); } + const int vgm_write() const { return int_value(OPTION_VGMWRITE); } const char *snap_name() const { return value(OPTION_SNAPNAME); } const char *snap_size() const { return value(OPTION_SNAPSIZE); } const char *snap_view() const { return value(OPTION_SNAPVIEW); } diff --git a/src/emu/machine.c b/src/emu/machine.c index 3f0eb5a9c15b7..4f18cc4c16cb6 100644 --- a/src/emu/machine.c +++ b/src/emu/machine.c @@ -82,6 +82,7 @@ #include "unzip.h" #include "debug/debugcon.h" #include "debug/debugvw.h" +#include "sound/vgmwrite.h" #include @@ -282,6 +283,9 @@ void running_machine::start() // so this location in the init order is important ui().set_startup_text("Initializing...", true); + // call the Initialisation for VGM logging (MUST be called before driver init) + vgm_start(*this); + // register callbacks for the devices, then start them add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(FUNC(running_machine::reset_all_devices), this)); add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(running_machine::stop_all_devices), this)); @@ -1152,6 +1156,9 @@ void running_machine::stop_all_devices() device_iterator iter(root_device()); for (device_t *device = iter.first(); device != NULL; device = iter.next()) device->stop(); + + // stop VGM logging + vgm_stop(); } diff --git a/src/emu/sound/vgmwrite.c b/src/emu/sound/vgmwrite.c new file mode 100644 index 0000000000000..73b6df8ba279c --- /dev/null +++ b/src/emu/sound/vgmwrite.c @@ -0,0 +1,2179 @@ +/* + vgmwrite.c + + VGM output module + + Written by Valley Bell + +*/ + +#include "emu.h" +#include "emuopts.h" +#include "vgmwrite.h" +#include + +#define DYNAMIC_HEADER_SIZE + +static UINT8 LOG_VGM_FILE = 0x00; + +typedef struct _vgm_file_header VGM_HEADER; +struct _vgm_file_header +{ + UINT32 fccVGM; + UINT32 lngEOFOffset; + UINT32 lngVersion; + UINT32 lngHzPSG; + UINT32 lngHz2413; + UINT32 lngGD3Offset; + UINT32 lngTotalSamples; + UINT32 lngLoopOffset; + UINT32 lngLoopSamples; + UINT32 lngRate; + UINT16 shtPSG_Feedback; + UINT8 bytPSG_SRWidth; + UINT8 bytPSG_Flags; + UINT32 lngHz2612; + UINT32 lngHz2151; + UINT32 lngDataOffset; + UINT32 lngHzSPCM; + UINT32 lngSPCMIntf; + UINT32 lngHzRF5C68; + UINT32 lngHz2203; + UINT32 lngHz2608; + UINT32 lngHz2610; + UINT32 lngHz3812; + UINT32 lngHz3526; + UINT32 lngHz8950; + UINT32 lngHz262; + UINT32 lngHz278B; + UINT32 lngHz271; + UINT32 lngHz280B; + UINT32 lngHzRF5C164; + UINT32 lngHzPWM; + UINT32 lngHzAY8910; + UINT8 lngAYType; + UINT8 lngAYFlags; + UINT8 lngAYFlagsYM2203; + UINT8 lngAYFlagsYM2608; + UINT8 bytModifiers[0x04]; + UINT32 lngHzGBDMG; // part of the LR35902 (GB Main CPU) + UINT32 lngHzNESAPU; // part of the N2A03 (NES Main CPU) + UINT32 lngHzMultiPCM; + UINT32 lngHzUPD7759; + UINT32 lngHzOKIM6258; + UINT8 bytOKI6258Flags; + UINT8 bytK054539Flags; + UINT8 bytC140Type; + UINT8 bytReservedFlags; + UINT32 lngHzOKIM6295; + UINT32 lngHzK051649; + UINT32 lngHzK054539; + UINT32 lngHzHuC6280; + UINT32 lngHzC140; + UINT32 lngHzK053260; + UINT32 lngHzPokey; + UINT32 lngHzQSound; + UINT32 lngHzSCSP; + //UINT32 lngHzOKIM6376; + UINT32 lngExtraOfs; + UINT8 bytReserved[0x10]; +}; // -> 0xD0 Bytes +typedef struct _vgm_gd3_tag GD3_TAG; +struct _vgm_gd3_tag +{ + UINT32 fccGD3; + UINT32 lngVersion; + UINT32 lngTagLength; + wchar_t strTrackNameE[0x70]; + wchar_t strTrackNameJ[0x10]; // Japanese Names are not used + wchar_t strGameNameE[0x70]; + wchar_t strGameNameJ[0x10]; + wchar_t strSystemNameE[0x30]; + wchar_t strSystemNameJ[0x10]; + wchar_t strAuthorNameE[0x30]; + wchar_t strAuthorNameJ[0x10]; + wchar_t strReleaseDate[0x10]; + wchar_t strCreator[0x20]; + wchar_t strNotes[0x50]; +}; // -> 0x200 Bytes + +typedef struct _vgm_rom_data_block VGM_ROM_DATA; +struct _vgm_rom_data_block +{ + UINT8 Type; + UINT32 DataSize; + const void* Data; +}; +typedef struct _vgm_rom_init_command VGM_INIT_CMD; +struct _vgm_rom_init_command +{ + UINT8 CmdLen; + UINT8 Data[0x08]; +}; +typedef struct _vgm_file_inf VGM_INF; +struct _vgm_file_inf +{ + FILE* hFile; + VGM_HEADER Header; + UINT8 WroteHeader; + UINT32 HeaderBytes; + UINT32 BytesWrt; + UINT32 SmplsWrt; + UINT32 EvtDelay; + + UINT32 DataCount; + VGM_ROM_DATA DataBlk[0x20]; + //UINT32 CmdAlloc; + UINT32 CmdCount; + VGM_INIT_CMD Commands[0x100]; + + UINT8 NesMemEmpty; + UINT8 NesMem[0x4000]; +}; +typedef struct _vgm_chip VGM_CHIP; +typedef struct _vgm_chip_pcmcache VGM_PCMCACHE; +struct _vgm_chip +{ + UINT16 VgmID; + UINT8 ChipType; + UINT8 HadWrite; + VGM_PCMCACHE* PCMCache; +}; +struct _vgm_chip_pcmcache +{ + UINT32 Start; + UINT32 Next; + UINT32 Pos; + UINT32 CacheSize; + UINT8* CacheData; +}; + + +#define MAX_VGM_FILES 0x10 +#define MAX_VGM_CHIPS 0x80 +static char vgm_namebase[0x80]; +static VGM_INF VgmFile[MAX_VGM_FILES]; +static VGM_CHIP VgmChip[MAX_VGM_CHIPS]; +static VGM_PCMCACHE VgmPCache[MAX_VGM_CHIPS]; +static GD3_TAG VgmTag; + +// Function Prototypes +INLINE int atwcpy(wchar_t* dststr, const char* srcstr); +static TIMER_CALLBACK(vgmfile_callback); +static void vgm_header_postwrite(UINT16 vgm_id); +static void vgm_header_sizecheck(UINT16 vgm_id, UINT32 MinVer, UINT32 MinSize); +static void vgm_header_clear(UINT16 vgm_id); +static void vgm_setup_pcmcache(VGM_PCMCACHE* TempPC, UINT32 Size); +static void vgm_close(UINT16 vgm_id); +static void vgm_write_delay(UINT16 vgm_id); +static UINT8 vgm_nes_ram_check(VGM_INF* VI, UINT32 datasize, UINT32* value1, UINT32* value2, const UINT8* data); +static void vgm_flush_pcm(VGM_CHIP* VC); + + +// ASCII to Wide-Char String Copy +INLINE int atwcpy(wchar_t* dststr, const char* srcstr) +{ + return mbstowcs(dststr, srcstr, strlen(srcstr) + 0x01); +} + +void vgm_start(running_machine &machine) +{ + UINT16 curvgm; + const game_driver* gamedrv; +#ifdef MESS + device_image_interface* devimg; + bool gotimg; +#endif + + LOG_VGM_FILE = (UINT8)machine.options().vgm_write(); + logerror("VGM logging mode: %02X\n", LOG_VGM_FILE); + + // Reset all files + for (curvgm = 0x00; curvgm < MAX_VGM_FILES; curvgm ++) + { + VgmFile[curvgm].hFile = NULL; + VgmFile[curvgm].DataCount = 0x00; + VgmFile[curvgm].CmdCount = 0x00; + VgmFile[curvgm].NesMemEmpty = 0x01; + } + for (curvgm = 0x00; curvgm < MAX_VGM_CHIPS; curvgm ++) + { + VgmChip[curvgm].ChipType = 0xFF; + VgmPCache[curvgm].CacheSize = 0x00; + VgmPCache[curvgm].CacheData = NULL; + } + + // start the timer + // (done here because it makes save states with vgmwrite off compatible with + // saves that have it on) + machine.scheduler().timer_pulse(attotime::from_hz(44100), FUNC(vgmfile_callback)); + //44.1 KHz VGM pulse timer + + if (! LOG_VGM_FILE) + return; + + // Get the Game Information and write the GD3 Tag + gamedrv = &machine.system(); +#ifdef MESS + devimg = NULL; + gotimg = machine.devicelist().first(devimg); +#endif + + if (gamedrv) + strcpy(vgm_namebase, gamedrv->name); + else + strcpy(vgm_namebase, "vgmlog"); + strcat(vgm_namebase, "_"); + + VgmTag.fccGD3 = 0x20336447; // 'Gd3 ' + VgmTag.lngVersion = 0x00000100; +#ifndef MESS + wcscpy(VgmTag.strTrackNameE, L""); + wcscpy(VgmTag.strTrackNameJ, L""); + if (gamedrv) + atwcpy(VgmTag.strGameNameE, gamedrv->description); + else + wcscpy(VgmTag.strGameNameE, L""); + wcscpy(VgmTag.strGameNameJ, L""); + wcscpy(VgmTag.strSystemNameE, L"Arcade Machine"); + wcscpy(VgmTag.strSystemNameJ, L""); + if (gamedrv) + atwcpy(VgmTag.strAuthorNameE, gamedrv->manufacturer); + else + wcscpy(VgmTag.strAuthorNameE, L""); + wcscpy(VgmTag.strAuthorNameJ, L""); + if (gamedrv) + atwcpy(VgmTag.strReleaseDate, gamedrv->year); + else + wcscpy(VgmTag.strReleaseDate, L""); +#else //#ifdef MESS + wcscpy(VgmTag.strTrackNameE, L""); + wcscpy(VgmTag.strTrackNameJ, L""); + if (gotimg) + { + if (strlen(devimg->longname())) + atwcpy(VgmTag.strGameNameE, devimg->longname()); + else + atwcpy(VgmTag.strGameNameE, devimg->basename_noext()); + } + else + { + wcscpy(VgmTag.strGameNameE, L""); + } + wcscpy(VgmTag.strGameNameJ, L""); + if (gamedrv) + atwcpy(VgmTag.strSystemNameE, gamedrv->description); + else + wcscpy(VgmTag.strSystemNameE, L""); + wcscpy(VgmTag.strSystemNameJ, L""); + if (gotimg) + atwcpy(VgmTag.strAuthorNameE, devimg->manufacturer()); + else + wcscpy(VgmTag.strAuthorNameE, L""); + wcscpy(VgmTag.strAuthorNameJ, L""); + if (gotimg) + atwcpy(VgmTag.strReleaseDate, devimg->year()); + else + wcscpy(VgmTag.strReleaseDate, L""); +#endif + wcscpy(VgmTag.strCreator, L""); + + swprintf(VgmTag.strNotes, sizeof VgmTag.strNotes, L"Generated by %hs %hs", emulator_info::get_appname(), build_version); + VgmTag.lngTagLength = wcslen(VgmTag.strTrackNameE) + 0x01 + + wcslen(VgmTag.strTrackNameJ) + 0x01 + + wcslen(VgmTag.strGameNameE) + 0x01 + + wcslen(VgmTag.strGameNameJ) + 0x01 + + wcslen(VgmTag.strSystemNameE) + 0x01 + + wcslen(VgmTag.strSystemNameJ) + 0x01 + + wcslen(VgmTag.strAuthorNameE) + 0x01 + + wcslen(VgmTag.strAuthorNameJ) + 0x01 + + wcslen(VgmTag.strReleaseDate) + 0x01 + + wcslen(VgmTag.strCreator) + 0x01 + + wcslen(VgmTag.strNotes) + 0x01; + VgmTag.lngTagLength *= sizeof(wchar_t); // String Length -> Byte Length + + logerror("VGM logging started ...\n"); + + return; +} + +void vgm_stop(void) +{ + UINT16 curchip; + UINT16 chip_unused; + UINT16 curvgm; + UINT32 clock_mask; + VGM_HEADER* VH; + VGM_CHIP* VC; + + if (! LOG_VGM_FILE) + return; + + chip_unused = 0x00; + for (curchip = 0x00; curchip < MAX_VGM_CHIPS; curchip ++) + { + VC = &VgmChip[curchip]; + if (VC->ChipType == 0xFF) + break; + + if (! VC->HadWrite) + { + chip_unused ++; + curvgm = VC->VgmID; + VH = &VgmFile[curvgm].Header; + // clock_mask - remove either the dual-chip bit or the entire clock + clock_mask = (VC->ChipType & 0x80) ? ~0x40000000 : 0x00000000; + + switch(VC->ChipType & 0x7F) + { + case VGMC_SN76496: + VH->lngHzPSG &= clock_mask; + if (! clock_mask) + { + VH->shtPSG_Feedback = 0x0000; + VH->bytPSG_SRWidth = 0x00; + VH->bytPSG_Flags = 0x00; + } + break; + case VGMC_YM2413: + VH->lngHz2413 &= clock_mask; + break; + case VGMC_YM2612: + VH->lngHz2612 &= clock_mask; + break; + case VGMC_YM2151: + VH->lngHz2151 &= clock_mask; + break; + case VGMC_SEGAPCM: + VH->lngHzSPCM &= clock_mask; + break; + case VGMC_RF5C68: + VH->lngHzRF5C68 &= clock_mask; + break; + case VGMC_YM2203: + VH->lngHz2203 &= clock_mask; + if (! clock_mask) + VH->lngAYFlagsYM2203 = 0x00; + break; + case VGMC_YM2608: + VH->lngHz2608 &= clock_mask; + if (! clock_mask) + VH->lngAYFlagsYM2608 = 0x00; + break; + case VGMC_YM2610: + VH->lngHz2610 &= clock_mask; + break; + case VGMC_YM3812: + VH->lngHz3812 &= clock_mask; + break; + case VGMC_YM3526: + VH->lngHz3526 &= clock_mask; + break; + case VGMC_Y8950: + VH->lngHz8950 &= clock_mask; + break; + case VGMC_YMF262: + VH->lngHz262 &= clock_mask; + break; + case VGMC_YMF278B: + VH->lngHz278B &= clock_mask; + break; + case VGMC_YMF271: + VH->lngHz271 &= clock_mask; + break; + case VGMC_YMZ280B: + VH->lngHz280B &= clock_mask; + break; + case VGMC_T6W28: + clock_mask = 0x00000000; + VH->lngHzPSG &= clock_mask; + if (! clock_mask) + { + VH->shtPSG_Feedback = 0x0000; + VH->bytPSG_SRWidth = 0x00; + VH->bytPSG_Flags = 0x00; + } + break; + case VGMC_RF5C164: + VH->lngHzRF5C164 &= clock_mask; + break; + case VGMC_PWM: + VH->lngHzPWM &= clock_mask; + break; + case VGMC_AY8910: + VH->lngHzAY8910 &= clock_mask; + if (! clock_mask) + { + VH->lngAYFlags = 0x00; + VH->lngAYType = 0x00; + } + break; + case VGMC_GBSOUND: + VH->lngHzGBDMG &= clock_mask; + break; + case VGMC_NESAPU: + VH->lngHzNESAPU &= clock_mask; + break; + case VGMC_MULTIPCM: + VH->lngHzMultiPCM &= clock_mask; + break; + case VGMC_UPD7759: + VH->lngHzUPD7759 &= clock_mask; + break; + case VGMC_OKIM6258: + VH->lngHzOKIM6258 &= clock_mask; + if (! clock_mask) + VH->bytOKI6258Flags = 0x00; + break; + case VGMC_OKIM6295: + VH->lngHzOKIM6295 &= clock_mask; + break; + case VGMC_K051649: + VH->lngHzK051649 &= clock_mask; + break; + case VGMC_K054539: + VH->lngHzK054539 &= clock_mask; + if (! clock_mask) + VH->bytK054539Flags = 0x00; + break; + case VGMC_C6280: + VH->lngHzHuC6280 &= clock_mask; + break; + case VGMC_C140: + VH->lngHzC140 &= clock_mask; + if (! clock_mask) + VH->bytC140Type = 0x00; + break; + case VGMC_K053260: + VH->lngHzK053260 &= clock_mask; + break; + case VGMC_POKEY: + VH->lngHzPokey &= clock_mask; + break; + case VGMC_QSOUND: + VH->lngHzQSound &= clock_mask; + break; + case VGMC_SCSP: + VH->lngHzSCSP &= clock_mask; + break; + // case VGMC_OKIM6376: + // VH->lngHzOKIM6376 &= clock_mask; + // break; + } + } + if (VC->PCMCache != NULL) + { + VC->PCMCache->CacheSize = 0x00; + free(VC->PCMCache->CacheData); + VC->PCMCache->CacheData = NULL; + VC->PCMCache = NULL; + } + } + if (chip_unused) + logerror("Header Data of %hu unused Chips removed.\n", chip_unused); + + for (curvgm = 0x00; curvgm < MAX_VGM_FILES; curvgm ++) + { + if (VgmFile[curvgm].hFile != NULL) + vgm_close(curvgm); + } + logerror("VGM stopped.\n"); + + return; +} + +static TIMER_CALLBACK(vgmfile_callback) +{ + UINT16 curvgm; + + if (! LOG_VGM_FILE) + return; + + for (curvgm = 0x00; curvgm < MAX_VGM_FILES; curvgm ++) + { + if (VgmFile[curvgm].hFile != NULL) + VgmFile[curvgm].EvtDelay ++; + } + + return; +} + +static void vgm_header_postwrite(UINT16 vgm_id) +{ + VGM_INF* VI; + VGM_HEADER* Header; + VGM_ROM_DATA* VR; + VGM_INIT_CMD* VC; + UINT32 curcmd; + UINT32 blocksize; + UINT32 templng; + + if (VgmFile[vgm_id].WroteHeader) + return; + + VI = &VgmFile[vgm_id]; + Header = &VI->Header; + + fseek(VI->hFile, 0x00, SEEK_SET); + VI->BytesWrt = 0x00; + + fwrite(Header, 0x01, VI->HeaderBytes, VI->hFile); + VI->BytesWrt += VI->HeaderBytes; + + for (curcmd = 0x00; curcmd < VI->DataCount; curcmd ++) + { + VR = &VI->DataBlk[curcmd]; + blocksize = 0x08; + if (VR->Data != NULL) + blocksize += VR->DataSize; + VR->DataSize &= 0x7FFFFFFF; + + fputc(0x67, VI->hFile); + fputc(0x66, VI->hFile); + fputc(VR->Type, VI->hFile); + fwrite(&blocksize, 0x04, 0x01, VI->hFile); // Data Block Size + fwrite(&VR->DataSize, 0x04, 0x01, VI->hFile); // ROM Size + templng = 0x00; + fwrite(&templng, 0x04, 0x01, VI->hFile); // Data Base Address + if (VR->Data != NULL) + fwrite(VR->Data, 0x01, VR->DataSize, VI->hFile); + VI->BytesWrt += 0x07 + (blocksize & 0x7FFFFFFF); + } + for (curcmd = 0x00; curcmd < VI->CmdCount; curcmd ++) + { + VC = &VI->Commands[curcmd]; + fwrite(VC->Data, 0x01, VC->CmdLen, VI->hFile); + VI->BytesWrt += VC->CmdLen; + } + VI->WroteHeader = 0x01; + + return; +} + +static void vgm_header_sizecheck(UINT16 vgm_id, UINT32 MinVer, UINT32 MinSize) +{ + VGM_INF* VI; + VGM_HEADER* Header; + + if (VgmFile[vgm_id].hFile == NULL) + return; + + VI = &VgmFile[vgm_id]; + Header = &VI->Header; + + if (Header->lngVersion < MinVer) + Header->lngVersion = MinVer; + if (VI->HeaderBytes < MinSize) + VI->HeaderBytes = MinSize; + + return; +} + +static void vgm_header_clear(UINT16 vgm_id) +{ + VGM_INF* VI; + VGM_HEADER* Header; + + if (VgmFile[vgm_id].hFile == NULL) + return; + + VI = &VgmFile[vgm_id]; + Header = &VI->Header; + memset(Header, 0x00, sizeof(VGM_HEADER)); + Header->fccVGM = 0x206D6756; // 'Vgm ' + Header->lngEOFOffset = 0x00000000; + Header->lngVersion = 0x00000151; + //Header->lngGD3Offset = 0x00000000; + //Header->lngTotalSamples = 0; + //Header->lngLoopOffset = 0x00000000; + //Header->lngLoopSamples = 0; +#ifdef DYNAMIC_HEADER_SIZE + VI->HeaderBytes = 0x38; + VI->WroteHeader = 0x00; +#else + VI->HeaderBytes = sizeof(VGM_HEADER); + VI->WroteHeader = 0x01; +#endif + Header->lngDataOffset = VI->HeaderBytes - 0x34; + + //fseek(VI->hFile, 0x00, SEEK_SET); + //fwrite(Header, 0x01, sizeof(VGM_HEADER), VI->hFile); + //VI->BytesWrt += sizeof(VGM_HEADER); + VI->BytesWrt = 0x00; + + return; +} + +UINT16 vgm_open(UINT8 chip_type, int clock) +{ + UINT16 chip_id; + UINT16 chip_file; + UINT16 curvgm; + UINT32 chip_val; + char vgm_name[0x20]; + UINT8 use_two; + + logerror("vgm_open - Chip Type %02X, Clock %u\n", chip_type, clock); + if (! LOG_VGM_FILE || chip_type == 0xFF) + return 0xFFFF; + + chip_id = 0xFFFF; + for (curvgm = 0x00; curvgm < MAX_VGM_CHIPS; curvgm ++) + { + if (VgmChip[curvgm].ChipType == 0xFF) + { + chip_id = curvgm; + break; + } + } + if (chip_id == 0xFFFF) + return 0xFFFF; + + if (LOG_VGM_FILE != 0xDD) + { + // prevent it from logging chips with known audible errors in VGM logs + if (chip_type == VGMC_SCSP) + return 0xFFFF; // streaming samples to the SCSP doesn't work correctly since 0.149 + } + + chip_file = 0xFFFF; + use_two = 0x00; + for (curvgm = 0x00; curvgm < MAX_VGM_FILES; curvgm ++) + { + if (VgmFile[curvgm].hFile != NULL) + { + use_two = 0x01; + switch(chip_type) + { + case VGMC_SN76496: + chip_val = VgmFile[curvgm].Header.lngHzPSG; + break; + case VGMC_YM2413: + chip_val = VgmFile[curvgm].Header.lngHz2413; + break; + case VGMC_YM2612: + chip_val = VgmFile[curvgm].Header.lngHz2612; + break; + case VGMC_YM2151: + chip_val = VgmFile[curvgm].Header.lngHz2151; + break; + case VGMC_SEGAPCM: + chip_val = VgmFile[curvgm].Header.lngHzSPCM; + break; + case VGMC_RF5C68: + chip_val = VgmFile[curvgm].Header.lngHzRF5C68; + use_two = 0x00; + break; + case VGMC_YM2203: + chip_val = VgmFile[curvgm].Header.lngHz2203; + break; + case VGMC_YM2608: + chip_val = VgmFile[curvgm].Header.lngHz2608; + break; + case VGMC_YM2610: + chip_val = VgmFile[curvgm].Header.lngHz2610; + break; + case VGMC_YM3812: + chip_val = VgmFile[curvgm].Header.lngHz3812; + break; + case VGMC_YM3526: + chip_val = VgmFile[curvgm].Header.lngHz3526; + break; + case VGMC_Y8950: + chip_val = VgmFile[curvgm].Header.lngHz8950; + break; + case VGMC_YMF262: + chip_val = VgmFile[curvgm].Header.lngHz262; + break; + case VGMC_YMF278B: + chip_val = VgmFile[curvgm].Header.lngHz278B; + break; + case VGMC_YMF271: + chip_val = VgmFile[curvgm].Header.lngHz271; + break; + case VGMC_YMZ280B: + chip_val = VgmFile[curvgm].Header.lngHz280B; + break; + case VGMC_T6W28: + chip_val = VgmFile[curvgm].Header.lngHzPSG; + use_two = 0x00; + break; + case VGMC_RF5C164: + chip_val = VgmFile[curvgm].Header.lngHzRF5C164; + use_two = 0x00; + break; + case VGMC_PWM: + chip_val = VgmFile[curvgm].Header.lngHzPWM; + use_two = 0x00; + break; + case VGMC_AY8910: + chip_val = VgmFile[curvgm].Header.lngHzAY8910; + break; + case VGMC_GBSOUND: + chip_val = VgmFile[curvgm].Header.lngHzGBDMG; + break; + case VGMC_NESAPU: + chip_val = VgmFile[curvgm].Header.lngHzNESAPU; + break; + case VGMC_MULTIPCM: + chip_val = VgmFile[curvgm].Header.lngHzMultiPCM; + use_two = 0x00; + break; + case VGMC_UPD7759: + chip_val = VgmFile[curvgm].Header.lngHzUPD7759; + break; + case VGMC_OKIM6258: + chip_val = VgmFile[curvgm].Header.lngHzOKIM6258; + break; + case VGMC_OKIM6295: + chip_val = VgmFile[curvgm].Header.lngHzOKIM6295; + break; + case VGMC_K051649: + chip_val = VgmFile[curvgm].Header.lngHzK051649; + break; + case VGMC_K054539: + chip_val = VgmFile[curvgm].Header.lngHzK054539; + break; + case VGMC_C6280: + chip_val = VgmFile[curvgm].Header.lngHzHuC6280; + break; + case VGMC_C140: + chip_val = VgmFile[curvgm].Header.lngHzC140; + break; + case VGMC_K053260: + chip_val = VgmFile[curvgm].Header.lngHzK053260; + break; + case VGMC_POKEY: + chip_val = VgmFile[curvgm].Header.lngHzPokey; + break; + case VGMC_QSOUND: + chip_val = VgmFile[curvgm].Header.lngHzQSound; + use_two = 0x00; + break; + case VGMC_SCSP: + chip_val = VgmFile[curvgm].Header.lngHzSCSP; + use_two = 0x00; + break; + // case VGMC_OKIM6376: + // chip_val = VgmFile[curvgm].Header.lngHzOKIM6376; + // use_two = 0x00; + // break; + default: + chip_val = 0x00000001; + use_two = 0x00; + break; + } + if (! chip_val) + { + chip_file = curvgm; + break; + } + else if (use_two) + { + if (! (chip_val & 0x40000000) && LOG_VGM_FILE == 0x01) + { + if (clock != chip_val) + logerror("VGM Log: Warning - 2-chip mode, but chip clocks different!\n"); + chip_file = curvgm; + clock = 0x40000000 | chip_val; + chip_type |= 0x80; + break; + } + } + } + } + if (chip_file == 0xFFFF) + { + for (curvgm = 0x00; curvgm < MAX_VGM_FILES; curvgm ++) + { + if (VgmFile[curvgm].hFile == NULL) + { + sprintf(vgm_name, "%s%hX.vgm", vgm_namebase, curvgm); + logerror("Opening %s ...\t", vgm_name); + VgmFile[curvgm].hFile = fopen(vgm_name, "wb"); + if (VgmFile[curvgm].hFile) + { + logerror("OK\n"); + chip_file = curvgm; + VgmFile[curvgm].BytesWrt = 0; + VgmFile[curvgm].SmplsWrt = 0; + VgmFile[curvgm].EvtDelay = 0; + vgm_header_clear(curvgm); + } + else + { + logerror("Failed to create the file!\n"); + } + break; + } + } + } + if (chip_file == 0xFFFF) + return 0xFFFF; + + VgmChip[chip_id].VgmID = chip_file; + VgmChip[chip_id].ChipType = chip_type; + VgmChip[chip_id].HadWrite = 0x00; + VgmChip[chip_id].PCMCache = NULL; + + switch(chip_type & 0x7F) + { + case VGMC_SN76496: + VgmFile[chip_file].Header.lngHzPSG = clock; + break; + case VGMC_YM2413: + VgmFile[chip_file].Header.lngHz2413 = clock; + break; + case VGMC_YM2612: + VgmFile[chip_file].Header.lngHz2612 = clock; + break; + case VGMC_YM2151: + VgmFile[chip_file].Header.lngHz2151 = clock; + break; + case VGMC_SEGAPCM: + VgmFile[chip_file].Header.lngHzSPCM = clock; + break; + case VGMC_RF5C68: + VgmFile[chip_file].Header.lngHzRF5C68 = clock; + VgmChip[chip_id].PCMCache = &VgmPCache[chip_file]; + vgm_setup_pcmcache(VgmChip[chip_id].PCMCache, 0x400); + break; + case VGMC_YM2203: + VgmFile[chip_file].Header.lngHz2203 = clock; + break; + case VGMC_YM2608: + VgmFile[chip_file].Header.lngHz2608 = clock; + break; + case VGMC_YM2610: + VgmFile[chip_file].Header.lngHz2610 = clock; + break; + case VGMC_YM3812: + VgmFile[chip_file].Header.lngHz3812 = clock; + break; + case VGMC_YM3526: + VgmFile[chip_file].Header.lngHz3526 = clock; + break; + case VGMC_Y8950: + VgmFile[chip_file].Header.lngHz8950 = clock; + break; + case VGMC_YMF262: + VgmFile[chip_file].Header.lngHz262 = clock; + break; + case VGMC_YMF278B: + VgmFile[chip_file].Header.lngHz278B = clock; + break; + case VGMC_YMF271: + VgmFile[chip_file].Header.lngHz271 = clock; + break; + case VGMC_YMZ280B: + VgmFile[chip_file].Header.lngHz280B = clock; + break; + case VGMC_T6W28: + VgmFile[chip_file].Header.lngHzPSG = clock | 0xC0000000; // Cheat to use 2 SN76489 chips + break; + case VGMC_RF5C164: + VgmFile[chip_file].Header.lngHzRF5C164 = clock; + VgmChip[chip_id].PCMCache = &VgmPCache[chip_file]; + vgm_setup_pcmcache(VgmChip[chip_id].PCMCache, 0x400); + break; + case VGMC_PWM: + VgmFile[chip_file].Header.lngHzPWM = clock; + break; + case VGMC_AY8910: + VgmFile[chip_file].Header.lngHzAY8910 = clock; + break; + case VGMC_GBSOUND: + VgmFile[chip_file].Header.lngHzGBDMG = clock; + break; + case VGMC_NESAPU: + VgmFile[chip_file].Header.lngHzNESAPU = clock; + break; + case VGMC_MULTIPCM: + VgmFile[chip_file].Header.lngHzMultiPCM = clock; + break; + case VGMC_UPD7759: + VgmFile[chip_file].Header.lngHzUPD7759 = clock; + break; + case VGMC_OKIM6258: + VgmFile[chip_file].Header.lngHzOKIM6258 = clock; + break; + case VGMC_OKIM6295: + VgmFile[chip_file].Header.lngHzOKIM6295 = clock; + break; + case VGMC_K051649: + VgmFile[chip_file].Header.lngHzK051649 = clock; + break; + case VGMC_K054539: + VgmFile[chip_file].Header.lngHzK054539 = clock; + break; + case VGMC_C6280: + VgmFile[chip_file].Header.lngHzHuC6280 = clock; + break; + case VGMC_C140: + VgmFile[chip_file].Header.lngHzC140 = clock; + break; + case VGMC_K053260: + VgmFile[chip_file].Header.lngHzK053260 = clock; + break; + case VGMC_POKEY: + VgmFile[chip_file].Header.lngHzPokey = clock; + break; + case VGMC_QSOUND: + VgmFile[chip_file].Header.lngHzQSound = clock; + break; + case VGMC_SCSP: + VgmFile[chip_file].Header.lngHzSCSP = clock; + VgmChip[chip_id].PCMCache = &VgmPCache[chip_file]; + vgm_setup_pcmcache(VgmChip[chip_id].PCMCache, 0x4000); + break; +// case VGMC_OKIM6376: +// VgmFile[chip_file].Header.lngHzOKIM6376 = clock; +// break; + } + + switch(chip_type & 0x7F) + { + case VGMC_SN76496: + case VGMC_YM2413: + case VGMC_YM2612: + case VGMC_YM2151: + case VGMC_SEGAPCM: + case VGMC_T6W28: + vgm_header_sizecheck(chip_file, 0x00000151, 0x40); + break; + case VGMC_RF5C68: + case VGMC_YM2203: + case VGMC_YM2608: + case VGMC_YM2610: + case VGMC_YM3812: + case VGMC_YM3526: + case VGMC_Y8950: + case VGMC_YMF262: + case VGMC_YMF278B: + case VGMC_YMF271: + case VGMC_YMZ280B: + case VGMC_RF5C164: + case VGMC_PWM: + case VGMC_AY8910: + vgm_header_sizecheck(chip_file, 0x00000151, 0x80); + break; + case VGMC_GBSOUND: + case VGMC_NESAPU: + case VGMC_MULTIPCM: + case VGMC_UPD7759: + case VGMC_OKIM6258: + case VGMC_OKIM6295: + case VGMC_K051649: + case VGMC_K054539: + case VGMC_C6280: + case VGMC_C140: + case VGMC_K053260: + case VGMC_POKEY: + case VGMC_QSOUND: +// case VGMC_OKIM6376: + vgm_header_sizecheck(chip_file, 0x00000161, 0xC0); + break; + case VGMC_SCSP: + vgm_header_sizecheck(chip_file, 0x00000171, 0xC0); + break; + } + + return chip_id; +} + +static void vgm_setup_pcmcache(VGM_PCMCACHE* TempPC, UINT32 Size) +{ + TempPC->CacheSize = Size; + if (TempPC->CacheData != NULL) + free(TempPC->CacheData); + TempPC->CacheData = (UINT8*)malloc(Size); + + return; +} + +void vgm_header_set(UINT16 chip_id, UINT8 attr, UINT32 data) +{ + VGM_HEADER* VH; + UINT8 bitcnt; + + if (! LOG_VGM_FILE || chip_id == 0xFFFF) + return; + if (VgmChip[chip_id].ChipType == 0xFF) + return; + + VH = &VgmFile[VgmChip[chip_id].VgmID].Header; + switch(VgmChip[chip_id].ChipType & 0x7F) // Write the Header data + { + case VGMC_SN76496: + case VGMC_T6W28: + switch(attr) + { + case 0x00: // Reserved + break; + case 0x01: // Shift Register Width (Feedback Mask) + bitcnt = 0x00; // Convert the BitMask to BitCount + while(data) + { + data >>= 1; + bitcnt ++; + } + VH->bytPSG_SRWidth = bitcnt; + break; + case 0x02: // Feedback Pattern (White Noise Tap #1) + VH->shtPSG_Feedback = (UINT16)data; + break; + case 0x03: // Feedback Pattern (White Noise Tap #2) + // must be called after #1 + VH->shtPSG_Feedback |= (UINT16)data; + break; + case 0x04: // Negate Channels Flag + VH->bytPSG_Flags &= ~(0x01 << 1); + VH->bytPSG_Flags |= (data & 0x01) << 1; + break; + case 0x05: // Stereo Flag (On/Off) + // 0 is Stereo and 1 is mono + VH->bytPSG_Flags &= ~(0x01 << 2); + VH->bytPSG_Flags |= (~data & 0x01) << 2; + break; + case 0x06: // Clock Divider (On/Off) + VH->bytPSG_Flags &= ~(0x01 << 3); + bitcnt = (data == 1) ? 0x01 : 0x00; + VH->bytPSG_Flags |= (bitcnt & 0x01) << 3; + break; + case 0x07: // Freq 0 is Max + VH->bytPSG_Flags &= ~(0x01 << 0); + VH->bytPSG_Flags |= (data & 0x01) << 0; + break; + } + break; + case VGMC_YM2413: + break; + case VGMC_YM2612: + break; + case VGMC_YM2151: + break; + case VGMC_SEGAPCM: + switch(attr) + { + case 0x00: // Reserved + break; + case 0x01: // Sega PCM Interface + VH->lngSPCMIntf = data; + break; + } + break; + case VGMC_RF5C68: + break; + case VGMC_YM2203: + switch(attr) + { + case 0x00: // Reserved + break; + case 0x01: // Flags + VH->lngAYFlagsYM2203 = data & 0xFF; + break; + } + break; + case VGMC_YM2608: + switch(attr) + { + case 0x00: // Reserved + break; + case 0x01: // Flags + VH->lngAYFlagsYM2608 = data & 0xFF; + break; + } + break; + case VGMC_YM2610: + switch(attr) + { + case 0x00: // Chip Type (set YM2610B mode) + VH->lngHz2610 = (VH->lngHz2610 & 0x7FFFFFFF) | (data << 31); + break; + } + break; + case VGMC_YM3812: + break; + case VGMC_YM3526: + break; + case VGMC_Y8950: + break; + case VGMC_YMF262: + switch(attr) + { + case 0x00: // is Part of OPL4 + if (data) + { + VgmChip[chip_id].ChipType = VGMC_YMF278B; + VH->lngHz262 = 0x00; + } + break; + } + break; + case VGMC_YMF278B: + break; + case VGMC_YMF271: + break; + case VGMC_YMZ280B: + break; + case VGMC_RF5C164: + break; + case VGMC_PWM: + break; + case VGMC_AY8910: + switch(attr) + { + case 0x00: // Device Type + VH->lngAYType = data & 0xFF; + break; + case 0x01: // Flags + VH->lngAYFlags = data & 0xFF; + break; + case 0x10: // Resistor Loads + case 0x11: + case 0x12: + logerror("AY8910: Resistor Load %hu = %u\n", attr & 0x0F, data); + break; + } + break; + case VGMC_GBSOUND: + break; + case VGMC_NESAPU: + break; + case VGMC_MULTIPCM: + break; + case VGMC_UPD7759: + switch(attr) + { + case 0x00: // Chip Type (set master/slave mode) + VH->lngHzUPD7759 = (VH->lngHzUPD7759 & 0x7FFFFFFF) | (data << 31); + break; + } + break; + case VGMC_OKIM6258: + switch(attr) + { + case 0x00: // Reserved + break; + case 0x01: // Clock Divider + VH->bytOKI6258Flags &= ~(0x03 << 0); + VH->bytOKI6258Flags |= (data & 0x03) << 0; + break; + case 0x02: // ADPCM Type + VH->bytOKI6258Flags &= ~(0x01 << 2); + VH->bytOKI6258Flags |= (data & 0x01) << 2; + break; + case 0x03: // 12-Bit Output + VH->bytOKI6258Flags &= ~(0x01 << 3); + VH->bytOKI6258Flags |= (data & 0x01) << 3; + break; + } + break; + case VGMC_OKIM6295: + switch(attr) + { + case 0x00: // Chip Type (pin 7 state) + VH->lngHzOKIM6295 = (VH->lngHzOKIM6295 & 0x7FFFFFFF) | (data << 31); + break; + } + break; + case VGMC_K051649: + break; + case VGMC_K054539: + switch(attr) + { + case 0x01: // Control Flags + VH->bytK054539Flags = data; + break; + } + break; + case VGMC_C6280: + break; + case VGMC_C140: + switch(attr) + { + case 0x01: // Banking Type + VH->bytC140Type = data; + break; + } + break; + case VGMC_K053260: + break; + case VGMC_POKEY: + break; + case VGMC_QSOUND: + break; + case VGMC_SCSP: + break; +// case VGMC_OKIM6376: +// break; + } + + return; +} + +static void vgm_close(UINT16 vgm_id) +{ + VGM_INF* VI; + VGM_HEADER* Header; + + if (! LOG_VGM_FILE || vgm_id == 0xFFFF) + return; + + VI = &VgmFile[vgm_id]; + Header = &VI->Header; + + if (! VI->WroteHeader) + { + fclose(VI->hFile); + VI->hFile = NULL; + return; + } + + vgm_write_delay(vgm_id); + fputc(0x66, VI->hFile); // Write EOF Command + VI->BytesWrt ++; + + // GD3 Tag + Header->lngGD3Offset = VI->BytesWrt - 0x00000014; + fwrite(&VgmTag.fccGD3, 0x04, 0x01, VI->hFile); + fwrite(&VgmTag.lngVersion, 0x04, 0x01, VI->hFile); + fwrite(&VgmTag.lngTagLength, 0x04, 0x01, VI->hFile); + fwrite(VgmTag.strTrackNameE, sizeof(wchar_t), wcslen(VgmTag.strTrackNameE) + 0x01, VI->hFile); + fwrite(VgmTag.strTrackNameJ, sizeof(wchar_t), wcslen(VgmTag.strTrackNameJ) + 0x01, VI->hFile); + fwrite(VgmTag.strGameNameE, sizeof(wchar_t), wcslen(VgmTag.strGameNameE) + 0x01, VI->hFile); + fwrite(VgmTag.strGameNameJ, sizeof(wchar_t), wcslen(VgmTag.strGameNameJ) + 0x01, VI->hFile); + fwrite(VgmTag.strSystemNameE, sizeof(wchar_t), wcslen(VgmTag.strSystemNameE) + 0x01, VI->hFile); + fwrite(VgmTag.strSystemNameJ, sizeof(wchar_t), wcslen(VgmTag.strSystemNameJ) + 0x01, VI->hFile); + fwrite(VgmTag.strAuthorNameE, sizeof(wchar_t), wcslen(VgmTag.strAuthorNameE) + 0x01, VI->hFile); + fwrite(VgmTag.strAuthorNameJ, sizeof(wchar_t), wcslen(VgmTag.strAuthorNameJ) + 0x01, VI->hFile); + fwrite(VgmTag.strReleaseDate, sizeof(wchar_t), wcslen(VgmTag.strReleaseDate) + 0x01, VI->hFile); + fwrite(VgmTag.strCreator, sizeof(wchar_t), wcslen(VgmTag.strCreator) + 0x01, VI->hFile); + fwrite(VgmTag.strNotes, sizeof(wchar_t), wcslen(VgmTag.strNotes) + 0x01, VI->hFile); + VI->BytesWrt += 0x0C + VgmTag.lngTagLength; + + // Rewrite Header + Header->lngTotalSamples = VI->SmplsWrt; + Header->lngEOFOffset = VI->BytesWrt - 0x00000004; + Header->lngDataOffset = VI->HeaderBytes - 0x34; + + fseek(VI->hFile, 0x00, SEEK_SET); + fwrite(Header, 0x01, VI->HeaderBytes, VI->hFile); + + fclose(VI->hFile); + VI->hFile = NULL; + + logerror("VGM %02hX closed.\t%u Bytes, %u Samples written\n", vgm_id, VI->BytesWrt, VI->SmplsWrt); + + return; +} + +static void vgm_write_delay(UINT16 vgm_id) +{ + VGM_INF* VI; + UINT16 delaywrite; + + VI = &VgmFile[vgm_id]; + if (! VI->WroteHeader && VI->EvtDelay) + vgm_header_postwrite(vgm_id); // write post-header data + + if (VI->EvtDelay) + { + for (delaywrite = 0x00; delaywrite < MAX_VGM_CHIPS; delaywrite ++) + { + if (VgmChip[delaywrite].ChipType != 0xFF) + vgm_flush_pcm(&VgmChip[delaywrite]); + } + } + + while(VI->EvtDelay) + { + if (VI->EvtDelay > 0x0000FFFF) + delaywrite = 0xFFFF; + else + delaywrite = (UINT16)VI->EvtDelay; + + if (delaywrite <= 0x0010) + { + fputc(0x6F + delaywrite, VI->hFile); + VI->BytesWrt += 0x01; + } + else + { + fputc(0x61, VI->hFile); + fwrite(&delaywrite, 0x02, 0x01, VI->hFile); + VI->BytesWrt += 0x03; + } + VI->SmplsWrt += delaywrite; + + VI->EvtDelay -= delaywrite; + } + + return; +} + +void vgm_write(UINT16 chip_id, UINT8 port, UINT16 r, UINT8 v) +{ + VGM_CHIP* VC; + VGM_INF* VI; + VGM_PCMCACHE* VPC; + INT8 cm; // "Cheat Mode" to support 2 instances of 1 chip within 1 file + UINT16 curchip; + VGM_INIT_CMD WriteCmd; + UINT32 mem_addr; + + if (! LOG_VGM_FILE || chip_id == 0xFFFF) + return; + if (VgmChip[chip_id].ChipType == 0xFF) + return; + + VC = &VgmChip[chip_id]; + VI = &VgmFile[VC->VgmID]; + if (VI->hFile == NULL) + return; + + if (! VC->HadWrite) + { + VC->HadWrite = 0x01; + if (VC->ChipType & 0x80) + { + for (curchip = 0x00; curchip < chip_id; curchip ++) + { + if (VgmChip[curchip].ChipType == (VC->ChipType & 0x7F)) + VgmChip[curchip].HadWrite = 0x01; + } + } + } + + cm = (VC->ChipType & 0x80) ? 0x50 : 0x00; + + switch(VC->ChipType & 0x7F) // Write the data + { + case VGMC_T6W28: + cm = ~port & 0x01; + port = 0x00; + // no break + case VGMC_SN76496: + switch(port) + { + case 0x00: + cm = cm ? -0x20 : 0x00; + WriteCmd.Data[0x00] = 0x50 + cm; + WriteCmd.Data[0x01] = r; + WriteCmd.CmdLen = 0x02; + break; + case 0x01: + cm = cm ? -0x10 : 0x00; + WriteCmd.Data[0x00] = 0x4F + cm; + WriteCmd.Data[0x01] = r; + WriteCmd.CmdLen = 0x02; + break; + } + break; + case VGMC_YM2413: + WriteCmd.Data[0x00] = 0x51 + cm; + WriteCmd.Data[0x01] = r; + WriteCmd.Data[0x02] = v; + WriteCmd.CmdLen = 0x03; + break; + case VGMC_YM2612: + WriteCmd.Data[0x00] = 0x52 + (port & 0x01) + cm; + WriteCmd.Data[0x01] = r; + WriteCmd.Data[0x02] = v; + WriteCmd.CmdLen = 0x03; + break; + case VGMC_YM2151: + WriteCmd.Data[0x00] = 0x54 + cm; + WriteCmd.Data[0x01] = r; + WriteCmd.Data[0x02] = v; + WriteCmd.CmdLen = 0x03; + break; + case VGMC_SEGAPCM: + r |= (VC->ChipType & 0x80) << 8; + WriteCmd.Data[0x00] = 0xC0; // Write Memory + WriteCmd.Data[0x01] = (r >> 0) & 0xFF; // offset low + WriteCmd.Data[0x02] = (r >> 8) & 0xFF; // offset high + WriteCmd.Data[0x03] = v; // data + WriteCmd.CmdLen = 0x04; + break; + case VGMC_RF5C68: + switch(port) + { + case 0x00: + WriteCmd.Data[0x00] = 0xB0; // Write Register + WriteCmd.Data[0x01] = r; // Register + WriteCmd.Data[0x02] = v; // Value + WriteCmd.CmdLen = 0x03; + break; + case 0x01: + WriteCmd.Data[0x00] = 0xC1; // Write Memory + WriteCmd.Data[0x01] = (r >> 0) & 0xFF; // offset low + WriteCmd.Data[0x02] = (r >> 8) & 0xFF; // offset high + WriteCmd.Data[0x03] = v; // Data + WriteCmd.CmdLen = 0x04; + break; + } + break; + case VGMC_YM2203: + WriteCmd.Data[0x00] = 0x55 + cm; + WriteCmd.Data[0x01] = r; + WriteCmd.Data[0x02] = v; + WriteCmd.CmdLen = 0x03; + break; + case VGMC_YM2608: + WriteCmd.Data[0x00] = 0x56 + (port & 0x01) + cm; + WriteCmd.Data[0x01] = r; + WriteCmd.Data[0x02] = v; + WriteCmd.CmdLen = 0x03; + break; + case VGMC_YM2610: + WriteCmd.Data[0x00] = 0x58 + (port & 0x01) + cm; + WriteCmd.Data[0x01] = r; + WriteCmd.Data[0x02] = v; + WriteCmd.CmdLen = 0x03; + break; + case VGMC_YM3812: + WriteCmd.Data[0x00] = 0x5A + cm; + WriteCmd.Data[0x01] = r; + WriteCmd.Data[0x02] = v; + WriteCmd.CmdLen = 0x03; + break; + case VGMC_YM3526: + WriteCmd.Data[0x00] = 0x5B + cm; + WriteCmd.Data[0x01] = r; + WriteCmd.Data[0x02] = v; + WriteCmd.CmdLen = 0x03; + break; + case VGMC_Y8950: + WriteCmd.Data[0x00] = 0x5C + cm; + WriteCmd.Data[0x01] = r; + WriteCmd.Data[0x02] = v; + WriteCmd.CmdLen = 0x03; + break; + case VGMC_YMF262: + WriteCmd.Data[0x00] = 0x5E + (port & 0x01) + cm; + WriteCmd.Data[0x01] = r; + WriteCmd.Data[0x02] = v; + WriteCmd.CmdLen = 0x03; + break; + case VGMC_YMF278B: + WriteCmd.Data[0x00] = 0xD0; + WriteCmd.Data[0x01] = port | (VC->ChipType & 0x80); + WriteCmd.Data[0x02] = r; + WriteCmd.Data[0x03] = v; + WriteCmd.CmdLen = 0x04; + break; + case VGMC_YMF271: + WriteCmd.Data[0x00] = 0xD1; + WriteCmd.Data[0x01] = port | (VC->ChipType & 0x80); + WriteCmd.Data[0x02] = r; + WriteCmd.Data[0x03] = v; + WriteCmd.CmdLen = 0x04; + break; + case VGMC_YMZ280B: + WriteCmd.Data[0x00] = 0x5D + cm; + WriteCmd.Data[0x01] = r; + WriteCmd.Data[0x02] = v; + WriteCmd.CmdLen = 0x03; + break; + case VGMC_RF5C164: + switch(port) + { + case 0x00: + WriteCmd.Data[0x00] = 0xB1; // Write Register + WriteCmd.Data[0x01] = r; // Register + WriteCmd.Data[0x02] = v; // Value + WriteCmd.CmdLen = 0x03; + break; + case 0x01: + WriteCmd.Data[0x00] = 0xC2; // Write Memory + WriteCmd.Data[0x01] = (r >> 0) & 0xFF; // offset low + WriteCmd.Data[0x02] = (r >> 8) & 0xFF; // offset high + WriteCmd.Data[0x03] = v; // Data + WriteCmd.CmdLen = 0x04; + break; + } + break; + case VGMC_PWM: + WriteCmd.Data[0x00] = 0xB2; + WriteCmd.Data[0x01] = (port << 4) | ((r & 0xF00) >> 8); + WriteCmd.Data[0x02] = r & 0xFF; + WriteCmd.CmdLen = 0x03; + break; + case VGMC_AY8910: + WriteCmd.Data[0x00] = 0xA0; + WriteCmd.Data[0x01] = r | (VC->ChipType & 0x80); + WriteCmd.Data[0x02] = v; + WriteCmd.CmdLen = 0x03; + break; + case VGMC_GBSOUND: + WriteCmd.Data[0x00] = 0xB3; + WriteCmd.Data[0x01] = r | (VC->ChipType & 0x80); + WriteCmd.Data[0x02] = v; + WriteCmd.CmdLen = 0x03; + break; + case VGMC_NESAPU: + WriteCmd.Data[0x00] = 0xB4; + WriteCmd.Data[0x01] = r | (VC->ChipType & 0x80); + WriteCmd.Data[0x02] = v; + WriteCmd.CmdLen = 0x03; + break; + case VGMC_MULTIPCM: + switch(port) + { + case 0x00: // Register Write + WriteCmd.Data[0x00] = 0xB5; + WriteCmd.Data[0x01] = r | (VC->ChipType & 0x80); + WriteCmd.Data[0x02] = v; + WriteCmd.CmdLen = 0x03; + break; + case 0x01: // Bank Write + WriteCmd.Data[0x00] = 0xC3; + WriteCmd.Data[0x01] = v | (VC->ChipType & 0x80); // Both/Left/Right Offset + WriteCmd.Data[0x02] = (r >> 0) & 0xFF; // offset low + WriteCmd.Data[0x03] = (r >> 8) & 0xFF; // offset high + WriteCmd.CmdLen = 0x04; + break; + } + break; + case VGMC_UPD7759: + WriteCmd.Data[0x00] = 0xB6; + WriteCmd.Data[0x01] = r | (VC->ChipType & 0x80); + WriteCmd.Data[0x02] = v; + WriteCmd.CmdLen = 0x03; + break; + case VGMC_OKIM6258: + WriteCmd.Data[0x00] = 0xB7; + WriteCmd.Data[0x01] = r | (VC->ChipType & 0x80); + WriteCmd.Data[0x02] = v; + WriteCmd.CmdLen = 0x03; + break; + case VGMC_OKIM6295: + WriteCmd.Data[0x00] = 0xB8; + WriteCmd.Data[0x01] = r | (VC->ChipType & 0x80); + WriteCmd.Data[0x02] = v; + WriteCmd.CmdLen = 0x03; + break; + case VGMC_K051649: + WriteCmd.Data[0x00] = 0xD2; + WriteCmd.Data[0x01] = port | (VC->ChipType & 0x80); + WriteCmd.Data[0x02] = r; + WriteCmd.Data[0x03] = v; + WriteCmd.CmdLen = 0x04; + break; + case VGMC_K054539: + WriteCmd.Data[0x00] = 0xD3; + WriteCmd.Data[0x01] = (r >> 8) | (VC->ChipType & 0x80); + WriteCmd.Data[0x02] = r & 0xFF; + WriteCmd.Data[0x03] = v; + WriteCmd.CmdLen = 0x04; + break; + case VGMC_C6280: + WriteCmd.Data[0x00] = 0xB9; + WriteCmd.Data[0x01] = r | (VC->ChipType & 0x80); + WriteCmd.Data[0x02] = v; + WriteCmd.CmdLen = 0x03; + break; + case VGMC_C140: + WriteCmd.Data[0x00] = 0xD4; + WriteCmd.Data[0x01] = (r >> 8) | (VC->ChipType & 0x80); + WriteCmd.Data[0x02] = r & 0xFF; + WriteCmd.Data[0x03] = v; + WriteCmd.CmdLen = 0x04; + break; + case VGMC_K053260: + WriteCmd.Data[0x00] = 0xBA; + WriteCmd.Data[0x01] = r | (VC->ChipType & 0x80); + WriteCmd.Data[0x02] = v; + WriteCmd.CmdLen = 0x03; + break; + case VGMC_POKEY: + WriteCmd.Data[0x00] = 0xBB; + WriteCmd.Data[0x01] = r | (VC->ChipType & 0x80); + WriteCmd.Data[0x02] = v; + WriteCmd.CmdLen = 0x03; + break; + case VGMC_QSOUND: + WriteCmd.Data[0x00] = 0xC4; + WriteCmd.Data[0x01] = (r & 0xFF00) >> 8; // Data MSB + WriteCmd.Data[0x02] = (r & 0x00FF) >> 0; // Data LSB + WriteCmd.Data[0x03] = v; // Command + WriteCmd.CmdLen = 0x04; + break; + case VGMC_SCSP: + switch(port & 0x80) + { + case 0x00: // Register Write + WriteCmd.Data[0x00] = 0xC5; + WriteCmd.Data[0x01] = ((r & 0xFF00) >> 8) | (VC->ChipType & 0x80); // Data MSB + WriteCmd.Data[0x02] = (r & 0x00FF) >> 0; // Data LSB + WriteCmd.Data[0x03] = v; // Command + WriteCmd.CmdLen = 0x04; + break; + case 0x80: // Memory Write + //vgm_write_delay(VC->VgmID); + + VPC = VC->PCMCache; + mem_addr = ((port & 0x7F) << 16) | (r << 0); + + // optimize consecutive Memory Writes + if (VPC->Start == 0xFFFFFFFF || mem_addr != VPC->Next) + { + // flush cache to file + vgm_flush_pcm(VC); + vgm_write_delay(VC->VgmID); + //printf("Mem Cache Start: %06X\n", mem_addr); + VPC->Start = mem_addr; + VPC->Next = VPC->Start; + VPC->Pos = 0x00; + } + VPC->CacheData[VPC->Pos] = v; + VPC->Pos ++; + VPC->Next ++; + if (VPC->Pos >= VPC->CacheSize) + VPC->Next = 0xFFFFFFFF; + return; + } + break; +// case VGMC_OKIM6376: +// WriteCmd.Data[0x00] = 0x31; +// WriteCmd.Data[0x01] = v; +// WriteCmd.CmdLen = 0x02; +// break; + } + + vgm_write_delay(VC->VgmID); + + if (! VI->WroteHeader) + { + /*if (VI->CmdCount >= VI->CmdAlloc) + { + VI->CmdAlloc += 0x100; + VI->Commands = (VGM_INIT_CMD*)realloc(VI->Commands, sizeof(VGM_INIT_CMD) * VI->CmdAlloc); + }*/ + + // most commands sent at time 0 come from soundchip_reset(), + // so I check if the command is "worth" being written + cm = 0x00; + switch(VC->ChipType & 0x7F) + { + case VGMC_YM2203: + case VGMC_YM2608: // (not on YM2610 and YM2612) + if (r >= 0x2D && r <= 0x2F) + cm = 0x01; // Prescaler Select + break; + case VGMC_OKIM6258: + if (r >= 0x08 && r <= 0x0F) + cm = 0x01; // OKIM6258 clock change + break; + } + + if (cm && VI->CmdCount < 0x100) + { + VI->Commands[VI->CmdCount] = WriteCmd; + VI->CmdCount ++; + } + return; + } + + if (VI->hFile != NULL) + { + fwrite(WriteCmd.Data, 0x01, WriteCmd.CmdLen, VI->hFile); + VI->BytesWrt += WriteCmd.CmdLen; + } + + return; +} + +void vgm_write_large_data(UINT16 chip_id, UINT8 type, UINT32 datasize, UINT32 value1, UINT32 value2, const void* data) +{ + // datasize - ROM/RAM size + // value1 - Start Address + // value2 - Bytes to Write (0 -> write from Start Address to end of ROM/RAM) + + VGM_INF* VI; + VGM_ROM_DATA* VR; + UINT32 finalsize; + UINT8 blk_type; + + if (! LOG_VGM_FILE || chip_id == 0xFFFF) + return; + if (VgmChip[chip_id].ChipType == 0xFF) + return; + + VI = &VgmFile[VgmChip[chip_id].VgmID]; + if (VI->hFile == NULL) + return; + + blk_type = 0x00; + switch(VgmChip[chip_id].ChipType & 0x7F) // Write the data + { + case VGMC_SN76496: + case VGMC_T6W28: + break; + case VGMC_YM2413: + break; + case VGMC_YM2612: + break; + case VGMC_YM2151: + break; + case VGMC_SEGAPCM: + switch(type) + { + case 0x00: + break; + case 0x01: // ROM Image + blk_type = 0x80; // Type: SegaPCM ROM Image + break; + } + break; + case VGMC_RF5C68: + break; + case VGMC_YM2203: + break; + case VGMC_YM2608: + switch(type) + { + case 0x00: + break; + case 0x01: // PCM ROM Data + blk_type = 0x81; // Type: YM2608 DELTA-T ROM Data + break; + } + break; + case VGMC_YM2610: + switch(type) + { + case 0x00: + break; + case 0x01: // PCM ROM Data A + blk_type = 0x82; // Type: YM2610 ADPCM ROM Data + break; + case 0x02: // PCM ROM Data B + blk_type = 0x83; // Type: YM2610 DELTA-T ROM Data + break; + } + break; + case VGMC_YM3812: + break; + case VGMC_YM3526: + break; + case VGMC_Y8950: + switch(type) + { + case 0x00: + break; + case 0x01: // DELTA-T Memory + blk_type = 0x88; // Type: Y8950 DELTA-T ROM Data + break; + } + break; + case VGMC_YMF262: + break; + case VGMC_YMF278B: + switch(type) + { + case 0x00: + break; + case 0x01: // ROM Data + blk_type = 0x84; // Type: YMF278B ROM Data + break; + case 0x02: // RAM Data + blk_type = 0x87; // Type: YMF278B RAM Data + break; + } + break; + case VGMC_YMF271: + switch(type) + { + case 0x00: + break; + case 0x01: // ROM Data + blk_type = 0x85; // Type: YMF271 ROM Data + break; + } + break; + case VGMC_YMZ280B: + switch(type) + { + case 0x00: + break; + case 0x01: // ROM Data + blk_type = 0x86; // Type: YMZ280B ROM Data + break; + } + break; + case VGMC_RF5C164: + break; + case VGMC_PWM: + break; + case VGMC_AY8910: + break; + case VGMC_GBSOUND: + break; + case VGMC_NESAPU: + switch(type) + { + case 0x00: + break; + case 0x01: // RAM Data + if (vgm_nes_ram_check(VI, datasize, &value1, &value2, (UINT8*)data)) + blk_type = 0xC2; + break; + } + break; + case VGMC_MULTIPCM: + switch(type) + { + case 0x00: + break; + case 0x01: // ROM Data + blk_type = 0x89; // Type: MultiPCM ROM Data + break; + } + break; + case VGMC_UPD7759: + switch(type) + { + case 0x00: + break; + case 0x01: // ROM Data + blk_type = 0x8A; // Type: UPD7759 ROM Data + break; + } + break; + case VGMC_OKIM6258: + break; + case VGMC_OKIM6295: + switch(type) + { + case 0x00: + break; + case 0x01: // ROM Data + blk_type = 0x8B; // Type: OKIM6295 ROM Data + break; + } + break; + case VGMC_K051649: + break; + case VGMC_K054539: + switch(type) + { + case 0x00: + break; + case 0x01: // ROM Data + blk_type = 0x8C; // Type: K054539 ROM Data + break; + } + break; + case VGMC_C6280: + break; + case VGMC_C140: + switch(type) + { + case 0x00: + break; + case 0x01: // ROM Data + blk_type = 0x8D; // Type: C140 ROM Data + break; + } + break; + case VGMC_K053260: + switch(type) + { + case 0x00: + break; + case 0x01: // ROM Data + blk_type = 0x8E; // Type: K053260 ROM Data + break; + } + break; + case VGMC_POKEY: + break; + case VGMC_QSOUND: + switch(type) + { + case 0x00: + break; + case 0x01: // ROM Data + blk_type = 0x8F; // Type: QSound ROM Data + break; + } + break; + case VGMC_SCSP: + switch(type) + { + case 0x00: + break; + case 0x01: // RAM Data + vgm_flush_pcm(&VgmChip[chip_id]); + blk_type = 0xE0; // Type: YMF292/SCSP RAM Data + break; + case 0x02: // ROM Data + blk_type = 0x06; // Type: unused/invalid + break; + } + break; +// case VGMC_OKIM6376: +// switch(type) +// { +// case 0x00: +// break; +// case 0x01: // ROM Data +// //blk_type = 0x8C; // Type: OKIM6376 ROM Data +// break; +// } +// break; + } + + if (! blk_type) + return; + + if (data == NULL) + logerror("ROM Data %02X: (0x%x bytes) is NULL!", blk_type, datasize); + + vgm_write_delay(VgmChip[chip_id].VgmID); + + if (! VI->WroteHeader) + { + switch(blk_type & 0xC0) + { + case 0x80: // ROM Image + if (VI->DataCount < 0x20) + { + VR = &VI->DataBlk[VI->DataCount]; + VI->DataCount ++; + + VR->Type = blk_type; + VR->DataSize = datasize | ((VgmChip[chip_id].ChipType & 0x80) << 24); + VR->Data = data; + } + break; + case 0xC0: // RAM Writes + break; + } + return; + } + + fputc(0x67, VI->hFile); + fputc(0x66, VI->hFile); + fputc(blk_type, VI->hFile); + + switch(blk_type & 0xC0) + { + case 0x00: // Normal Data Block + if (! value2) + value2 = datasize - value1; + if (data == NULL) + { + value1 = 0x00; + value2 = 0x00; + } + finalsize = value2; + finalsize |= (VgmChip[chip_id].ChipType & 0x80) << 24; + + fwrite(&finalsize, 0x04, 0x01, VI->hFile); // Data Block Size + fwrite(data, 0x01, value2, VI->hFile); + VI->BytesWrt += 0x07 + (finalsize & 0x7FFFFFFF); + break; + case 0x80: // ROM Image + // Value 1 & 2 are used to write parts of the image (and save space) + if (! value2) + value2 = datasize - value1; + if (data == NULL) + { + value1 = 0x00; + value2 = 0x00; + } + finalsize = 0x08 + value2; + finalsize |= (VgmChip[chip_id].ChipType & 0x80) << 24; + + fwrite(&finalsize, 0x04, 0x01, VI->hFile); // Data Block Size + fwrite(&datasize, 0x04, 0x01, VI->hFile); // ROM Size + fwrite(&value1, 0x04, 0x01, VI->hFile); // Data Base Address + fwrite(data, 0x01, value2, VI->hFile); + VI->BytesWrt += 0x07 + (finalsize & 0x7FFFFFFF); + break; + case 0xC0: // RAM Writes + if (! value2) + value2 = datasize - value1; + if (data == NULL) + { + value1 = 0x00; + value2 = 0x00; + } + + if (! (blk_type & 0x20)) + { + finalsize = 0x02 + value2; + finalsize |= (VgmChip[chip_id].ChipType & 0x80) << 24; + + fwrite(&finalsize, 0x04, 0x01, VI->hFile); // Data Block Size + fwrite(&value1, 0x02, 0x01, VI->hFile); // Data Address + } + else + { + finalsize = 0x04 + value2; + finalsize |= (VgmChip[chip_id].ChipType & 0x80) << 24; + + fwrite(&finalsize, 0x04, 0x01, VI->hFile); // Data Block Size + fwrite(&value1, 0x04, 0x01, VI->hFile); // Data Address + } + fwrite(data, 0x01, value2, VI->hFile); + VI->BytesWrt += 0x07 + (finalsize & 0x7FFFFFFF); + break; + } + + return; +} + +static UINT8 vgm_nes_ram_check(VGM_INF* VI, UINT32 datasize, UINT32* value1, UINT32* value2, const UINT8* data) +{ + UINT16 CurPos; + UINT16 DataStart; + UINT16 DataEnd; + + if (VI->NesMemEmpty) + { + VI->NesMemEmpty = 0x00; + memcpy(VI->NesMem, data, 0x4000); + + *value1 = 0xC000; + *value2 = 0x4000; + return 0x02; + } + + DataStart = *value1 & 0x3FFF; + if (! *value2) + DataEnd = 0x4000; + else + DataEnd = DataStart + *value2; + if (DataEnd > 0x4000) + DataEnd = 0x4000; + + for (CurPos = DataStart; CurPos < DataEnd; CurPos ++) + { + if (VI->NesMem[CurPos] != data[CurPos]) + { + memcpy(VI->NesMem + DataStart, data + DataStart, DataEnd - DataStart); + return 0x01; + } + } + + return 0x00; +} + +UINT16 vgm_get_chip_idx(UINT8 chip_type, UINT8 Num) +{ + // This is a small helper-function, that allows drivers/machines to + // make writes for their chips. + // e.g. NeoGeo CD rewrites the YM2610's PCM-RAM after changes + UINT16 curchip; + + for (curchip = 0x00; curchip < MAX_VGM_CHIPS; curchip ++) + { + if ((VgmChip[curchip].ChipType & 0x7F) == chip_type && + (VgmChip[curchip].ChipType >> 7) == Num) + return curchip; + } + + return 0xFFFF; +} + +static void vgm_flush_pcm(VGM_CHIP* VC) +{ + VGM_INF* VI; + VGM_PCMCACHE* VPC; + UINT8 SingleWrt; + UINT8 CmdType; + UINT32 finalsize; + + VI = &VgmFile[VC->VgmID]; + if (! LOG_VGM_FILE || VI->hFile == NULL) + return; + + VPC = VC->PCMCache; + if (VPC == NULL || VPC->Start == 0xFFFFFFFF || ! VPC->Pos) + return; + //logerror("Flushing PCM Data: Chip %02X, Addr %06X, Size %06X\n", + // VC->ChipType, VPC->Start, VPC->Pos); + + if (VPC->Pos == 0x01 && (VC->ChipType & 0x7F) != VGMC_SCSP) + SingleWrt = 0x01; + else + SingleWrt = 0x00; + + if (SingleWrt) + { + switch(VC->ChipType & 0x7F) + { + case VGMC_RF5C68: + CmdType = 0xC1; + break; + case VGMC_RF5C164: + CmdType = 0xC2; + break; + default: + CmdType = 0xFF; + break; + } + } + else + { + switch(VC->ChipType & 0x7F) + { + case VGMC_RF5C68: + CmdType = 0xC0; + break; + case VGMC_RF5C164: + CmdType = 0xC1; + break; + case VGMC_SCSP: + CmdType = 0xE0; + break; + default: + CmdType = 0xFF; + break; + } + } + + if (SingleWrt) + { + // it would be a waste of space to write a data block for 1 byte of data + fputc(CmdType, VI->hFile); // Write Memory + fputc((VPC->Start >> 0) & 0xFF, VI->hFile); // offset low + fputc((VPC->Start >> 8) & 0xFF, VI->hFile); // offset high + fputc(VPC->CacheData[0x00], VI->hFile); // Data + VI->BytesWrt += 0x04; + } + else + { + // calling vgm_write_large_data doesn't work if vgm_flush_pcm is + // called from vgm_write_delay + fputc(0x67, VI->hFile); + fputc(0x66, VI->hFile); + fputc(CmdType, VI->hFile); + if (! (CmdType & 0x20)) + { + finalsize = 0x02 + VPC->Pos; + finalsize |= (VC->ChipType & 0x80) << 24; + + fwrite(&finalsize, 0x04, 0x01, VI->hFile); // Data Block Size + fwrite(&VPC->Start, 0x02, 0x01, VI->hFile); // Data Address + } + else + { + finalsize = 0x04 + VPC->Pos; + finalsize |= (VC->ChipType & 0x80) << 24; + + fwrite(&finalsize, 0x04, 0x01, VI->hFile); // Data Block Size + fwrite(&VPC->Start, 0x04, 0x01, VI->hFile); // Data Address + } + fwrite(VPC->CacheData, 0x01, VPC->Pos, VI->hFile); + VI->BytesWrt += 0x07 + (finalsize & 0x7FFFFFFF); + } + + VPC->Start = 0xFFFFFFFF; + + return; +} diff --git a/src/emu/sound/vgmwrite.h b/src/emu/sound/vgmwrite.h new file mode 100644 index 0000000000000..373cba1da44a2 --- /dev/null +++ b/src/emu/sound/vgmwrite.h @@ -0,0 +1,54 @@ +#pragma once + +#ifndef __VGMWRITE_H__ +#define __VGMWRITE_H__ + +void vgm_start(running_machine &machine); +void vgm_stop(void); +UINT16 vgm_open(UINT8 chip_type, int clock); +void vgm_header_set(UINT16 chip_id, UINT8 attr, UINT32 data); +void vgm_write(UINT16 chip_id, UINT8 port, UINT16 r, UINT8 v); +void vgm_write_large_data(UINT16 chip_id, UINT8 type, UINT32 datasize, UINT32 value1, UINT32 value2, const void* data); +UINT16 vgm_get_chip_idx(UINT8 chip_type, UINT8 Num); + +// VGM Chip Constants +// v1.00 +#define VGMC_SN76496 0x00 +#define VGMC_YM2413 0x01 +#define VGMC_YM2612 0x02 +#define VGMC_YM2151 0x03 +// v1.51 +#define VGMC_SEGAPCM 0x04 +#define VGMC_RF5C68 0x05 +#define VGMC_YM2203 0x06 +#define VGMC_YM2608 0x07 +#define VGMC_YM2610 0x08 +#define VGMC_YM3812 0x09 +#define VGMC_YM3526 0x0A +#define VGMC_Y8950 0x0B +#define VGMC_YMF262 0x0C +#define VGMC_YMF278B 0x0D +#define VGMC_YMF271 0x0E +#define VGMC_YMZ280B 0x0F +#define VGMC_T6W28 0x7F // note: emulated via 2xSN76496 +#define VGMC_RF5C164 0x10 +#define VGMC_PWM 0x11 +#define VGMC_AY8910 0x12 +// v1.61 +#define VGMC_GBSOUND 0x13 +#define VGMC_NESAPU 0x14 +#define VGMC_MULTIPCM 0x15 +#define VGMC_UPD7759 0x16 +#define VGMC_OKIM6258 0x17 +#define VGMC_OKIM6295 0x18 +#define VGMC_K051649 0x19 +#define VGMC_K054539 0x1A +#define VGMC_C6280 0x1B +#define VGMC_C140 0x1C +#define VGMC_K053260 0x1D +#define VGMC_POKEY 0x1E +#define VGMC_QSOUND 0x1F +#define VGMC_SCSP 0x20 + +#define VGMC_OKIM6376 0xFF +#endif /* __VGMWRITE_H__ */