summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Olivier Galibert <galibert@pobox.com>2016-08-14 14:19:02 +0200
committer Olivier Galibert <galibert@pobox.com>2016-08-16 08:40:53 +0200
commit00c8a3b165794519f64852b971096793509f5f73 (patch)
tree726ab3dfa790e4a4c4423b4e77a386ddf9b90e0d
parentcb43e37d29da66bf7a937256b2b37bd7cd2e6757 (diff)
Preliminary VGM player [O. Galibert]
Use: ./mame vgmplay -bitb file.vgm/vgz Lots of chips missing, no visualization, no control of anything, no volume control either, and no sound shutdown on end of file.
-rw-r--r--scripts/target/mame/mame.lua3
-rw-r--r--scripts/target/mame/virtual.lua73
-rw-r--r--src/devices/sound/segapcm.cpp13
-rw-r--r--src/devices/sound/segapcm.h4
-rw-r--r--src/mame/drivers/vgmplay.cpp794
-rw-r--r--src/mame/mame.lst4
-rw-r--r--src/mame/virtual.flt1
-rw-r--r--src/mame/virtual.lst4
8 files changed, 886 insertions, 10 deletions
diff --git a/scripts/target/mame/mame.lua b/scripts/target/mame/mame.lua
index b7360486042..d58bfab4595 100644
--- a/scripts/target/mame/mame.lua
+++ b/scripts/target/mame/mame.lua
@@ -11,13 +11,16 @@
dofile("arcade.lua")
dofile("mess.lua")
+dofile("virtual.lua")
function createProjects_mame_mame(_target, _subtarget)
createProjects_mame_arcade(_target, _subtarget)
createProjects_mame_mess(_target, _subtarget)
+ createProjects_mame_virtual(_target, _subtarget)
end
function linkProjects_mame_mame(_target, _subtarget)
linkProjects_mame_arcade(_target, _subtarget)
linkProjects_mame_mess(_target, _subtarget)
+ linkProjects_mame_virtual(_target, _subtarget)
end
diff --git a/scripts/target/mame/virtual.lua b/scripts/target/mame/virtual.lua
new file mode 100644
index 00000000000..5bec4adb607
--- /dev/null
+++ b/scripts/target/mame/virtual.lua
@@ -0,0 +1,73 @@
+-- license:BSD-3-Clause
+-- copyright-holders:MAMEdev Team
+
+---------------------------------------------------------------------------
+--
+-- virtual.lua
+--
+-- Virtual target makefile
+--
+---------------------------------------------------------------------------
+
+--------------------------------------------------
+-- specify available CPU cores
+--------------------------------------------------
+
+
+--------------------------------------------------
+-- specify available sound cores; some of these are
+-- only for MAME and so aren't included
+--------------------------------------------------
+
+
+--------------------------------------------------
+-- specify available video cores
+--------------------------------------------------
+
+
+--------------------------------------------------
+-- specify available machine cores
+--------------------------------------------------
+
+
+--------------------------------------------------
+-- specify available bus cores
+--------------------------------------------------
+
+
+--------------------------------------------------
+-- this is the driver library that
+-- comprise the virtual drivers
+--------------------------------------------------
+function linkProjects_mame_virtual(_target, _subtarget)
+ links {
+ "virtual",
+ }
+end
+
+function createVirtualProjects(_target, _subtarget, _name)
+ project (_name)
+ targetsubdir(_target .."_" .. _subtarget)
+ kind (LIBTYPE)
+ uuid (os.uuid("drv-" .. _target .."_" .. _subtarget .. "_" .._name))
+ addprojectflags()
+ precompiledheaders()
+
+ includedirs {
+ MAME_DIR .. "src/osd",
+ MAME_DIR .. "src/emu",
+ MAME_DIR .. "src/devices",
+ MAME_DIR .. "src/mame",
+ MAME_DIR .. "src/lib",
+ MAME_DIR .. "src/lib/util",
+ MAME_DIR .. "src/lib/netlist",
+ MAME_DIR .. "3rdparty",
+ }
+end
+
+function createProjects_mame_virtual(_target, _subtarget)
+createVirtualProjects(_target, _subtarget, "virtual")
+files {
+ MAME_DIR .. "src/mame/drivers/vgmplay.cpp",
+}
+end
diff --git a/src/devices/sound/segapcm.cpp b/src/devices/sound/segapcm.cpp
index 555ee019b16..d0e7864dd1f 100644
--- a/src/devices/sound/segapcm.cpp
+++ b/src/devices/sound/segapcm.cpp
@@ -19,7 +19,7 @@ const device_type SEGAPCM = &device_creator<segapcm_device>;
segapcm_device::segapcm_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
: device_t(mconfig, SEGAPCM, "Sega PCM", tag, owner, clock, "segapcm", __FILE__),
device_sound_interface(mconfig, *this),
- m_rom(*this, DEVICE_SELF),
+ device_rom_interface(mconfig, *this, 21),
m_ram(nullptr),
m_bank(0),
m_bankshift(0),
@@ -35,7 +35,7 @@ segapcm_device::segapcm_device(const machine_config &mconfig, const char *tag, d
void segapcm_device::device_start()
{
- int mask, rom_mask;
+ int mask;
m_ram = std::make_unique<UINT8[]>(0x800);
@@ -46,10 +46,7 @@ void segapcm_device::device_start()
if (!mask)
mask = BANK_MASK7 >> 16;
- for(rom_mask = 1; rom_mask < m_rom.length(); rom_mask *= 2) { };
- rom_mask--;
-
- m_bankmask = mask & (rom_mask >> m_bankshift);
+ m_bankmask = mask & (0x1fffff >> m_bankshift);
m_stream = stream_alloc(0, 2, clock() / 128);
@@ -97,7 +94,7 @@ void segapcm_device::sound_stream_update(sound_stream &stream, stream_sample_t *
/* only process active channels */
if (!(regs[0x86]&1))
{
- const UINT8 *rom = m_rom + ((regs[0x86] & m_bankmask) << m_bankshift);
+ int offset = (regs[0x86] & m_bankmask) << m_bankshift;
UINT32 addr = (regs[0x85] << 16) | (regs[0x84] << 8) | m_low[ch];
UINT32 loop = (regs[0x05] << 16) | (regs[0x04] << 8);
UINT8 end = regs[6] + 1;
@@ -120,7 +117,7 @@ void segapcm_device::sound_stream_update(sound_stream &stream, stream_sample_t *
}
/* fetch the sample */
- v = rom[(addr >> 8) & m_rom.mask()] - 0x80;
+ v = read_byte(offset + (addr >> 8)) - 0x80;
/* apply panning and advance */
outputs[0][i] += v * (regs[2] & 0x7f);
diff --git a/src/devices/sound/segapcm.h b/src/devices/sound/segapcm.h
index 48474134b46..5861db66940 100644
--- a/src/devices/sound/segapcm.h
+++ b/src/devices/sound/segapcm.h
@@ -35,7 +35,8 @@
//**************************************************************************
class segapcm_device : public device_t,
- public device_sound_interface
+ public device_sound_interface,
+ public device_rom_interface
{
public:
segapcm_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
@@ -55,7 +56,6 @@ protected:
virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override;
private:
- required_region_ptr<UINT8> m_rom;
std::unique_ptr<UINT8[]> m_ram;
UINT8 m_low[16];
int m_bank;
diff --git a/src/mame/drivers/vgmplay.cpp b/src/mame/drivers/vgmplay.cpp
new file mode 100644
index 00000000000..4cd78fbac70
--- /dev/null
+++ b/src/mame/drivers/vgmplay.cpp
@@ -0,0 +1,794 @@
+// license:BSD-3-Clause
+// copyright-holders: Olivier Galibert
+
+// A "virtual" driver to play vgm files
+// Use with mame vgmplay -bitb file.vgm
+
+#include <zlib.h>
+
+#include "emu.h"
+#include "debugger.h"
+#include "imagedev/bitbngr.h"
+
+#include "sound/2612intf.h"
+#include "sound/ym2151.h"
+#include "sound/segapcm.h"
+
+class vgmplay_device : public cpu_device
+{
+public:
+ enum {
+ REG_SIZE = 0x00000000,
+ A_YM2612 = 0x00000010,
+ A_YM2151 = 0x00000020,
+ A_SEGAPCM = 0x00001000,
+ };
+
+ vgmplay_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ virtual UINT32 execute_min_cycles() const override;
+ virtual UINT32 execute_max_cycles() const override;
+ virtual UINT32 execute_input_lines() const override;
+ virtual void execute_run() override;
+ virtual void execute_set_input(int inputnum, int state) override;
+
+ virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const override;
+
+ virtual void state_import(const device_state_entry &entry) override;
+ virtual void state_export(const device_state_entry &entry) override;
+ virtual void state_string_export(const device_state_entry &entry, std::string &str) const override;
+
+ virtual UINT32 disasm_min_opcode_bytes() const override;
+ virtual UINT32 disasm_max_opcode_bytes() const override;
+ virtual offs_t disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options) override;
+
+ READ8_MEMBER(segapcm_rom_r);
+
+private:
+ struct rom_block {
+ offs_t start_address;
+ offs_t end_address;
+ std::unique_ptr<UINT8[]> data;
+
+ rom_block(rom_block &&) = default;
+ rom_block(offs_t start, offs_t end, UINT8 d[]) {
+ start_address = start;
+ end_address = end;
+ data = d;
+ }
+ };
+
+ enum { RESET, RUN, DONE };
+
+ address_space_config m_file_config, m_io_config;
+ address_space *m_file, *m_io;
+
+ int m_icount, m_state;
+
+ UINT32 m_pc;
+
+ std::list<rom_block> m_rom_blocks[0x80];
+ UINT32 m_rom_masks[0x80];
+
+ UINT8 rom_r(UINT8 type, offs_t offset);
+ UINT32 handle_data_block(UINT32 address);
+ void blocks_clear();
+};
+
+const device_type VGMPLAY = &device_creator<vgmplay_device>;
+
+class vgmplay_state : public driver_device
+{
+public:
+ vgmplay_state(const machine_config &mconfig, device_type type, const char *tag);
+
+ virtual void machine_start() override;
+
+ READ8_MEMBER(file_r);
+ READ8_MEMBER(file_size_r);
+
+private:
+ std::vector<UINT8> m_file_data;
+ required_device<bitbanger_device> m_file;
+ required_device<ym2612_device> m_ym2612;
+ required_device<ym2151_device> m_ym2151;
+ required_device<segapcm_device> m_segapcm;
+
+ UINT32 r32(int offset) const;
+};
+
+vgmplay_device::vgmplay_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ cpu_device(mconfig, VGMPLAY, "VGM Player engine", tag, owner, clock, "vgmplay", __FILE__),
+ m_file_config("file", ENDIANNESS_LITTLE, 8, 32),
+ m_io_config("io", ENDIANNESS_LITTLE, 8, 32)
+{
+}
+
+void vgmplay_device::device_start()
+{
+ m_icountptr = &m_icount;
+ m_file = &space(AS_PROGRAM);
+ m_io = &space(AS_IO);
+
+ save_item(NAME(m_pc));
+
+ state_add(STATE_GENPC, "GENPC", m_pc).noshow();
+ state_add(STATE_GENPCBASE, "GENPCBASE", m_pc).noshow();
+}
+
+void vgmplay_device::device_reset()
+{
+ m_pc = 0;
+ m_state = RESET;
+ blocks_clear();
+}
+
+UINT32 vgmplay_device::execute_min_cycles() const
+{
+ return 0;
+}
+
+UINT32 vgmplay_device::execute_max_cycles() const
+{
+ return 65536;
+}
+
+UINT32 vgmplay_device::execute_input_lines() const
+{
+ return 0;
+}
+
+void vgmplay_device::blocks_clear()
+{
+ for(int i = 0; i < 128; i++) {
+ m_rom_blocks[i].clear();
+ m_rom_masks[i] = 0;
+ }
+}
+
+UINT32 vgmplay_device::handle_data_block(UINT32 address)
+{
+ UINT32 size = m_file->read_dword(m_pc+3);
+ UINT8 type = m_file->read_byte(m_pc+2);
+ if(type >= 0x80) {
+ UINT32 rs = m_file->read_dword(m_pc+7);
+ UINT32 start = m_file->read_dword(m_pc+11);
+ UINT8 *block = new UINT8[size - 8];
+ for(UINT32 i=0; i<size-8; i++)
+ block[i] = m_file->read_byte(m_pc+15+i);
+ m_rom_blocks[type - 0x80].emplace_front(start, start+size-9, block);
+
+ UINT32 mask = rs-1;
+ mask |= mask >> 1;
+ mask |= mask >> 2;
+ mask |= mask >> 4;
+ mask |= mask >> 8;
+ mask |= mask >> 16;
+ m_rom_masks[type - 0x80] = mask;
+ } else
+ logerror("ignored block size %x type %02x\n", size, type);
+ return 7+size;
+}
+
+void vgmplay_device::execute_run()
+{
+ while(m_icount > 0) {
+ switch(m_state) {
+ case RESET: {
+ UINT32 size = m_io->read_dword(REG_SIZE);
+ if(!size) {
+ m_pc = 0;
+ m_state = DONE;
+ break;
+ }
+ UINT32 version = m_file->read_dword(8);
+ m_pc = version < 0x150 ? 0x40 : 0x34 + m_file->read_dword(0x34);
+ m_state = RUN;
+ break;
+ }
+ case RUN: {
+ if(machine().debug_flags & DEBUG_FLAG_ENABLED)
+ debugger_instruction_hook(this, m_pc);
+ UINT8 code = m_file->read_byte(m_pc);
+ switch(code) {
+ case 0x4f:
+ logerror("ignored psg 06\n");
+ m_pc += 2;
+ break;
+
+ case 0x50:
+ logerror("ignored psg\n");
+ m_pc += 2;
+ break;
+
+ case 0x52:
+ m_io->write_byte(A_YM2612+0, m_file->read_byte(m_pc+1));
+ m_io->write_byte(A_YM2612+1, m_file->read_byte(m_pc+2));
+ m_pc += 3;
+ break;
+
+ case 0x53:
+ m_io->write_byte(A_YM2612+2, m_file->read_byte(m_pc+1));
+ m_io->write_byte(A_YM2612+3, m_file->read_byte(m_pc+2));
+ m_pc += 3;
+ break;
+
+ case 0x54:
+ m_io->write_byte(A_YM2151+0, m_file->read_byte(m_pc+1));
+ m_io->write_byte(A_YM2151+1, m_file->read_byte(m_pc+2));
+ m_pc += 3;
+ break;
+
+ case 0x61: {
+ UINT32 duration = m_file->read_word(m_pc+1);
+ m_icount -= duration;
+ m_pc += 3;
+ break;
+ }
+
+ case 0x62:
+ m_icount -= 735;
+ m_pc++;
+ break;
+
+ case 0x63:
+ m_icount -= 882;
+ m_pc++;
+ break;
+
+ case 0x66:
+ m_state = DONE;
+ break;
+
+ case 0x67:
+ m_pc += handle_data_block(m_pc);
+ break;
+
+ case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77:
+ case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7c: case 0x7d: case 0x7e: case 0x7f:
+ m_icount -= 1+(code & 0xf);
+ m_pc += 1;
+ break;
+
+ case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87:
+ case 0x88: case 0x89: case 0x8a: case 0x8b: case 0x8c: case 0x8d: case 0x8e: case 0x8f:
+ logerror("ignored ym2612 dac from databank\n");
+ m_pc += 1;
+ m_icount -= code & 0xf;
+ break;
+
+ case 0xc0:
+ m_io->write_byte(A_SEGAPCM + (m_file->read_word(m_pc+1) & 0x7ff), m_file->read_byte(m_pc+3));
+ m_pc += 4;
+ break;
+
+ case 0xe0:
+ logerror("ignored ym2612 offset\n");
+ m_pc += 5;
+ break;
+
+ default:
+ logerror("unhandled code %02x\n", code);
+ m_state = DONE;
+ m_icount = 0;
+ break;
+ }
+ break;
+ }
+ case DONE: {
+ static bool done = false;
+ if(!done) {
+ logerror("done\n");
+ done = true;
+ }
+ if(machine().debug_flags & DEBUG_FLAG_ENABLED)
+ debugger_instruction_hook(this, m_pc);
+ m_icount = 0;
+ break;
+ }
+ }
+ }
+}
+
+void vgmplay_device::execute_set_input(int inputnum, int state)
+{
+}
+
+const address_space_config *vgmplay_device::memory_space_config(address_spacenum spacenum) const
+{
+ switch(spacenum) {
+ case AS_PROGRAM: return &m_file_config;
+ case AS_IO: return &m_io_config;
+ default: return nullptr;
+ }
+}
+
+void vgmplay_device::state_import(const device_state_entry &entry)
+{
+}
+
+void vgmplay_device::state_export(const device_state_entry &entry)
+{
+}
+
+void vgmplay_device::state_string_export(const device_state_entry &entry, std::string &str) const
+{
+}
+
+UINT32 vgmplay_device::disasm_min_opcode_bytes() const
+{
+ return 1;
+}
+
+UINT32 vgmplay_device::disasm_max_opcode_bytes() const
+{
+ return 9;
+}
+
+offs_t vgmplay_device::disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options)
+{
+ switch(oprom[0]) {
+ case 0x4f:
+ sprintf(buffer, "psg r06 = %02x", oprom[1]);
+ return 2 | DASMFLAG_SUPPORTED;
+
+ case 0x50:
+ sprintf(buffer, "psg write %02x", oprom[1]);
+ return 2 | DASMFLAG_SUPPORTED;
+
+ case 0x51:
+ sprintf(buffer, "ym2413 r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0x52:
+ sprintf(buffer, "ym2612.0 r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0x53:
+ sprintf(buffer, "ym2612.1 r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0x54:
+ sprintf(buffer, "ym2151 r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0x55:
+ sprintf(buffer, "ym2203 r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0x56:
+ sprintf(buffer, "ym2608.0 r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0x57:
+ sprintf(buffer, "ym2608.1 r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0x58:
+ sprintf(buffer, "ym2610.0 r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0x59:
+ sprintf(buffer, "ym2610.1 r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0x5a:
+ sprintf(buffer, "ym3812 r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0x5b:
+ sprintf(buffer, "ym3526 r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0x5c:
+ sprintf(buffer, "y8950 r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0x5d:
+ sprintf(buffer, "ymz280b r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0x5e:
+ sprintf(buffer, "ymf262.0 r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0x5f:
+ sprintf(buffer, "ymf262.1 r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0x61: {
+ UINT32 duration = oprom[1] | (oprom[2] << 8);
+ sprintf(buffer, "wait %d", duration);
+ return 3 | DASMFLAG_SUPPORTED;
+ }
+
+ case 0x62:
+ sprintf(buffer, "wait 735");
+ return 1 | DASMFLAG_SUPPORTED;
+
+ case 0x63:
+ sprintf(buffer, "wait 882");
+ return 1 | DASMFLAG_SUPPORTED;
+
+ case 0x66:
+ sprintf(buffer, "end");
+ return 1 | DASMFLAG_SUPPORTED;
+
+ case 0x67: {
+ static const char *const basic_types[8] = {
+ "ym2612 pcm",
+ "rf5c68 pcm",
+ "rf5c164 pcm",
+ "pwm pcm",
+ "okim6258 adpcm",
+ "huc6280 pcm",
+ "scsp pcm",
+ "nes apu dpcm"
+ };
+
+ static const char *const rom_types[20] = {
+ "sega pcm rom",
+ "ym2608 delta-t rom",
+ "ym2610 adpcm rom",
+ "ym2610 delta-t rom",
+ "ymf278b rom",
+ "ymf271 rom",
+ "ymz280b rom",
+ "ymf278b rom",
+ "y8950 delta-t rom",
+ "multipcm rom",
+ "upd7759 rom",
+ "okim6295 rom",
+ "k054539 rom",
+ "c140 rom",
+ "k053260 rom",
+ "qsound rom",
+ "es5505/es5506 rom",
+ "x1-010 rom",
+ "c352 rom",
+ "ga20 rom"
+ };
+
+ static const char *const ram_types[3] = {
+ "rf5c68 ram",
+ "rf5c164 ram",
+ "nes apu ram"
+ };
+
+ static const char *const ram2_types[2] = {
+ "scsp ram",
+ "es5503 ram"
+ };
+
+ UINT8 type = oprom[2];
+ UINT32 size = oprom[3] | (oprom[4] << 8) | (oprom[5] << 16) | (oprom[6] << 24);
+ if(type < 0x8)
+ sprintf(buffer, "data-block %x, %s", size, basic_types[type]);
+ else if(type < 0x40)
+ sprintf(buffer, "data-block %x, %02x", size, type);
+ else if(type < 0x48)
+ sprintf(buffer, "data-block %x comp., %s", size, basic_types[type & 0x3f]);
+ else if(type < 0x7f)
+ sprintf(buffer, "data-block %x comp., %02x", size, type & 0x3f);
+ else if(type < 0x80)
+ sprintf(buffer, "decomp-table %x, %02x/%02x", size, oprom[7], oprom[8]);
+ else if(type < 0x94)
+ sprintf(buffer, "data-block %x, %s", size, rom_types[type & 0x7f]);
+ else if(type < 0xc0)
+ sprintf(buffer, "data-block %x, rom %02x", size, type);
+ else if(type < 0x93)
+ sprintf(buffer, "data-block %x, %s", size, ram_types[type & 0x1f]);
+ else if(type < 0xe0)
+ sprintf(buffer, "data-block %x, ram %02x", size, type);
+ else if(type < 0xe2)
+ sprintf(buffer, "data-block %x, %s", size, ram2_types[type & 0x1f]);
+ else
+ sprintf(buffer, "data-block %x, ram %02x", size, type);
+ return (7+size) | DASMFLAG_SUPPORTED;
+ }
+
+ case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77:
+ case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7c: case 0x7d: case 0x7e: case 0x7f:
+ sprintf(buffer, "wait %d", 1+(oprom[0] & 0x0f));
+ return 1 | DASMFLAG_SUPPORTED;
+
+ case 0x80:
+ sprintf(buffer, "ym2612.0 r2a = rom++");
+ return 1 | DASMFLAG_SUPPORTED;
+
+ case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87:
+ case 0x88: case 0x89: case 0x8a: case 0x8b: case 0x8c: case 0x8d: case 0x8e: case 0x8f:
+ sprintf(buffer, "ym2612.0 r2a = rom++; wait %d", oprom[0] & 0xf);
+ return 1 | DASMFLAG_SUPPORTED;
+
+ case 0xa0:
+ sprintf(buffer, "ay8910 r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0xb0:
+ sprintf(buffer, "rf5c68 r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0xb1:
+ sprintf(buffer, "rf5c68 r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0xb2:
+ sprintf(buffer, "pwm r%x = %03x", oprom[1] >> 4, oprom[2] | ((oprom[1] & 0xf) << 8));
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0xb3:
+ sprintf(buffer, "dmg r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0xb4:
+ sprintf(buffer, "apu r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0xb5:
+ sprintf(buffer, "multipcm r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0xb6:
+ sprintf(buffer, "upd7759 r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0xb7:
+ sprintf(buffer, "okim6258 r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0xb8:
+ sprintf(buffer, "huc6280 r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0xb9:
+ sprintf(buffer, "k053260 r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0xba:
+ sprintf(buffer, "pokey r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0xbb:
+ sprintf(buffer, "rf5c68 r%02x = %02x", oprom[1], oprom[2]);
+ return 3 | DASMFLAG_SUPPORTED;
+
+ case 0xc0:
+ sprintf(buffer, "segapcm %04x = %02x", oprom[1] | (oprom[2] << 8), oprom[3]);
+ return 4 | DASMFLAG_SUPPORTED;
+
+ case 0xc1:
+ sprintf(buffer, "rf5c68 %04x = %02x", oprom[1] | (oprom[2] << 8), oprom[3]);
+ return 4 | DASMFLAG_SUPPORTED;
+
+ case 0xc2:
+ sprintf(buffer, "rf5c163 %04x = %02x", oprom[1] | (oprom[2] << 8), oprom[3]);
+ return 4 | DASMFLAG_SUPPORTED;
+
+ case 0xc3:
+ sprintf(buffer, "multipcm c%02x.off = %04x", oprom[1], oprom[2] | (oprom[3] << 8));
+ return 4 | DASMFLAG_SUPPORTED;
+
+ case 0xc4:
+ sprintf(buffer, "qsound %02x = %04x", oprom[3], oprom[2] | (oprom[1] << 8));
+ return 4 | DASMFLAG_SUPPORTED;
+
+ case 0xd0:
+ sprintf(buffer, "ymf278b r%02x.%02x = %02x", oprom[1], oprom[2], oprom[3]);
+ return 4 | DASMFLAG_SUPPORTED;
+
+ case 0xd1:
+ sprintf(buffer, "ymf271 r%02x.%02x = %02x", oprom[1], oprom[2], oprom[3]);
+ return 4 | DASMFLAG_SUPPORTED;
+
+ case 0xd2:
+ sprintf(buffer, "scc1 r%02x.%02x = %02x", oprom[1], oprom[2], oprom[3]);
+ return 4 | DASMFLAG_SUPPORTED;
+
+ case 0xd3:
+ sprintf(buffer, "k054539 r%02x.%02x = %02x", oprom[1], oprom[2], oprom[3]);
+ return 4 | DASMFLAG_SUPPORTED;
+
+ case 0xd4:
+ sprintf(buffer, "c140 r%02x.%02x = %02x", oprom[1], oprom[2], oprom[3]);
+ return 4 | DASMFLAG_SUPPORTED;
+
+ case 0xe0: {
+ UINT32 off = oprom[1] | (oprom[2] << 8) | (oprom[3] << 16) | (oprom[4] << 24);
+ sprintf(buffer, "ym2612 offset = %x", off);
+ return 5 | DASMFLAG_SUPPORTED;
+ }
+
+ default:
+ sprintf(buffer, "?? %02x", oprom[0]);
+ return 1 | DASMFLAG_SUPPORTED;
+ }
+}
+
+UINT8 vgmplay_device::rom_r(UINT8 type, offs_t offset)
+{
+ logerror("rom_r %02x, %08x\n", type, offset);
+ return 0;
+}
+
+READ8_MEMBER(vgmplay_device::segapcm_rom_r)
+{
+ return rom_r(0x80, offset);
+}
+
+vgmplay_state::vgmplay_state(const machine_config &mconfig, device_type type, const char *tag) :
+ driver_device(mconfig, type, tag),
+ m_file(*this, "file"),
+ m_ym2612(*this, "ym2612"),
+ m_ym2151(*this, "ym2151"),
+ m_segapcm(*this, "segapcm")
+{
+}
+
+UINT32 vgmplay_state::r32(int off) const
+{
+ if(off + 3 < int(m_file_data.size()))
+ return m_file_data[off] | (m_file_data[off+1] << 8) | (m_file_data[off+2] << 16) | (m_file_data[off+3] << 24);
+ return 0;
+}
+
+void vgmplay_state::machine_start()
+{
+ UINT32 size = 0;
+ if(m_file->exists()) {
+ size = m_file->length();
+ m_file_data.resize(size);
+ m_file->input(&m_file_data[0], size);
+
+ // Decompress gzip-compressed files (aka vgz)
+ if(m_file_data[0] == 0x1f && m_file_data[1] == 0x8b) {
+ std::vector<UINT8> decomp;
+ int bs = m_file_data.size();
+ decomp.resize(2*bs);
+ z_stream str;
+ str.zalloc = nullptr;
+ str.zfree = nullptr;
+ str.opaque = nullptr;
+ str.data_type = 0;
+ str.next_in = &m_file_data[0];
+ str.avail_in = m_file_data.size();
+ str.total_in = 0;
+ str.total_out = 0;
+ int err = inflateInit2(&str, 31);
+ if(err != Z_OK) {
+ logerror("gzip header but not a gzip file\n");
+ m_file_data.clear();
+ return;
+ }
+ do {
+ if(str.total_out >= decomp.size())
+ decomp.resize(decomp.size() + bs);
+ str.next_out = &decomp[str.total_out];
+ str.avail_out = decomp.size() - str.total_out;
+ err = inflate(&str, Z_SYNC_FLUSH);
+ } while(err == Z_OK);
+ if(err != Z_STREAM_END) {
+ logerror("broken gzip file\n");
+ m_file_data.clear();
+ return;
+ }
+ m_file_data.resize(str.total_out);
+ memcpy(&m_file_data[0], &decomp[0], str.total_out);
+ }
+
+ if(m_file_data.size() < 0x40 || r32(0) != 0x206d6756) {
+ logerror("Not a vgm/vgz file\n");
+ m_file_data.clear();
+ return;
+ }
+
+ UINT32 version = r32(8);
+ logerror("File version %x.%02x\n", version >> 8, version & 0xff);
+ if(r32(0x0c))
+ logerror("Warning: file requests an unsupported SN76489\n");
+ if(r32(0x10))
+ logerror("Warning: file requests an unsupported YM2413\n");
+ if(version <= 0x101 && r32(0x0c)) {
+ m_ym2612->set_unscaled_clock(r32(0x0c));
+ m_ym2151->set_unscaled_clock(r32(0x0c));
+ }
+ if(version >= 0x110 && r32(0x2c))
+ m_ym2612->set_unscaled_clock(r32(0x2c));
+ if(version >= 0x110 && r32(0x30))
+ m_ym2151->set_unscaled_clock(r32(0x30));
+ if(version >= 0x151 && r32(0x38))
+ m_segapcm->set_unscaled_clock(r32(0x38));
+ if(version >= 0x151 && r32(0x40))
+ logerror("Warning: file requests an unsupported RF5C68\n");
+ if(version >= 0x151 && r32(0x44))
+ logerror("Warning: file requests an unsupported YM2203\n");
+ if(version >= 0x151 && r32(0x48))
+ logerror("Warning: file requests an unsupported YM2608\n");
+ if(version >= 0x151 && r32(0x4c))
+ logerror("Warning: file requests an unsupported %s\n", r32(0x4c) & 0x80000000 ? "YM2610B" : "YM2610");
+ if(version >= 0x151 && r32(0x50))
+ logerror("Warning: file requests an unsupported YM3812\n");
+ if(version >= 0x151 && r32(0x54))
+ logerror("Warning: file requests an unsupported YM3526\n");
+ if(version >= 0x151 && r32(0x58))
+ logerror("Warning: file requests an unsupported Y8950\n");
+ if(version >= 0x151 && r32(0x5c))
+ logerror("Warning: file requests an unsupported YMF262\n");
+ if(version >= 0x151 && r32(0x60))
+ logerror("Warning: file requests an unsupported YMF278B\n");
+ if(version >= 0x151 && r32(0x64))
+ logerror("Warning: file requests an unsupported YMF271\n");
+ if(version >= 0x151 && r32(0x68))
+ logerror("Warning: file requests an unsupported YMZ280B\n");
+ if(version >= 0x151 && r32(0x6c))
+ logerror("Warning: file requests an unsupported RF5C164\n");
+ if(version >= 0x151 && r32(0x70))
+ logerror("Warning: file requests an unsupported PWM\n");
+ if(version >= 0x151 && r32(0x74))
+ logerror("Warning: file requests an unsupported AY8910\n");
+ }
+}
+
+READ8_MEMBER(vgmplay_state::file_r)
+{
+ if(offset < m_file_data.size())
+ return m_file_data[offset];
+ return 0;
+}
+
+READ8_MEMBER(vgmplay_state::file_size_r)
+{
+ UINT32 size = m_file_data.size();
+ return size >> (8*offset);
+}
+
+static INPUT_PORTS_START( vgmplay )
+INPUT_PORTS_END
+
+static ADDRESS_MAP_START( file_map, AS_PROGRAM, 8, vgmplay_state )
+ AM_RANGE(0x00000000, 0xffffffff) AM_READ(file_r)
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START( soundchips_map, AS_IO, 8, vgmplay_state )
+ AM_RANGE(vgmplay_device::REG_SIZE, vgmplay_device::REG_SIZE+3) AM_READ(file_size_r)
+ AM_RANGE(vgmplay_device::A_YM2612, vgmplay_device::A_YM2612+3) AM_DEVREADWRITE("ym2612", ym2612_device, read, write)
+ AM_RANGE(vgmplay_device::A_YM2151, vgmplay_device::A_YM2151+3) AM_DEVREADWRITE("ym2151", ym2151_device, read, write)
+ AM_RANGE(vgmplay_device::A_SEGAPCM, vgmplay_device::A_SEGAPCM+0x7ff) AM_DEVREADWRITE("segapcm", segapcm_device, sega_pcm_r, sega_pcm_w)
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START( segapcm_map, AS_0, 8, vgmplay_state )
+ AM_RANGE(0, 0x1fffff) AM_DEVREAD("vgmplay", vgmplay_device, segapcm_rom_r)
+ADDRESS_MAP_END
+
+static MACHINE_CONFIG_START( vgmplay, vgmplay_state )
+ MCFG_CPU_ADD("vgmplay", VGMPLAY, 44100)
+ MCFG_CPU_PROGRAM_MAP( file_map )
+ MCFG_CPU_IO_MAP( soundchips_map )
+
+ MCFG_DEVICE_ADD("file", BITBANGER, 0)
+
+ MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker")
+
+ MCFG_SOUND_ADD("ym2612", YM2612, 7670454)
+ MCFG_SOUND_ROUTE(0, "lspeaker", 1)
+ MCFG_SOUND_ROUTE(1, "rspeaker", 1)
+
+ MCFG_SOUND_ADD("ym2151", YM2151, 3579545)
+ MCFG_SOUND_ROUTE(0, "lspeaker", 1)
+ MCFG_SOUND_ROUTE(1, "rspeaker", 1)
+
+ MCFG_SOUND_ADD("segapcm", SEGAPCM, 4000000)
+ MCFG_SEGAPCM_BANK(BANK_512) // Should be configurable for yboard...
+ MCFG_DEVICE_ADDRESS_MAP(AS_0, segapcm_map)
+ MCFG_SOUND_ROUTE(0, "lspeaker", 1)
+ MCFG_SOUND_ROUTE(1, "rspeaker", 1)
+MACHINE_CONFIG_END
+
+ROM_START( vgmplay )
+ROM_END
+
+CONS( 2016, vgmplay, 0, 0, vgmplay, vgmplay, driver_device, 0, "MAME", "VGM player", 0)
+
diff --git a/src/mame/mame.lst b/src/mame/mame.lst
index 06fcad2cd1f..cb73fd2890f 100644
--- a/src/mame/mame.lst
+++ b/src/mame/mame.lst
@@ -37568,3 +37568,7 @@ ts1500 // Timex Sinclair 1500
zx80 // 1980 Sinclair ZX-80
zx81 // 1981 Sinclair ZX-81
zx97 //
+
+@source:vgmplay.cpp
+vgmplay
+
diff --git a/src/mame/virtual.flt b/src/mame/virtual.flt
new file mode 100644
index 00000000000..8373a776c00
--- /dev/null
+++ b/src/mame/virtual.flt
@@ -0,0 +1 @@
+vgmplay.cpp
diff --git a/src/mame/virtual.lst b/src/mame/virtual.lst
new file mode 100644
index 00000000000..27a3e40416c
--- /dev/null
+++ b/src/mame/virtual.lst
@@ -0,0 +1,4 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+@source:vgmplay.cpp
+vgmplay