Skip to content

Instantly share code, notes, and snippets.

@h1romas4
Last active January 1, 2022 12:08
Show Gist options
  • Select an option

  • Save h1romas4/6e4ccaa738e50e73f394209ed863e1e7 to your computer and use it in GitHub Desktop.

Select an option

Save h1romas4/6e4ccaa738e50e73f394209ed863e1e7 to your computer and use it in GitHub Desktop.
MAME vgmwrite patch (YM2151 only) | to start mame with the "-vgmwrite 1" option. (ex. mame -vgmwrite 1)
From a6d60e1b9620975707728d80490d0ca9e4215cf1 Mon Sep 17 00:00:00 2001
From: h1romas4 <h1romas4@gmail.com>
Date: Sat, 1 Jan 2022 21:06:16 +0900
Subject: [PATCH] vgmwrite patched
---
makefile | 4 +-
scripts/src/lib.lua | 2 +
src/devices/sound/ymfm_mame.h | 12 +
src/devices/sound/ymopm.cpp | 20 +
src/emu/emuopts.cpp | 1 +
src/emu/emuopts.h | 2 +
src/emu/machine.cpp | 7 +
src/lib/util/vgmwrite.cpp | 2605 +++++++++++++++++++++++++++++++++
src/lib/util/vgmwrite.h | 66 +
9 files changed, 2717 insertions(+), 2 deletions(-)
create mode 100644 src/lib/util/vgmwrite.cpp
create mode 100644 src/lib/util/vgmwrite.h
diff --git a/makefile b/makefile
index 5f091b9d10c..311594583e9 100644
--- a/makefile
+++ b/makefile
@@ -1553,7 +1553,7 @@ $(GENDIR)/version.cpp: makefile $(GENDIR)/git_desc | $(GEN_FOLDERS)
@echo 'extern const char build_version[];' >> $@
@echo 'const char bare_build_version[] = BARE_BUILD_VERSION;' >> $@
@echo 'const char bare_vcs_revision[] = BARE_VCS_REVISION;' >> $@
- @echo 'const char build_version[] = BARE_BUILD_VERSION " (" BARE_VCS_REVISION ")";' >> $@
+ @echo 'const char build_version[] = BARE_BUILD_VERSION "VGM mod (" BARE_VCS_REVISION ")";' >> $@
else
$(GENDIR)/version.cpp: makefile $(GENDIR)/git_desc | $(GEN_FOLDERS)
@echo #define BARE_BUILD_VERSION "0.239" > $@
@@ -1563,7 +1563,7 @@ $(GENDIR)/version.cpp: makefile $(GENDIR)/git_desc | $(GEN_FOLDERS)
@echo extern const char build_version[]; >> $@
@echo const char bare_build_version[] = BARE_BUILD_VERSION; >> $@
@echo const char bare_vcs_revision[] = BARE_VCS_REVISION; >> $@
- @echo const char build_version[] = BARE_BUILD_VERSION " (" BARE_VCS_REVISION ")"; >> $@
+ @echo const char build_version[] = BARE_BUILD_VERSION "VGM mod (" BARE_VCS_REVISION ")"; >> $@
endif
diff --git a/scripts/src/lib.lua b/scripts/src/lib.lua
index 1cb1ce50f60..aed9eff604f 100644
--- a/scripts/src/lib.lua
+++ b/scripts/src/lib.lua
@@ -130,6 +130,8 @@ end
MAME_DIR .. "src/lib/util/vbiparse.h",
MAME_DIR .. "src/lib/util/vecstream.cpp",
MAME_DIR .. "src/lib/util/vecstream.h",
+ MAME_DIR .. "src/lib/util/vgmwrite.cpp",
+ MAME_DIR .. "src/lib/util/vgmwrite.h",
MAME_DIR .. "src/lib/util/wavwrite.cpp",
MAME_DIR .. "src/lib/util/wavwrite.h",
MAME_DIR .. "src/lib/util/xmlfile.cpp",
diff --git a/src/devices/sound/ymfm_mame.h b/src/devices/sound/ymfm_mame.h
index d55be4f35f2..f17572743e8 100644
--- a/src/devices/sound/ymfm_mame.h
+++ b/src/devices/sound/ymfm_mame.h
@@ -8,7 +8,12 @@
#include "ymfm/src/ymfm.h"
#include "ymfm/src/ymfm_ssg.h"
+#include "ymfm/src/ymfm_misc.h"
+#include "ymfm/src/ymfm_opl.h"
+#include "ymfm/src/ymfm_opm.h"
+#include "ymfm/src/ymfm_opn.h"
+#include "vgmwrite.h"
// set this to control the output sample rate for SSG-based chips
#define SSG_FIDELITY (ymfm::OPN_FIDELITY_MED)
@@ -162,6 +167,8 @@ protected:
devcb_write_line m_update_irq; // IRQ update callback
devcb_read8 m_io_read[2]; // up to 2 input port handlers
devcb_write8 m_io_write[2]; // up to 2 output port handlers
+ uint16_t vgm_idx; // vgmwrite hack
+ uint16_t vgm_offset; // vgmwrite hack
};
@@ -207,6 +214,11 @@ protected:
ymfm::ymfm_saved_state state(m_save_blob, true);
m_chip.save_restore(state);
+ // vgmwrite hack
+ if (std::is_same<ChipClass, ymfm::ym2151>::value) {
+ vgm_idx = vgm_open(VGMC_YM2151, device_t::clock());
+ }
+
// now register the blob for save, on the assumption the size won't change
save_item(NAME(m_save_blob));
}
diff --git a/src/devices/sound/ymopm.cpp b/src/devices/sound/ymopm.cpp
index a75da72b449..df3fff41e30 100644
--- a/src/devices/sound/ymopm.cpp
+++ b/src/devices/sound/ymopm.cpp
@@ -30,6 +30,19 @@ void ym2151_device::write(offs_t offset, u8 data)
{
if (m_reset_state == 0)
return;
+
+ // vgmwrite hack
+ switch (offset & 1)
+ {
+ case 0: // address port
+ vgm_offset = data;
+ break;
+
+ case 1: // data port
+ vgm_write(vgm_idx, 0x00, vgm_offset, data);
+ break;
+ }
+
parent::write(offset, data);
}
@@ -37,6 +50,10 @@ void ym2151_device::address_w(u8 data)
{
if (m_reset_state == 0)
return;
+
+ // vgmwrite hack
+ vgm_offset = data;
+
parent::address_w(data);
}
@@ -44,6 +61,9 @@ void ym2151_device::data_w(u8 data)
{
if (m_reset_state == 0)
return;
+ // vgmwrite hack
+ vgm_write(vgm_idx, 0x00, vgm_offset, data);
+
parent::data_w(data);
}
diff --git a/src/emu/emuopts.cpp b/src/emu/emuopts.cpp
index 8959c00da03..a515bb2db65 100644
--- a/src/emu/emuopts.cpp
+++ b/src/emu/emuopts.cpp
@@ -74,6 +74,7 @@ const options_entry emu_options::s_option_entries[] =
{ OPTION_MNGWRITE, nullptr, OPTION_STRING, "optional filename to write a MNG movie of the current session" },
{ OPTION_AVIWRITE, nullptr, OPTION_STRING, "optional filename to write an AVI movie of the current session" },
{ OPTION_WAVWRITE, nullptr, 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 (<width>x<height>) or 'auto' to use minimal size " },
{ OPTION_SNAPVIEW, "auto", OPTION_STRING, "snapshot/movie view - 'auto' for default, or 'native' for per-screen pixel-aspect views" },
diff --git a/src/emu/emuopts.h b/src/emu/emuopts.h
index 296e2e46590..b56cd08beef 100644
--- a/src/emu/emuopts.h
+++ b/src/emu/emuopts.h
@@ -60,6 +60,7 @@
#define OPTION_MNGWRITE "mngwrite"
#define OPTION_AVIWRITE "aviwrite"
#define OPTION_WAVWRITE "wavwrite"
+#define OPTION_VGMWRITE "vgmwrite"
#define OPTION_SNAPNAME "snapname"
#define OPTION_SNAPSIZE "snapsize"
#define OPTION_SNAPVIEW "snapview"
@@ -345,6 +346,7 @@ public:
const char *mng_write() const { return value(OPTION_MNGWRITE); }
const char *avi_write() const { return value(OPTION_AVIWRITE); }
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.cpp b/src/emu/machine.cpp
index b70ffb69931..8136d3b786b 100644
--- a/src/emu/machine.cpp
+++ b/src/emu/machine.cpp
@@ -29,6 +29,7 @@
#include "corestr.h"
#include "unzip.h"
+#include "vgmwrite.h"
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>
@@ -200,6 +201,9 @@ void running_machine::start()
for (device_t &device : device_enumerator(root_device()))
device.resolve_post_map();
+ // 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(&running_machine::reset_all_devices, this));
add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(&running_machine::stop_all_devices, this));
@@ -1040,6 +1044,9 @@ void running_machine::stop_all_devices()
// iterate over devices and stop them
for (device_t &device : device_enumerator(root_device()))
device.stop();
+
+ // stop VGM logging
+ vgm_stop();
}
diff --git a/src/lib/util/vgmwrite.cpp b/src/lib/util/vgmwrite.cpp
new file mode 100644
index 00000000000..8aef477ea77
--- /dev/null
+++ b/src/lib/util/vgmwrite.cpp
@@ -0,0 +1,2605 @@
+/*
+ vgmwrite.c
+
+ VGM output module
+
+ Written by Valley Bell
+
+*/
+#pragma GCC diagnostic ignored "-Wunused-function"
+
+#include "../../emu/emu.h"
+#include "../../emu/emuopts.h"
+#include "osdcomm.h"
+#include <wchar.h>
+#include "vgmwrite.h"
+
+#define DYNAMIC_HEADER_SIZE
+
+//#define print_info osd_printf_info
+//#define print_warn osd_printf_warning
+//#define print_err osd_printf_error
+#define print_info hMachine->logerror
+#define print_warn hMachine->logerror
+#define print_err hMachine->logerror
+
+typedef uint16_t gd3char_t; // UTF-16 character, for VGM GD3 tag
+
+static uint8_t LOG_VGM_FILE = 0x00;
+
+typedef struct _vgm_file_header VGM_HEADER;
+struct _vgm_file_header
+{
+ uint32_t fccVGM;
+ uint32_t lngEOFOffset;
+ uint32_t lngVersion;
+ uint32_t lngHzPSG;
+ uint32_t lngHz2413;
+ uint32_t lngGD3Offset;
+ uint32_t lngTotalSamples;
+ uint32_t lngLoopOffset;
+ uint32_t lngLoopSamples;
+ uint32_t lngRate;
+ uint16_t shtPSG_Feedback;
+ uint8_t bytPSG_SRWidth;
+ uint8_t bytPSG_Flags;
+ uint32_t lngHz2612;
+ uint32_t lngHz2151;
+ uint32_t lngDataOffset;
+ uint32_t lngHzSPCM;
+ uint32_t lngSPCMIntf;
+
+ uint32_t lngHzRF5C68;
+ uint32_t lngHz2203;
+ uint32_t lngHz2608;
+ uint32_t lngHz2610;
+ uint32_t lngHz3812;
+ uint32_t lngHz3526;
+ uint32_t lngHz8950;
+ uint32_t lngHz262;
+ uint32_t lngHz278B;
+ uint32_t lngHz271;
+ uint32_t lngHz280B;
+ uint32_t lngHzRF5C164;
+ uint32_t lngHzPWM;
+ uint32_t lngHzAY8910;
+ uint8_t lngAYType;
+ uint8_t lngAYFlags;
+ uint8_t lngAYFlagsYM2203;
+ uint8_t lngAYFlagsYM2608;
+ uint8_t bytModifiers[0x04];
+
+ uint32_t lngHzGBDMG; // part of the LR35902 (GB Main CPU)
+ uint32_t lngHzNESAPU; // part of the N2A03 (NES Main CPU)
+ uint32_t lngHzMultiPCM;
+ uint32_t lngHzUPD7759;
+ uint32_t lngHzOKIM6258;
+ uint8_t bytOKI6258Flags;
+ uint8_t bytK054539Flags;
+ uint8_t bytC140Type;
+ uint8_t bytReservedFlags;
+ uint32_t lngHzOKIM6295;
+ uint32_t lngHzK051649;
+ uint32_t lngHzK054539;
+ uint32_t lngHzHuC6280;
+ uint32_t lngHzC140;
+ uint32_t lngHzK053260;
+ uint32_t lngHzPokey;
+ uint32_t lngHzQSound;
+ uint32_t lngHzSCSP;
+ uint32_t lngExtraOfs;
+
+ uint32_t lngHzWSwan;
+ uint32_t lngHzVSU;
+ uint32_t lngHzSAA1099;
+ uint32_t lngHzES5503;
+ uint32_t lngHzES5506;
+ uint8_t bytES5503Chns;
+ uint8_t bytES5506Chns;
+ uint8_t bytC352ClkDiv;
+ uint8_t bytESReserved;
+ uint32_t lngHzX1_010;
+ uint32_t lngHzC352;
+
+ uint32_t lngHzGA20;
+ uint8_t bytReserved[0x1C];
+}; // -> 0x100 Bytes
+typedef struct _vgm_gd3_tag GD3_TAG;
+struct _vgm_gd3_tag
+{
+ uint32_t fccGD3;
+ uint32_t lngVersion;
+ uint32_t lngTagLength;
+ gd3char_t strTrackNameE[0x70];
+ gd3char_t strTrackNameJ[0x10]; // Japanese Names are not used
+ gd3char_t strGameNameE[0x70];
+ gd3char_t strGameNameJ[0x10];
+ gd3char_t strSystemNameE[0x30];
+ gd3char_t strSystemNameJ[0x10];
+ gd3char_t strAuthorNameE[0x30];
+ gd3char_t strAuthorNameJ[0x10];
+ gd3char_t strReleaseDate[0x10];
+ gd3char_t strCreator[0x20];
+ gd3char_t strNotes[0x50];
+}; // -> 0x200 Bytes
+
+typedef struct _vgm_rom_data_block VGM_ROM_DATA;
+struct _vgm_rom_data_block
+{
+ uint8_t Type;
+ uint8_t dstart_msb;
+ uint32_t DataSize;
+ const void* Data;
+};
+typedef struct _vgm_rom_init_command VGM_INIT_CMD;
+struct _vgm_rom_init_command
+{
+ uint8_t CmdLen;
+ uint8_t Data[0x08];
+};
+typedef struct _vgm_file_inf VGM_INF;
+struct _vgm_file_inf
+{
+ FILE* hFile;
+ VGM_HEADER Header;
+ uint8_t WroteHeader;
+ uint32_t HeaderBytes;
+ uint32_t BytesWrt;
+ uint32_t SmplsWrt;
+ uint32_t EvtDelay;
+
+ uint32_t DataCount;
+ VGM_ROM_DATA DataBlk[0x20];
+ //uint32_t CmdAlloc;
+ uint32_t CmdCount;
+ VGM_INIT_CMD Commands[0x100];
+
+ uint8_t NesMemEmpty;
+ uint8_t NesMem[0x4000];
+};
+typedef struct _vgm_chip VGM_CHIP;
+typedef struct _vgm_chip_pcmcache VGM_PCMCACHE;
+struct _vgm_chip
+{
+ uint16_t VgmID;
+ uint8_t ChipType;
+ uint8_t HadWrite;
+ VGM_PCMCACHE* PCMCache;
+};
+struct _vgm_chip_pcmcache
+{
+ uint32_t Start;
+ uint32_t Next;
+ uint32_t Pos;
+ uint32_t CacheSize;
+ uint8_t* CacheData;
+};
+
+
+class VGMTimer
+{
+private:
+ emu_timer* m_timer;
+
+ TIMER_CALLBACK_MEMBER(vgmfile_callback);
+
+public:
+ VGMTimer(running_machine &machine)
+ {
+ m_timer = machine.scheduler().timer_alloc(timer_expired_delegate(FUNC(VGMTimer::vgmfile_callback), this));
+ m_timer->adjust(attotime::from_hz(44100), 0, attotime::from_hz(44100));
+ }
+};
+
+#define MAX_VGM_FILES 0x10
+#define MAX_VGM_CHIPS 0x80
+static char vgm_namebase[0x80] = "vgmwrite";
+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;
+static VGMTimer* VgmTimerObj;
+static running_machine* hMachine = nullptr;
+
+// Function Prototypes
+static size_t str2utf16(gd3char_t* dststr, const char* srcstr, size_t max);
+static size_t wcs2utf16(gd3char_t* dststr, const wchar_t* srcstr, size_t max);
+static size_t utf16len(const gd3char_t* str);
+static void vgm_header_postwrite(uint16_t vgm_id);
+static void vgm_header_sizecheck(uint16_t vgm_id, uint32_t MinVer, uint32_t MinSize);
+static void vgm_header_clear(uint16_t vgm_id);
+static void vgm_setup_pcmcache(VGM_PCMCACHE* TempPC, uint32_t Size);
+static void vgm_close(uint16_t vgm_id);
+static void vgm_write_delay(uint16_t vgm_id);
+static uint8_t vgm_nes_ram_check(VGM_INF* VI, uint32_t datasize, uint32_t* value1, uint32_t* value2, const uint8_t* data);
+static void vgm_flush_pcm(VGM_CHIP* VC);
+
+
+static size_t str2utf16(gd3char_t* dststr, const char* srcstr, size_t max)
+{
+ size_t srclen;
+ size_t wlen;
+ wchar_t* wstr;
+
+ srclen = strlen(srcstr);
+ wstr = (wchar_t*)calloc(srclen, sizeof(wchar_t));
+
+ mbstowcs(wstr, srcstr, srclen + 1);
+ wlen = wcs2utf16(dststr, wstr, max);
+
+ free(wstr);
+ return wlen;
+}
+
+static size_t wcs2utf16(gd3char_t* dststr, const wchar_t* srcstr, size_t max)
+{
+ size_t wlen;
+
+ max --;
+ wlen = wcslen(srcstr);
+ if (wlen > max)
+ wlen = max;
+
+ if (sizeof(gd3char_t) == sizeof(wchar_t))
+ {
+ wcsncpy((wchar_t*)dststr, srcstr, wlen + 1);
+ return wlen;
+ }
+ else
+ {
+ size_t idxSrc;
+ size_t idxDst;
+
+ // return number of copied bytes
+ idxDst = 0;
+ for (idxSrc = 0; idxSrc < wlen && idxDst < max; idxSrc ++)
+ {
+ if (srcstr[idxSrc] >= 0x10000)
+ {
+#define HI_SURROGATE_START 0xD800
+#define LO_SURROGATE_START 0xDC00
+ // surrogate conversion from http://unicode.org/faq/utf_bom.html
+ gd3char_t X, W;
+
+ X = (gd3char_t)srcstr[idxSrc];
+ W = ((srcstr[idxSrc] >> 16) & 0x1F) - 1;
+ gd3char_t HiSurrogate = HI_SURROGATE_START | (W << 6) | (X >> 10);
+ gd3char_t LoSurrogate = LO_SURROGATE_START | (X & 0x3FF);
+
+ dststr[idxDst] = HiSurrogate; idxDst ++;
+ dststr[idxDst] = LoSurrogate; idxDst ++;
+ }
+ else
+ {
+ // just truncate/copy
+ dststr[idxDst] = (gd3char_t)srcstr[idxSrc]; idxDst ++;
+ }
+ }
+ dststr[idxDst] = (gd3char_t)L'\0';
+ return idxDst;
+ }
+}
+
+static size_t utf16len(const gd3char_t* str)
+{
+ const gd3char_t* end_ptr = str;
+
+ while(*end_ptr != 0)
+ end_ptr ++;
+ return end_ptr - str;
+}
+
+void vgm_start(running_machine &machine)
+{
+ uint16_t curvgm;
+
+ LOG_VGM_FILE = (uint8_t)machine.options().vgm_write();
+ hMachine = &machine;
+ printf("VGM logging mode: %02X\n", LOG_VGM_FILE);
+
+ // Reset all files
+ for (curvgm = 0x00; curvgm < MAX_VGM_FILES; curvgm ++)
+ {
+ VgmFile[curvgm].hFile = nullptr;
+ 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 = nullptr;
+ }
+
+ // start the timer
+ // (done here because it makes save states with vgmwrite off compatible with
+ // saves that have it on)
+ VgmTimerObj = new VGMTimer(machine);
+ hMachine = &machine;
+
+ if (! LOG_VGM_FILE)
+ return;
+
+ VgmTag.fccGD3 = 0x20336447; // 'Gd3 '
+ VgmTag.lngVersion = 0x00000100;
+ wcs2utf16(VgmTag.strTrackNameE, L"", 0x70);
+ wcs2utf16(VgmTag.strTrackNameJ, L"", 0x10);
+ wcs2utf16(VgmTag.strGameNameE, L"", 0x70);
+ wcs2utf16(VgmTag.strGameNameJ, L"", 0x10);
+ wcs2utf16(VgmTag.strSystemNameE, L"", 0x30);
+ wcs2utf16(VgmTag.strSystemNameJ, L"", 0x10);
+ wcs2utf16(VgmTag.strAuthorNameE, L"", 0x30);
+ wcs2utf16(VgmTag.strAuthorNameJ, L"", 0x10);
+ wcs2utf16(VgmTag.strReleaseDate, L"", 0x10);
+ wcs2utf16(VgmTag.strCreator, L"", 0x20);
+ wcs2utf16(VgmTag.strNotes, L"", 0x50);
+
+ printf("VGM logging started ...\n");
+
+ return;
+}
+
+void vgm_stop(void)
+{
+ uint16_t curchip;
+ uint16_t chip_unused;
+ uint16_t curvgm;
+ uint32_t 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_WSWAN:
+ VH->lngHzWSwan &= clock_mask;
+ break;
+ case VGMC_VSU:
+ VH->lngHzVSU &= clock_mask;
+ break;
+ case VGMC_SAA1099:
+ VH->lngHzSAA1099 &= clock_mask;
+ break;
+ case VGMC_ES5503:
+ VH->lngHzES5503 &= clock_mask;
+ if (! clock_mask)
+ VH->bytES5503Chns = 0x00;
+ break;
+ case VGMC_ES5506:
+ VH->lngHzES5506 &= clock_mask;
+ if (! clock_mask)
+ VH->bytES5506Chns = 0x00;
+ break;
+ case VGMC_X1_010:
+ VH->lngHzX1_010 &= clock_mask;
+ break;
+ case VGMC_C352:
+ VH->lngHzC352 &= clock_mask;
+ if (! clock_mask)
+ VH->bytC352ClkDiv = 0x00;
+ break;
+ case VGMC_GA20:
+ VH->lngHzGA20 &= clock_mask;
+ break;
+ // case VGMC_OKIM6376:
+ // VH->lngHzOKIM6376 &= clock_mask;
+ // break;
+ }
+ }
+ if (VC->PCMCache != nullptr)
+ {
+ VC->PCMCache->CacheSize = 0x00;
+ free(VC->PCMCache->CacheData);
+ VC->PCMCache->CacheData = nullptr;
+ VC->PCMCache = nullptr;
+ }
+ }
+ if (chip_unused)
+ print_info("Header Data of %hu unused Chips removed.\n", chip_unused);
+
+ for (curvgm = 0x00; curvgm < MAX_VGM_FILES; curvgm ++)
+ {
+ if (VgmFile[curvgm].hFile != nullptr)
+ vgm_close(curvgm);
+ }
+ delete VgmTimerObj; VgmTimerObj = nullptr;
+ print_info("VGM stopped.\n");
+
+ return;
+}
+
+TIMER_CALLBACK_MEMBER(VGMTimer::vgmfile_callback)
+{
+ uint16_t curvgm;
+
+ if (! LOG_VGM_FILE)
+ return;
+
+ for (curvgm = 0x00; curvgm < MAX_VGM_FILES; curvgm ++)
+ {
+ if (VgmFile[curvgm].hFile != nullptr)
+ VgmFile[curvgm].EvtDelay ++;
+ }
+
+ return;
+}
+
+static void vgm_header_postwrite(uint16_t vgm_id)
+{
+ VGM_INF* VI;
+ VGM_HEADER* Header;
+ VGM_ROM_DATA* VR;
+ VGM_INIT_CMD* VC;
+ uint32_t curcmd;
+ uint32_t blocksize;
+ uint32_t templng;
+
+ if (VgmFile[vgm_id].WroteHeader)
+ return;
+
+ VI = &VgmFile[vgm_id];
+ Header = &VI->Header;
+
+ print_info("Writing VGM Header ...\n");
+ 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 != nullptr)
+ 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;
+ templng |= (VR->dstart_msb << 24);
+ fwrite(&templng, 0x04, 0x01, VI->hFile); // Data Base Address
+ if (VR->Data != nullptr)
+ 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_t vgm_id, uint32_t MinVer, uint32_t MinSize)
+{
+ VGM_INF* VI;
+ VGM_HEADER* Header;
+
+ if (VgmFile[vgm_id].hFile == nullptr)
+ 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_t vgm_id)
+{
+ VGM_INF* VI;
+ VGM_HEADER* Header;
+
+ if (VgmFile[vgm_id].hFile == nullptr)
+ 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_t vgm_open(uint8_t chip_type, int clock)
+{
+ uint16_t chip_id;
+ uint16_t chip_file;
+ uint16_t curvgm;
+ uint32_t chip_val;
+ char vgm_name[0x20];
+ uint8_t use_two;
+
+ printf("vgm_open - Chip Type %d, Clock %d\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_ES5506)
+ return 0xFFFF; // logging needs to be reworked for this one
+ if (chip_type == VGMC_SCSP)
+ return 0xFFFF; // timing is horribly off (see Fighting Vipers)
+ else if (chip_type == VGMC_WSWAN)
+ return 0xFFFF; // WonderSwan emulation is horrible in general - even the logs are screwed
+ }
+
+ chip_file = 0xFFFF;
+ for (curvgm = 0x00; curvgm < MAX_VGM_FILES; curvgm ++)
+ {
+ if (VgmFile[curvgm].hFile == nullptr)
+ continue;
+
+ 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;
+ 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_WSWAN:
+ chip_val = VgmFile[curvgm].Header.lngHzWSwan;
+ break;
+ case VGMC_VSU:
+ chip_val = VgmFile[curvgm].Header.lngHzVSU;
+ break;
+ case VGMC_SAA1099:
+ chip_val = VgmFile[curvgm].Header.lngHzSAA1099;
+ break;
+ case VGMC_ES5503:
+ chip_val = VgmFile[curvgm].Header.lngHzES5503;
+ break;
+ case VGMC_ES5506:
+ chip_val = VgmFile[curvgm].Header.lngHzES5506;
+ break;
+ case VGMC_X1_010:
+ chip_val = VgmFile[curvgm].Header.lngHzX1_010;
+ break;
+ case VGMC_C352:
+ chip_val = VgmFile[curvgm].Header.lngHzC352;
+ use_two = 0x00;
+ break;
+ case VGMC_GA20:
+ chip_val = VgmFile[curvgm].Header.lngHzGA20;
+ break;
+ // case VGMC_OKIM6376:
+ // chip_val = VgmFile[curvgm].Header.lngHzOKIM6376;
+ // use_two = 0x00;
+ // break;
+ default:
+ return 0xFFFF; // unknown chip - don't log
+ }
+ if (! chip_val)
+ {
+ chip_file = curvgm;
+ break;
+ }
+ else if (use_two)
+ {
+ if (! (chip_val & 0x40000000) && (LOG_VGM_FILE & 0x01))
+ {
+ if (clock != chip_val)
+ print_warn("VGM Log: Warning - 2-chip mode, but chip clocks different!\n");
+ chip_file = curvgm;
+ clock = 0x40000000 | chip_val;
+ chip_type |= 0x80;
+ break;
+ }
+ }
+ } // end for(curvgm)
+ if (chip_file == 0xFFFF)
+ {
+ for (curvgm = 0x00; curvgm < MAX_VGM_FILES; curvgm ++)
+ {
+ if (VgmFile[curvgm].hFile == nullptr)
+ {
+ sprintf(vgm_name, "%s%hX.vgm", vgm_namebase, curvgm);
+ print_info("Opening %s ...\t", vgm_name);
+ VgmFile[curvgm].hFile = fopen(vgm_name, "wb");
+ if (VgmFile[curvgm].hFile)
+ {
+ print_info("OK\n");
+ chip_file = curvgm;
+ VgmFile[curvgm].BytesWrt = 0;
+ VgmFile[curvgm].SmplsWrt = 0;
+ VgmFile[curvgm].EvtDelay = 0;
+ vgm_header_clear(curvgm);
+ }
+ else
+ {
+ print_info("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 = nullptr;
+
+ 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_WSWAN:
+ VgmFile[chip_file].Header.lngHzWSwan = clock;
+ break;
+ case VGMC_VSU:
+ VgmFile[chip_file].Header.lngHzVSU = clock;
+ break;
+ case VGMC_SAA1099:
+ VgmFile[chip_file].Header.lngHzSAA1099 = clock;
+ break;
+ case VGMC_ES5503:
+ VgmFile[chip_file].Header.lngHzES5503 = clock;
+ VgmChip[chip_id].PCMCache = &VgmPCache[chip_file];
+ vgm_setup_pcmcache(VgmChip[chip_id].PCMCache, 0x1000);
+ break;
+ case VGMC_ES5506:
+ VgmFile[chip_file].Header.lngHzES5506 = clock;
+ break;
+ case VGMC_X1_010:
+ VgmFile[chip_file].Header.lngHzX1_010 = clock;
+ break;
+ case VGMC_C352:
+ VgmFile[chip_file].Header.lngHzC352 = clock;
+ break;
+ case VGMC_GA20:
+ VgmFile[chip_file].Header.lngHzGA20 = clock;
+ 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;
+ case VGMC_WSWAN:
+ case VGMC_VSU:
+ case VGMC_SAA1099:
+ case VGMC_ES5503:
+ case VGMC_ES5506:
+ case VGMC_X1_010:
+ case VGMC_C352:
+ vgm_header_sizecheck(chip_file, 0x00000171, 0xE0);
+ break;
+ case VGMC_GA20:
+ vgm_header_sizecheck(chip_file, 0x00000171, 0x100);
+ break;
+ }
+
+ return chip_id;
+}
+
+static void vgm_setup_pcmcache(VGM_PCMCACHE* TempPC, uint32_t Size)
+{
+ TempPC->CacheSize = Size;
+ if (TempPC->CacheData != nullptr)
+ free(TempPC->CacheData);
+ TempPC->CacheData = (uint8_t*)malloc(Size);
+
+ return;
+}
+
+void vgm_header_set(uint16_t chip_id, uint8_t attr, uint32_t data)
+{
+ VGM_HEADER* VH;
+ uint8_t 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_t)data;
+ break;
+ case 0x03: // Feedback Pattern (White Noise Tap #2)
+ // must be called after #1
+ VH->shtPSG_Feedback |= (uint16_t)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:
+ print_info("AY8910: Resistor Load %hu = %u\n", attr & 0x0F, data);
+ break;
+ }
+ break;
+ case VGMC_GBSOUND:
+ break;
+ case VGMC_NESAPU:
+ switch(attr)
+ {
+ case 0x00: // FDS Enable
+ VH->lngHzNESAPU = (VH->lngHzNESAPU & 0x7FFFFFFF) | (data << 31);
+ break;
+ }
+ 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:
+ switch(attr)
+ {
+ case 0x00: // Chip Type (K051649/SCC or K052539/SCC+)
+ VH->lngHzK051649 = (VH->lngHzK051649 & 0x7FFFFFFF) | (data << 31);
+ break;
+ }
+ 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_WSWAN:
+ break;
+ case VGMC_VSU:
+ break;
+ case VGMC_SAA1099:
+ break;
+ case VGMC_ES5503:
+ switch(attr)
+ {
+ case 0x00: // Reserved
+ break;
+ case 0x01: // Channels
+ VH->bytES5503Chns = data & 0xFF;
+ break;
+ }
+ break;
+ case VGMC_ES5506:
+ switch(attr)
+ {
+ case 0x00: // Chip Type (5505/5506)
+ VH->lngHzES5506 = (VH->lngHzES5506 & 0x7FFFFFFF) | (data << 31);
+ break;
+ case 0x01: // Channels
+ VH->bytES5506Chns = data & 0xFF;
+ break;
+ }
+ break;
+ case VGMC_X1_010:
+ break;
+ case VGMC_C352:
+ switch(attr)
+ {
+ case 0x00: // Reserved
+ break;
+ case 0x01: // Clock Divider
+ VH->bytC352ClkDiv = (data / 4) & 0xFF;
+ break;
+ }
+ break;
+ case VGMC_GA20:
+ break;
+// case VGMC_OKIM6376:
+// break;
+ }
+
+ return;
+}
+
+static void vgm_close(uint16_t 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 = nullptr;
+ 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(gd3char_t), utf16len(VgmTag.strTrackNameE) + 1, VI->hFile);
+ fwrite(VgmTag.strTrackNameJ, sizeof(gd3char_t), utf16len(VgmTag.strTrackNameJ) + 1, VI->hFile);
+ fwrite(VgmTag.strGameNameE, sizeof(gd3char_t), utf16len(VgmTag.strGameNameE) + 1, VI->hFile);
+ fwrite(VgmTag.strGameNameJ, sizeof(gd3char_t), utf16len(VgmTag.strGameNameJ) + 1, VI->hFile);
+ fwrite(VgmTag.strSystemNameE, sizeof(gd3char_t), utf16len(VgmTag.strSystemNameE) + 1, VI->hFile);
+ fwrite(VgmTag.strSystemNameJ, sizeof(gd3char_t), utf16len(VgmTag.strSystemNameJ) + 1, VI->hFile);
+ fwrite(VgmTag.strAuthorNameE, sizeof(gd3char_t), utf16len(VgmTag.strAuthorNameE) + 1, VI->hFile);
+ fwrite(VgmTag.strAuthorNameJ, sizeof(gd3char_t), utf16len(VgmTag.strAuthorNameJ) + 1, VI->hFile);
+ fwrite(VgmTag.strReleaseDate, sizeof(gd3char_t), utf16len(VgmTag.strReleaseDate) + 1, VI->hFile);
+ fwrite(VgmTag.strCreator, sizeof(gd3char_t), utf16len(VgmTag.strCreator) + 1, VI->hFile);
+ fwrite(VgmTag.strNotes, sizeof(gd3char_t), utf16len(VgmTag.strNotes) + 1, 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 = nullptr;
+
+ print_info("VGM %02hX closed. %u Bytes, %u Samples written.\n", vgm_id, VI->BytesWrt, VI->SmplsWrt);
+
+ return;
+}
+
+static void vgm_write_delay(uint16_t vgm_id)
+{
+ VGM_INF* VI;
+ uint16_t 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_t)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_t chip_id, uint8_t port, uint16_t r, uint8_t v)
+{
+ VGM_CHIP* VC;
+ VGM_INF* VI;
+ VGM_PCMCACHE* VPC;
+ int8_t cm; // "Cheat Mode" to support 2 instances of 1 chip within 1 file
+ uint16_t curchip;
+ VGM_INIT_CMD WriteCmd;
+ uint32_t 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 == nullptr)
+ 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;
+ }
+ }
+ }
+
+ WriteCmd.CmdLen = 0x00;
+ cm = (VC->ChipType & 0x80) ? 0x50 : 0x00;
+ switch(VC->ChipType & 0x7F) // Write the data
+ {
+ case VGMC_T6W28:
+ cm = ~port & 0x01;
+ port = 0x00;
+ // fall through
+ 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_WSWAN:
+ switch(port)
+ {
+ case 0x00:
+ WriteCmd.Data[0x00] = 0xBC;
+ WriteCmd.Data[0x01] = r | (VC->ChipType & 0x80);
+ WriteCmd.Data[0x02] = v;
+ WriteCmd.CmdLen = 0x03;
+ break;
+ case 0x01:
+ WriteCmd.Data[0x00] = 0xC6; // 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_VSU:
+ r |= (VC->ChipType & 0x80) << 8;
+ WriteCmd.Data[0x00] = 0xC7;
+ WriteCmd.Data[0x01] = (r & 0xFF00) >> 8; // Offset MSB
+ WriteCmd.Data[0x02] = (r & 0x00FF) >> 0; // Offset LSB
+ WriteCmd.Data[0x03] = v; // Data
+ WriteCmd.CmdLen = 0x04;
+ break;
+ case VGMC_SAA1099:
+ WriteCmd.Data[0x00] = 0xBD;
+ WriteCmd.Data[0x01] = r | (VC->ChipType & 0x80);
+ WriteCmd.Data[0x02] = v;
+ WriteCmd.CmdLen = 0x03;
+ break;
+ case VGMC_ES5503:
+ switch(port & 0x80)
+ {
+ case 0x00: // register write
+ r |= (VC->ChipType & 0x80) << 8;
+ WriteCmd.Data[0x00] = 0xD5;
+ WriteCmd.Data[0x01] = (r & 0xFF00) >> 8; // Chip Select
+ WriteCmd.Data[0x02] = (r & 0x00FF) >> 0; // Register
+ WriteCmd.Data[0x03] = v;
+ WriteCmd.CmdLen = 0x04;
+ break;
+ case 0x80: // RAM write
+ 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);
+ 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_ES5506:
+ switch(port & 0x80)
+ {
+ case 0x00: // 8-bit register write
+ WriteCmd.Data[0x00] = 0xBE;
+ WriteCmd.Data[0x01] = r | (VC->ChipType & 0x80);
+ WriteCmd.Data[0x02] = v;
+ WriteCmd.CmdLen = 0x03;
+ break;
+ case 0x80: // 16-bit register write
+ WriteCmd.Data[0x00] = 0xD6;
+ WriteCmd.Data[0x01] = v | (VC->ChipType & 0x80);
+ WriteCmd.Data[0x02] = (r & 0xFF00) >> 8; // Data MSB
+ WriteCmd.Data[0x03] = (r & 0x00FF) >> 0; // Data LSB
+ WriteCmd.CmdLen = 0x04;
+ break;
+ }
+ break;
+ case VGMC_X1_010:
+ r |= (VC->ChipType & 0x80) << 8;
+ WriteCmd.Data[0x00] = 0xC8;
+ WriteCmd.Data[0x01] = (r & 0xFF00) >> 8; // Offset MSB
+ WriteCmd.Data[0x02] = (r & 0x00FF) >> 0; // Offset LSB
+ WriteCmd.Data[0x03] = v;
+ WriteCmd.CmdLen = 0x04;
+ break;
+ /*case VGMC_C352:
+ if (port == 0x01)
+ {
+ v = 0x06/2; // flags register (channel 0)
+ r = 0xFFFF;
+ }
+ WriteCmd.Data[0x00] = 0xC9;
+ WriteCmd.Data[0x01] = v; // Register
+ WriteCmd.Data[0x02] = (r & 0xFF00) >> 8; // Data MSB
+ WriteCmd.Data[0x03] = (r & 0x00FF) >> 0; // Data LSB
+ WriteCmd.CmdLen = 0x04;
+ break;*/
+ case VGMC_C352:
+ port |= (VC->ChipType & 0x80);
+ WriteCmd.Data[0x00] = 0xE1;
+ WriteCmd.Data[0x01] = port; // Register MSB
+ WriteCmd.Data[0x02] = v; // Register LSB
+ WriteCmd.Data[0x03] = (r & 0xFF00) >> 8; // Data MSB
+ WriteCmd.Data[0x04] = (r & 0x00FF) >> 0; // Data LSB
+ WriteCmd.CmdLen = 0x05;
+ break;
+ case VGMC_GA20:
+ WriteCmd.Data[0x00] = 0xBF;
+ WriteCmd.Data[0x01] = r | (VC->ChipType & 0x80);
+ WriteCmd.Data[0x02] = v;
+ WriteCmd.CmdLen = 0x03;
+ 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;
+ case VGMC_OKIM6295:
+ if (r >= 0x08 && r <= 0x0F)
+ cm = 0x01; // OKIM6295 clock change and configuration
+ break;
+ }
+
+ if (cm && VI->CmdCount < 0x100)
+ {
+ VI->Commands[VI->CmdCount] = WriteCmd;
+ VI->CmdCount ++;
+ }
+ return;
+ }
+
+ if (VI->hFile != nullptr)
+ {
+ fwrite(WriteCmd.Data, 0x01, WriteCmd.CmdLen, VI->hFile);
+ VI->BytesWrt += WriteCmd.CmdLen;
+ }
+
+ return;
+}
+
+void vgm_write_large_data(uint16_t chip_id, uint8_t type, uint32_t datasize, uint32_t value1, uint32_t 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_t finalsize;
+ uint8_t blk_type;
+ uint8_t dstart_msb;
+
+ if (! LOG_VGM_FILE || chip_id == 0xFFFF)
+ return;
+ if (VgmChip[chip_id].ChipType == 0xFF)
+ return;
+
+ VI = &VgmFile[VgmChip[chip_id].VgmID];
+ if (VI->hFile == nullptr)
+ return;
+
+ blk_type = 0x00;
+ dstart_msb = 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_t*)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_WSWAN:
+ break;
+ case VGMC_VSU:
+ break;
+ case VGMC_SAA1099:
+ break;
+ case VGMC_ES5503:
+ switch(type)
+ {
+ case 0x00:
+ break;
+ case 0x01: // RAM Data
+ vgm_flush_pcm(&VgmChip[chip_id]);
+ blk_type = 0xE1; // Type: ES5503 RAM Data
+ break;
+ }
+ break;
+ case VGMC_ES5506:
+ // "type" tells us the actual region
+ blk_type = 0x90; // Type: ES5506 ROM Data
+ dstart_msb = type << 4;
+ break;
+ case VGMC_X1_010:
+ switch(type)
+ {
+ case 0x00:
+ break;
+ case 0x01: // ROM Data
+ blk_type = 0x91; // Type: X1-010 ROM Data
+ break;
+ }
+ break;
+ case VGMC_C352:
+ switch(type)
+ {
+ case 0x00:
+ break;
+ case 0x01: // ROM Data
+ blk_type = 0x92; // Type: C352 ROM Data
+ break;
+ }
+ break;
+ case VGMC_GA20:
+ switch(type)
+ {
+ case 0x00:
+ break;
+ case 0x01: // ROM Data
+ blk_type = 0x93; // Type: GA20 ROM Data
+ 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 == nullptr)
+ print_info("ROM Data %02X: (0x%X bytes) is NULL!\n", 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->dstart_msb = dstart_msb;
+ 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 == nullptr)
+ {
+ 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 == nullptr)
+ {
+ value1 = 0x00;
+ value2 = 0x00;
+ }
+ finalsize = 0x08 + value2;
+ finalsize |= (VgmChip[chip_id].ChipType & 0x80) << 24;
+ value1 |= (dstart_msb << 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 == nullptr)
+ {
+ 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_t vgm_nes_ram_check(VGM_INF* VI, uint32_t datasize, uint32_t* value1, uint32_t* value2, const uint8_t* data)
+{
+ uint16_t CurPos;
+ uint16_t DataStart;
+ uint16_t 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_t vgm_get_chip_idx(uint8_t chip_type, uint8_t 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_t 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_t SingleWrt;
+ uint8_t CmdType;
+ uint32_t finalsize;
+
+ VI = &VgmFile[VC->VgmID];
+ if (! LOG_VGM_FILE || VI->hFile == nullptr)
+ return;
+
+ VPC = VC->PCMCache;
+ if (VPC == nullptr || VPC->Start == 0xFFFFFFFF || ! VPC->Pos)
+ return;
+ //print_info("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;
+ case VGMC_ES5503:
+ CmdType = 0xE1;
+ 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;
+}
+
+void vgm_change_rom_data(uint32_t oldsize, const void* olddata, uint32_t newsize, const void* newdata)
+{
+ VGM_INF* VI;
+ VGM_ROM_DATA* VR;
+ uint16_t curvgm;
+ uint8_t curdblk;
+
+ print_info("Changing ROM Data (address %p, 0x%X bytes -> address %p, 0x%X bytes) ...\n",
+ olddata, oldsize, newdata, newsize);
+ for (curvgm = 0x00; curvgm < MAX_VGM_FILES; curvgm ++)
+ {
+ VI = &VgmFile[curvgm];
+ if (VI->hFile == nullptr)
+ continue;
+
+ for (curdblk = 0x00; curdblk < VI->DataCount; curdblk ++)
+ {
+ VR = &VI->DataBlk[curdblk];
+ if ((VR->DataSize & 0x7FFFFFFF) == oldsize && VR->Data == olddata)
+ {
+ print_info("Match found in VGM %02hX, Block Type %02X\n", curvgm, VR->Type);
+ VR->DataSize &= 0x80000000; // keep "chip select" bit
+ VR->DataSize |= newsize;
+ VR->Data = newdata;
+ }
+ }
+ }
+
+ return;
+}
+
+void vgm_dump_sample_rom(uint16_t chip_id, uint8_t type, memory_region* region)
+{
+ if (region == nullptr)
+ return;
+
+ print_info("VGM - Dumping ROM-Region %s: size 0x%X, ptr %p\n", region->name(), region->bytes(), region->base());
+ vgm_write_large_data(chip_id, type, region->bytes(), 0x00, 0x00, region->base());
+
+ return;
+}
+
+void vgm_dump_sample_rom(uint16_t chip_id, uint8_t type, address_space& space)
+{
+ uint32_t dataSize = space.addrmask() + 1;
+ const void* dataPtr = space.get_read_ptr(0);
+
+ print_info("VGM - Dumping ROM-Space %s: size 0x%X, space-ptr %p\n", space.name(), dataSize, dataPtr);
+ vgm_write_large_data(chip_id, type, dataSize, 0x00, 0x00, dataPtr);
+
+ return;
+}
diff --git a/src/lib/util/vgmwrite.h b/src/lib/util/vgmwrite.h
new file mode 100644
index 00000000000..3ea23ca2583
--- /dev/null
+++ b/src/lib/util/vgmwrite.h
@@ -0,0 +1,66 @@
+#pragma once
+
+#ifndef __VGMWRITE_H__
+#define __VGMWRITE_H__
+
+void vgm_start(running_machine &machine);
+void vgm_stop(void);
+uint16_t vgm_open(uint8_t chip_type, int clock);
+void vgm_header_set(uint16_t chip_id, uint8_t attr, uint32_t data);
+void vgm_write(uint16_t chip_id, uint8_t port, uint16_t r, uint8_t v);
+void vgm_write_large_data(uint16_t chip_id, uint8_t type, uint32_t datasize, uint32_t value1, uint32_t value2, const void* data);
+uint16_t vgm_get_chip_idx(uint8_t chip_type, uint8_t Num);
+void vgm_change_rom_data(uint32_t oldsize, const void* olddata, uint32_t newsize, const void* newdata);
+void vgm_dump_sample_rom(uint16_t chip_id, uint8_t type, memory_region* region);
+void vgm_dump_sample_rom(uint16_t chip_id, uint8_t type, address_space& space);
+
+// 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
+// v1.71
+#define VGMC_SCSP 0x20
+#define VGMC_WSWAN 0x21
+#define VGMC_VSU 0x22
+#define VGMC_SAA1099 0x23
+#define VGMC_ES5503 0x24
+#define VGMC_ES5506 0x25
+#define VGMC_X1_010 0x26
+#define VGMC_C352 0x27
+#define VGMC_GA20 0x28
+
+//#define VGMC_OKIM6376 0xFF
+#endif /* __VGMWRITE_H__ */
--
2.25.1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment