// license:BSD-3-Clause // copyright-holders:Aaron Giles /*************************************************************************** MAME Compressed Hunks of Data file format ***************************************************************************/ #ifndef MAME_LIB_UTIL_CHD_H #define MAME_LIB_UTIL_CHD_H #pragma once #include "chdcodec.h" #include "hashing.h" #include "ioprocs.h" #include "osdcore.h" #include #include #include #include #include #include /*************************************************************************** Compressed Hunks of Data header format. All numbers are stored in Motorola (big-endian) byte ordering. ========================================================================= V1 header: [ 0] char tag[8]; // 'MComprHD' [ 8] uint32_t length; // length of header (including tag and length fields) [ 12] uint32_t version; // drive format version [ 16] uint32_t flags; // flags (see below) [ 20] uint32_t compression; // compression type [ 24] uint32_t hunksize; // 512-byte sectors per hunk [ 28] uint32_t totalhunks; // total # of hunks represented [ 32] uint32_t cylinders; // number of cylinders on hard disk [ 36] uint32_t heads; // number of heads on hard disk [ 40] uint32_t sectors; // number of sectors on hard disk [ 44] uint8_t md5[16]; // MD5 checksum of raw data [ 60] uint8_t parentmd5[16]; // MD5 checksum of parent file [ 76] (V1 header length) Flags: 0x00000001 - set if this drive has a parent 0x00000002 - set if this drive allows writes Compression types: CHDCOMPRESSION_NONE = 0 CHDCOMPRESSION_ZLIB = 1 V1 map format: [ 0] uint64_t offset : 44; // starting offset within the file [ 0] uint64_t length : 20; // length of data; if == hunksize, data is uncompressed ========================================================================= V2 header: [ 0] char tag[8]; // 'MComprHD' [ 8] uint32_t length; // length of header (including tag and length fields) [ 12] uint32_t version; // drive format version [ 16] uint32_t flags; // flags (see below) [ 20] uint32_t compression; // compression type [ 24] uint32_t hunksize; // seclen-byte sectors per hunk [ 28] uint32_t totalhunks; // total # of hunks represented [ 32] uint32_t cylinders; // number of cylinders on hard disk [ 36] uint32_t heads; // number of heads on hard disk [ 40] uint32_t sectors; // number of sectors on hard disk [ 44] uint8_t md5[16]; // MD5 checksum of raw data [ 60] uint8_t parentmd5[16]; // MD5 checksum of parent file [ 76] uint32_t seclen; // number of bytes per sector [ 80] (V2 header length) Flags and map format are same as V1 ========================================================================= V3 header: [ 0] char tag[8]; // 'MComprHD' [ 8] uint32_t length; // length of header (including tag and length fields) [ 12] uint32_t version; // drive format version [ 16] uint32_t flags; // flags (see below) [ 20] uint32_t compression; // compression type [ 24] uint32_t totalhunks; // total # of hunks represented [ 28] uint64_t logicalbytes; // logical size of the data (in bytes) [ 36] uint64_t metaoffset; // offset to the first blob of metadata [ 44] uint8_t md5[16]; // MD5 checksum of raw data [ 60] uint8_t parentmd5[16]; // MD5 checksum of parent file [ 76] uint32_t hunkbytes; // number of bytes per hunk [ 80] uint8_t sha1[20]; // SHA1 checksum of raw data [100] uint8_t parentsha1[20];// SHA1 checksum of parent file [120] (V3 header length) Flags are the same as V1 Compression types: CHDCOMPRESSION_NONE = 0 CHDCOMPRESSION_ZLIB = 1 CHDCOMPRESSION_ZLIB_PLUS = 2 V3 map format: [ 0] uint64_t offset; // starting offset within the file [ 8] uint32_t crc32; // 32-bit CRC of the uncompressed data [ 12] uint16_t length_lo; // lower 16 bits of length [ 14] uint8_t length_hi; // upper 8 bits of length [ 15] uint8_t flags; // flags, indicating compression info ========================================================================= V4 header: [ 0] char tag[8]; // 'MComprHD' [ 8] uint32_t length; // length of header (including tag and length fields) [ 12] uint32_t version; // drive format version [ 16] uint32_t flags; // flags (see below) [ 20] uint32_t compression; // compression type [ 24] uint32_t totalhunks; // total # of hunks represented [ 28] uint64_t logicalbytes; // logical size of the data (in bytes) [ 36] uint64_t metaoffset; // offset to the first blob of metadata [ 44] uint32_t hunkbytes; // number of bytes per hunk [ 48] uint8_t sha1[20]; // combined raw+meta SHA1 [ 68] uint8_t parentsha1[20];// combined raw+meta SHA1 of parent [ 88] uint8_t rawsha1[20]; // raw data SHA1 [108] (V4 header length) Flags are the same as V1 Compression types: CHDCOMPRESSION_NONE = 0 CHDCOMPRESSION_ZLIB = 1 CHDCOMPRESSION_ZLIB_PLUS = 2 CHDCOMPRESSION_AV = 3 Map format is the same as V3 ========================================================================= V5 header: [ 0] char tag[8]; // 'MComprHD' [ 8] uint32_t length; // length of header (including tag and length fields) [ 12] uint32_t version; // drive format version [ 16] uint32_t compressors[4];// which custom compressors are used? [ 32] uint64_t logicalbytes; // logical size of the data (in bytes) [ 40] uint64_t mapoffset; // offset to the map [ 48] uint64_t metaoffset; // offset to the first blob of metadata [ 56] uint32_t hunkbytes; // number of bytes per hunk (512k maximum) [ 60] uint32_t unitbytes; // number of bytes per unit within each hunk [ 64] uint8_t rawsha1[20]; // raw data SHA1 [ 84] uint8_t sha1[20]; // combined raw+meta SHA1 [104] uint8_t parentsha1[20];// combined raw+meta SHA1 of parent [124] (V5 header length) If parentsha1 != 0, we have a parent (no need for flags) If compressors[0] == 0, we are uncompressed (including maps) V5 uncompressed map format: [ 0] uint32_t offset; // starting offset / hunk size V5 compressed map format header: [ 0] uint32_t length; // length of compressed map [ 4] UINT48 datastart; // offset of first block [ 10] uint16_t crc; // crc-16 of the map [ 12] uint8_t lengthbits; // bits used to encode complength [ 13] uint8_t hunkbits; // bits used to encode self-refs [ 14] uint8_t parentunitbits; // bits used to encode parent unit refs [ 15] uint8_t reserved; // future use [ 16] (compressed header length) Each compressed map entry, once expanded, looks like: [ 0] uint8_t compression; // compression type [ 1] UINT24 complength; // compressed length [ 4] UINT48 offset; // offset [ 10] uint16_t crc; // crc-16 of the data ***************************************************************************/ //************************************************************************** // CONSTANTS //************************************************************************** // pseudo-codecs returned by hunk_info constexpr chd_codec_type CHD_CODEC_SELF = 1; // copy of another hunk constexpr chd_codec_type CHD_CODEC_PARENT = 2; // copy of a parent's hunk constexpr chd_codec_type CHD_CODEC_MINI = 3; // legacy "mini" 8-byte repeat // core types typedef uint32_t chd_metadata_tag; // metadata parameters constexpr chd_metadata_tag CHDMETATAG_WILDCARD = 0; constexpr uint32_t CHDMETAINDEX_APPEND = ~0; // metadata flags constexpr uint8_t CHD_MDFLAGS_CHECKSUM = 0x01; // indicates data is checksummed // standard hard disk metadata constexpr chd_metadata_tag HARD_DISK_METADATA_TAG = CHD_MAKE_TAG('G','D','D','D'); extern const char *HARD_DISK_METADATA_FORMAT; // hard disk identify information constexpr chd_metadata_tag HARD_DISK_IDENT_METADATA_TAG = CHD_MAKE_TAG('I','D','N','T'); // hard disk key information constexpr chd_metadata_tag HARD_DISK_KEY_METADATA_TAG = CHD_MAKE_TAG('K','E','Y',' '); // pcmcia CIS information constexpr chd_metadata_tag PCMCIA_CIS_METADATA_TAG = CHD_MAKE_TAG('C','I','S',' '); // standard CD-ROM metadata constexpr chd_metadata_tag CDROM_OLD_METADATA_TAG = CHD_MAKE_TAG('C','H','C','D'); constexpr chd_metadata_tag CDROM_TRACK_METADATA_TAG = CHD_MAKE_TAG('C','H','T','R'); extern const char *CDROM_TRACK_METADATA_FORMAT; constexpr chd_metadata_tag CDROM_TRACK_METADATA2_TAG = CHD_MAKE_TAG('C','H','T','2'); extern const char *CDROM_TRACK_METADATA2_FORMAT; constexpr chd_metadata_tag GDROM_OLD_METADATA_TAG = CHD_MAKE_TAG('C','H','G','T'); constexpr chd_metadata_tag GDROM_TRACK_METADATA_TAG = CHD_MAKE_TAG('C', 'H', 'G', 'D'); extern const char *GDROM_TRACK_METADATA_FORMAT; // standard DVD metafata constexpr chd_metadata_tag DVD_METADATA_TAG = CHD_MAKE_TAG('D', 'V', 'D', ' '); // standard A/V metadata constexpr chd_metadata_tag AV_METADATA_TAG = CHD_MAKE_TAG('A','V','A','V'); extern const char *AV_METADATA_FORMAT; // A/V laserdisc frame metadata constexpr chd_metadata_tag AV_LD_METADATA_TAG = CHD_MAKE_TAG('A','V','L','D'); //************************************************************************** // TYPE DEFINITIONS //************************************************************************** // ======================> chd_file // core file class class chd_file { friend class chd_file_compressor; friend class chd_verifier; // constants static constexpr uint32_t HEADER_VERSION = 5; static constexpr uint32_t V3_HEADER_SIZE = 120; static constexpr uint32_t V4_HEADER_SIZE = 108; static constexpr uint32_t V5_HEADER_SIZE = 124; static constexpr uint32_t MAX_HEADER_SIZE = V5_HEADER_SIZE; public: // error types enum class error { NO_INTERFACE = 1, NOT_OPEN, ALREADY_OPEN, INVALID_FILE, INVALID_DATA, REQUIRES_PARENT, FILE_NOT_WRITEABLE, CODEC_ERROR, INVALID_PARENT, HUNK_OUT_OF_RANGE, DECOMPRESSION_ERROR, COMPRESSION_ERROR, CANT_VERIFY, METADATA_NOT_FOUND, INVALID_METADATA_SIZE, UNSUPPORTED_VERSION, VERIFY_INCOMPLETE, INVALID_METADATA, INVALID_STATE, OPERATION_PENDING, UNSUPPORTED_FORMAT, UNKNOWN_COMPRESSION, WALKING_PARENT, COMPRESSING }; using open_parent_func = std::function (util::sha1_t const &)>; // construction/destruction chd_file(); virtual ~chd_file(); // getters util::random_read &file(); bool opened() const noexcept { return bool(m_file); } uint32_t version() const noexcept { return m_version; } uint64_t logical_bytes() const noexcept { return m_logicalbytes; } uint32_t hunk_bytes() const noexcept { return m_hunkbytes; } uint32_t hunk_count() const noexcept { return m_hunkcount; } uint32_t unit_bytes() const noexcept { return m_unitbytes; } uint64_t unit_count() const noexcept { return m_unitcount; } bool compressed() const noexcept { return (m_compression[0] != CHD_CODEC_NONE); } chd_codec_type compression(int index) const noexcept { return m_compression[index]; } chd_file *parent() const noexcept { return m_parent.get(); } bool parent_missing() const noexcept; util::sha1_t sha1() const noexcept; util::sha1_t raw_sha1() const noexcept; util::sha1_t parent_sha1() const noexcept; std::error_condition hunk_info(
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
/**************************************************************************

Yamaha SFG01/SFG05 emulation

**************************************************************************/

#include "emu.h"
#include "sfg.h"

#include "bus/midi/midi.h"
#include "bus/msx/cart/msx_audio_kb.h"
#include "machine/ym2148.h"
#include "sound/ymopm.h"

#include "speaker.h"


namespace {

class msx_cart_sfg_device : public device_t, public msx_cart_interface
{
protected:
	msx_cart_sfg_device(const machine_config &mconfig, const device_type type, const char *tag, device_t *owner, u32 clock);

	// device_t implementation
	virtual void device_start() override ATTR_COLD;
	virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;

	IRQ_CALLBACK_MEMBER(irq_callback);

	void ym2151_irq_w(int state);
	void ym2148_irq_w(int state);

	void check_irq();

	required_memory_region m_region_sfg;
	required_device<ym_generic_device> m_ym2151;
	required_device<msx_audio_kbdc_port_device> m_kbdc;
	required_device<ym2148_device> m_ym2148;
	int m_ym2151_irq_state;
	int m_ym2148_irq_state;
	u32 m_rom_mask;
};

msx_cart_sfg_device::msx_cart_sfg_device(const machine_config &mconfig, const device_type type, const char *tag, device_t *owner, u32 clock)
	: device_t(mconfig, type, tag, owner, clock)
	, msx_cart_interface(mconfig, *this)
	, m_region_sfg(*this, "sfg")
	, m_ym2151(*this, "ym2151")
	, m_kbdc(*this, "kbdc")
	, m_ym2148(*this, "ym2148")
	, m_ym2151_irq_state(CLEAR_LINE)
	, m_ym2148_irq_state(CLEAR_LINE)
	, m_rom_mask(0)
{
}

void msx_cart_sfg_device::device_add_mconfig(machine_config &config)
{
	// YM2151 (OPM)
	// YM3012 (DAC)
	// YM2148 (MKS)

	SPEAKER(config, "speaker", 2).front();
	ym2151_device &ym2151(YM2151(config, m_ym2151, DERIVED_CLOCK(1, 1)));  // The SFG01 uses a YM2151, the SFG05 uses a YM2164, input clock comes from the main cpu frequency
	ym2151.irq_handler().set(FUNC(msx_cart_sfg_device::ym2151_irq_w));
	ym2151.add_route(0, "speaker", 0.80, 0);
	ym2151.add_route(1, "speaker", 0.80, 1);

	YM2148(config, m_ym2148, XTAL(4'000'000));
	m_ym2148->txd_handler().set("mdout", FUNC(midi_port_device::write_txd));
	m_ym2148->port_write_handler().set("kbdc", FUNC(msx_audio_kbdc_port_device::write));
	m_ym2148->port_read_handler().set("kbdc", FUNC(msx_audio_kbdc_port_device::read));
	m_ym2148->irq_handler().set(FUNC(msx_cart_sfg_device::ym2148_irq_w));

	MSX_AUDIO_KBDC_PORT(config, m_kbdc, msx_audio_keyboards, nullptr);

	MIDI_PORT(config, "mdout", midiout_slot, "midiout");
	MIDI_PORT(config, "mdin", midiin_slot, "midiin").rxd_handler().set("ym2148", FUNC(ym2148_device::write_rxd));
}

void msx_cart_sfg_device::device_start()
{
	// Set rom mask
	m_rom_mask = m_region_sfg->bytes() - 1;

	maincpu().set_irq_acknowledge_callback(*this, FUNC(msx_cart_sfg_device::irq_callback));

	page(0)->install_rom(0x0000, 0x3fff, m_region_sfg->base());
	if (m_region_sfg->bytes() == 0x8000)
	{
		page(1)->install_rom(0x4000, 0x7fff, m_region_sfg->base() + 0x4000);
	}
	else
	{
		page(1)->install_rom(0x4000, 0x7fff, m_region_sfg->base());
	}

	for (int i = 0; i < 4; i++)
	{
		// These addresses deliberately overlap
		page(i)->install_read_handler(0x4000 * i + 0x3ff0, 0x4000 * i + 0x3ff6, emu::rw_delegate(*m_ym2148, FUNC(ym2148_device::read)));
		page(i)->install_read_handler(0x4000 * i + 0x3ff0, 0x4000 * i + 0x3ff1, emu::rw_delegate(*m_ym2151, FUNC(ym_generic_device::read)));
		page(i)->install_write_handler(0x4000 * i + 0x3ff0, 0x4000 * i + 0x3ff6, emu::rw_delegate(*m_ym2148, FUNC(ym2148_device::write)));
		page(i)->install_write_handler(0x4000 * i + 0x3ff0, 0x4000 * i + 0x3ff1, emu::rw_delegate(*m_ym2151, FUNC(ym_generic_device::write)));
	}
}

IRQ_CALLBACK_MEMBER(msx_cart_sfg_device::irq_callback)
{
	return m_ym2148->get_irq_vector();
}

void msx_cart_sfg_device::ym2151_irq_w(int state)
{
	m_ym2151_irq_state = state ? ASSERT_LINE : CLEAR_LINE;
	check_irq();
}

void msx_cart_sfg_device::ym2148_irq_w(int state)
{
	m_ym2148_irq_state = state ? ASSERT_LINE : CLEAR_LINE;
	check_irq();
}

void msx_cart_sfg_device::check_irq()
{
	if (m_ym2151_irq_state != CLEAR_LINE || m_ym2148_irq_state != CLEAR_LINE)
	{
		irq_out(ASSERT_LINE);
	}
	else
	{
		irq_out(CLEAR_LINE);
	}
}


class msx_cart_sfg01_device : public msx_cart_sfg_device
{
public:
	msx_cart_sfg01_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);

protected:
	virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD;
};

msx_cart_sfg01_device::msx_cart_sfg01_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
	: msx_cart_sfg_device(mconfig, MSX_CART_SFG01, tag, owner, clock)
{
}

ROM_START(msx_sfg01)
	ROM_REGION(0x4000, "sfg", 0)
	ROM_LOAD("sfg01.rom", 0x0, 0x4000, CRC(0995fb36) SHA1(434651305f92aa770a89e40b81125fb22d91603d)) // correct label is almost certainly "yamaha__ym2211-22702__48_18_89_b.ic104" though the datecode portion may vary between late 1983 and mid 1984
ROM_END

const tiny_rom_entry *msx_cart_sfg01_device::device_rom_region() const
{
	return ROM_NAME(msx_sfg01);
}



class msx_cart_sfg05_device : public msx_cart_sfg_device
{
public:
	msx_cart_sfg05_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);

protected:
	virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
	virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD;
};

msx_cart_sfg05_device::msx_cart_sfg05_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
	: msx_cart_sfg_device(mconfig, MSX_CART_SFG05, tag, owner, clock)
{
}

void msx_cart_sfg05_device::device_add_mconfig(machine_config &config)
{
	msx_cart_sfg_device::device_add_mconfig(config);

	ym2164_device &ym2164(YM2164(config.replace(), m_ym2151, DERIVED_CLOCK(1, 1)));
	ym2164.irq_handler().set(FUNC(msx_cart_sfg05_device::ym2151_irq_w));
	ym2164.add_route(0, "speaker", 0.80, 0);
	ym2164.add_route(1, "speaker", 0.80, 1);
}

ROM_START(msx_sfg05)
	ROM_REGION(0x8000, "sfg", 0)
	// Version string starts at $02BD
	ROM_SYSTEM_BIOS(0, "m5.01.011", "SFG05 (original) M5.01.011")
	ROMX_LOAD( "sfg05.rom", 0x0, 0x8000, CRC(2425c279) SHA1(d956167e234f60ad916120437120f86fc8c3c321), ROM_BIOS(0)) // correct label MIGHT be "yamaha__ym2301-23959.ic104" but this needs redump/verification
	ROM_SYSTEM_BIOS(1, "m5.00.013", "SFG05 (SFG01 upgrade) M5.00.013") // SFG01 PCB, Yamaha official upgrade, has YM2151 instead of YM2164
	ROMX_LOAD( "sfg05a.rom", 0x0, 0x8000, CRC(5bc237f8) SHA1(930338f45c08228108c0831cc4a26014c2674718), ROM_BIOS(1)) // this came on a single eprom on a daughterboard on an SFG01 board which had been factory upgraded to SFG05
	ROM_SYSTEM_BIOS(2, "m5.00.011", "SFG05 M5.00.011") // Found in Sakhr AX-200M
	ROMX_LOAD("sfg05_m5_00_011.rom", 0x0, 0x8000, BAD_DUMP CRC(9d5e20c9) SHA1(fcc385b90c65575e29fc009aa00b5120fc4c251a), ROM_BIOS(2)) // Still seems to have I/O reads at $7ff0 - $7fff
ROM_END

const tiny_rom_entry *msx_cart_sfg05_device::device_rom_region() const
{
	return ROM_NAME(msx_sfg05);
}

} // anonymous namespace

DEFINE_DEVICE_TYPE_PRIVATE(MSX_CART_SFG01, msx_cart_interface, msx_cart_sfg01_device, "msx_cart_sfg01", "MSX Cartridge - SFG01")
DEFINE_DEVICE_TYPE_PRIVATE(MSX_CART_SFG05, msx_cart_interface, msx_cart_sfg05_device, "msx_cart_sfg05", "MSX Cartridge - SFG05")