diff options
Diffstat (limited to 'src/devices/bus/gameboy/cartbase.cpp')
-rw-r--r-- | src/devices/bus/gameboy/cartbase.cpp | 537 |
1 files changed, 537 insertions, 0 deletions
diff --git a/src/devices/bus/gameboy/cartbase.cpp b/src/devices/bus/gameboy/cartbase.cpp new file mode 100644 index 00000000000..82bca2c786a --- /dev/null +++ b/src/devices/bus/gameboy/cartbase.cpp @@ -0,0 +1,537 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + Shared Game Boy cartridge helpers + + ***************************************************************************/ + +#include "emu.h" +#include "cartbase.h" + +#include "bus/generic/slot.h" + +//#define VERBOSE 1 +//#define LOG_OUTPUT_FUNC osd_printf_info +#include "logmacro.h" + + + +namespace bus::gameboy { + +//************************************************************************** +// 16 KiB fixed at 0x0000, 16 KiB switchable at 0x4000 +//************************************************************************** + +mbc_device_base::mbc_device_base( + machine_config const &mconfig, + device_type type, + char const *tag, + device_t *owner, + u32 clock) : + device_t(mconfig, type, tag, owner, clock), + device_gb_cart_interface(mconfig, *this), + m_bank_rom(*this, "rom"), + m_bank_bits_rom(0U), + m_bank_mask_rom(0U) +{ +} + + +void mbc_device_base::device_start() +{ +} + + +bool mbc_device_base::check_rom(std::string &message) +{ + memory_region *const romregion(cart_rom_region()); + if (!romregion || !romregion->bytes()) + { + // setting default bank on reset causes a fatal error when no banks are configured + message = "No ROM data found"; + return false; + } + + auto const rombytes(romregion->bytes()); + if ((rombytes & (PAGE_ROM_SIZE - 1)) || ((u32(PAGE_ROM_SIZE) << m_bank_bits_rom) < rombytes)) + { + message = util::string_format( + "Unsupported cartridge ROM size (must be a multiple of 16 KiB and no larger than %u pages)", + 1U << m_bank_bits_rom); + return false; + } + + return true; +} + + +void mbc_device_base::install_rom() +{ + configure_bank_rom(); + + logerror("Installing fixed ROM at 0x0000-0x3FFF and banked ROM at 0x4000-0x7FFF\n"); + cart_space()->install_rom(0x0000, 0x3fff, cart_rom_region()->base()); + cart_space()->install_read_bank(0x4000, 0x7fff, m_bank_rom); +} + + +void mbc_device_base::configure_bank_rom() +{ + memory_region *const romregion(cart_rom_region()); + m_bank_mask_rom = device_generic_cart_interface::map_non_power_of_two( + unsigned(romregion->bytes() / PAGE_ROM_SIZE), + [this, base = &romregion->as_u8()] (unsigned entry, unsigned page) + { + LOG( + "Install ROM 0x%06X-0x%06X in bank entry %u\n", + page * PAGE_ROM_SIZE, + (page * PAGE_ROM_SIZE) + (PAGE_ROM_SIZE - 1), + entry); + m_bank_rom->configure_entry(entry, &base[page * PAGE_ROM_SIZE]); + }); +} + + +void mbc_device_base::set_bank_rom(u16 entry) +{ + entry &= m_bank_mask_rom; + LOG( + "%s: Set ROM bank = 0x%06X\n", + machine().describe_context(), + entry * PAGE_ROM_SIZE); + m_bank_rom->set_entry(entry); +} + + + +//************************************************************************** +// 16 KiB switchable coarse at 0x0000, 16 KiB switchable fine at 0x4000 +//************************************************************************** + +mbc_dual_device_base::mbc_dual_device_base( + machine_config const &mconfig, + device_type type, + char const *tag, + device_t *owner, + u32 clock) : + device_t(mconfig, type, tag, owner, clock), + device_gb_cart_interface(mconfig, *this), + m_bank_rom(*this, { "low", "high" }), + m_bank_bits_rom{ 0U, 0U }, + m_bank_mask_rom{ 0U, 0U }, + m_bank_sel_rom{ 0U, 0U }, + m_bank_rom_coarse_mask{ 0xffffU, 0xffffU } +{ +} + + +void mbc_dual_device_base::device_start() +{ + save_item(NAME(m_bank_sel_rom)); + save_item(NAME(m_bank_rom_coarse_mask)); +} + + +bool mbc_dual_device_base::check_rom(std::string &message) +{ + memory_region *const romregion(cart_rom_region()); + if (!romregion || !romregion->bytes()) + { + // setting default bank on reset causes a fatal error when no banks are configured + message = "No ROM data found"; + return false; + } + + auto const rombytes(romregion->bytes()); + auto const pages(u32(1) << (m_bank_bits_rom[0] + m_bank_bits_rom[1])); + if ((rombytes & (PAGE_ROM_SIZE - 1)) || ((PAGE_ROM_SIZE * pages) < rombytes)) + { + message = util::string_format( + "Unsupported cartridge ROM size (must be a multiple of 16 KiB and no larger than %u pages)", + pages); + return false; + } + + return true; +} + + +void mbc_dual_device_base::install_rom() +{ + configure_bank_rom(); + + logerror("Installing banked ROM at 0x0000-0x3FFF and 0x4000-0x7FFF\n"); + cart_space()->install_read_bank(0x0000, 0x3fff, m_bank_rom[0]); + cart_space()->install_read_bank(0x4000, 0x7fff, m_bank_rom[1]); +} + + +void mbc_dual_device_base::configure_bank_rom() +{ + memory_region *const romregion(cart_rom_region()); + auto const rombytes(romregion->bytes()); + u8 *const rombase(&romregion->as_u8()); + auto const multiplier(1U << m_bank_bits_rom[1]); + + m_bank_mask_rom[0] = device_generic_cart_interface::map_non_power_of_two( + unsigned(((rombytes / PAGE_ROM_SIZE) + multiplier - 1) / multiplier), + [this, rombase, multiplier] (unsigned entry, unsigned page) + { + LOG( + "Install ROM 0x%06X-0x%06X in low bank entry %u\n", + page * PAGE_ROM_SIZE * multiplier, + (page * PAGE_ROM_SIZE * multiplier) + (PAGE_ROM_SIZE - 1), + entry); + m_bank_rom[0]->configure_entry(entry, &rombase[page * PAGE_ROM_SIZE * multiplier]); + }); + + m_bank_mask_rom[1] = device_generic_cart_interface::map_non_power_of_two( + unsigned(rombytes / PAGE_ROM_SIZE), + [this, rombase] (unsigned entry, unsigned page) + { + LOG( + "Install ROM 0x%06X-0x%06X in high bank entry %u\n", + page * PAGE_ROM_SIZE, + (page * PAGE_ROM_SIZE) + (PAGE_ROM_SIZE - 1), + entry); + m_bank_rom[1]->configure_entry(entry, &rombase[page * PAGE_ROM_SIZE]); + }); +} + + +void mbc_dual_device_base::set_bank_rom_coarse(u16 entry) +{ + m_bank_sel_rom[0] = entry; + u16 const lo(bank_rom_entry_low()); + u16 const hi(bank_rom_entry_high()); + LOG( + "%s: Set ROM banks low = 0x%06X, high = 0x%06X\n", + machine().describe_context(), + (u32(lo) * PAGE_ROM_SIZE) << m_bank_bits_rom[1], + u32(hi) * PAGE_ROM_SIZE); + m_bank_rom[0]->set_entry(lo); + m_bank_rom[1]->set_entry(hi); +} + + +void mbc_dual_device_base::set_bank_rom_fine(u16 entry) +{ + m_bank_sel_rom[1] = entry; + u16 const hi(bank_rom_entry_high()); + LOG( + "%s: Set ROM bank high = 0x%06X\n", + machine().describe_context(), + u32(hi) * PAGE_ROM_SIZE); + m_bank_rom[1]->set_entry(hi); +} + + +void mbc_dual_device_base::set_bank_rom_low_coarse_mask(u16 mask) +{ + m_bank_rom_coarse_mask[0] = mask; + u16 const lo(bank_rom_entry_low()); + LOG( + "%s: Set ROM bank low = 0x%06X\n", + machine().describe_context(), + (u32(lo) * PAGE_ROM_SIZE) << m_bank_bits_rom[1]); + m_bank_rom[0]->set_entry(lo); +} + + +void mbc_dual_device_base::set_bank_rom_high_coarse_mask(u16 mask) +{ + m_bank_rom_coarse_mask[1] = mask; + u16 const hi(bank_rom_entry_high()); + LOG( + "%s: Set ROM bank high = 0x%06X\n", + machine().describe_context(), + u32(hi) * PAGE_ROM_SIZE); + m_bank_rom[1]->set_entry(hi); +} + + + +//************************************************************************** +// 16 KiB switchable at 0x0000, 16 KiB switchable at 0x4000 +//************************************************************************** + +mbc_dual_uniform_device_base::mbc_dual_uniform_device_base( + machine_config const &mconfig, + device_type type, + char const *tag, + device_t *owner, + u32 clock) : + device_t(mconfig, type, tag, owner, clock), + device_gb_cart_interface(mconfig, *this), + m_bank_rom(*this, { "low", "high" }), + m_bank_bits_rom(0U), + m_bank_mask_rom(0U) +{ +} + + +void mbc_dual_uniform_device_base::device_start() +{ +} + + +bool mbc_dual_uniform_device_base::check_rom(std::string &message) +{ + memory_region *const romregion(cart_rom_region()); + if (!romregion || !romregion->bytes()) + { + // setting default bank on reset causes a fatal error when no banks are configured + message = "No ROM data found"; + return false; + } + + auto const rombytes(romregion->bytes()); + if ((rombytes & (PAGE_ROM_SIZE - 1)) || ((u32(PAGE_ROM_SIZE) << m_bank_bits_rom) < rombytes)) + { + message = util::string_format( + "Unsupported cartridge ROM size (must be a multiple of 16 KiB and no larger than %u pages)", + 1U << m_bank_bits_rom); + return false; + } + + return true; +} + + +void mbc_dual_uniform_device_base::install_rom() +{ + configure_bank_rom(); + + logerror("Installing banked ROM at 0x0000-0x3FFF and 0x4000-0x7FFF\n"); + cart_space()->install_read_bank(0x0000, 0x3fff, m_bank_rom[0]); + cart_space()->install_read_bank(0x4000, 0x7fff, m_bank_rom[1]); +} + + +void mbc_dual_uniform_device_base::configure_bank_rom() +{ + memory_region *const romregion(cart_rom_region()); + m_bank_mask_rom = device_generic_cart_interface::map_non_power_of_two( + unsigned(romregion->bytes() / PAGE_ROM_SIZE), + [this, base = &romregion->as_u8()] (unsigned entry, unsigned page) + { + LOG( + "Install ROM 0x%06X-0x%06X in bank entry %u\n", + page * PAGE_ROM_SIZE, + (page * PAGE_ROM_SIZE) + (PAGE_ROM_SIZE - 1), + entry); + m_bank_rom[0]->configure_entry(entry, &base[page * PAGE_ROM_SIZE]); + m_bank_rom[1]->configure_entry(entry, &base[page * PAGE_ROM_SIZE]); + }); +} + + +void mbc_dual_uniform_device_base::set_bank_rom_low(u16 entry) +{ + entry &= m_bank_mask_rom; + LOG( + "%s: Set ROM bank low = 0x%06X\n", + machine().describe_context(), + entry * PAGE_ROM_SIZE); + m_bank_rom[0]->set_entry(entry); +} + + +void mbc_dual_uniform_device_base::set_bank_rom_high(u16 entry) +{ + entry &= m_bank_mask_rom; + LOG( + "%s: Set ROM bank high = 0x%06X\n", + machine().describe_context(), + entry * PAGE_ROM_SIZE); + m_bank_rom[1]->set_entry(entry); +} + + + +//************************************************************************** +// 16 KiB fixed at 0x0000, 8 KiB switchable at 0x4000 and 0x6000 +//************************************************************************** + +mbc_8k_device_base::mbc_8k_device_base( + machine_config const &mconfig, + device_type type, + char const *tag, + device_t *owner, + u32 clock) : + device_t(mconfig, type, tag, owner, clock), + device_gb_cart_interface(mconfig, *this), + m_bank_rom(*this, { "romlow", "romhigh" }), + m_bank_bits_rom(0U), + m_bank_mask_rom(0U) +{ +} + + +void mbc_8k_device_base::device_start() +{ +} + + +bool mbc_8k_device_base::check_rom(std::string &message) +{ + memory_region *const romregion(cart_rom_region()); + if (!romregion || !romregion->bytes()) + { + // setting default bank on reset causes a fatal error when no banks are configured + message = "No ROM data found"; + return false; + } + + auto const rombytes(romregion->bytes()); + if ((rombytes & (PAGE_ROM_SIZE - 1)) || ((u32(PAGE_ROM_SIZE) << m_bank_bits_rom) < rombytes)) + { + message = util::string_format( + "Unsupported cartridge ROM size (must be a multiple of 8 KiB and no larger than %u pages)", + 1U << m_bank_bits_rom); + return false; + } + + return true; +} + + +void mbc_8k_device_base::install_rom( + address_space_installer &fixedspace, + address_space_installer &lowspace, + address_space_installer &highspace) +{ + memory_region *const romregion(cart_rom_region()); + auto const rombytes(romregion->bytes()); + + // install the fixed ROM, mirrored if it’s too small + if (0x4000 > rombytes) + fixedspace.install_rom(0x0000, 0x1fff, 0x2000, romregion->base()); + else + fixedspace.install_rom(0x0000, 0x3fff, 0x0000, romregion->base()); + + // configure both banks as views of the ROM + m_bank_mask_rom = device_generic_cart_interface::map_non_power_of_two( + unsigned(rombytes / PAGE_ROM_SIZE), + [this, base = &romregion->as_u8()] (unsigned entry, unsigned page) + { + LOG( + "Install ROM 0x%06X-0x%06X in bank entry %u\n", + page * PAGE_ROM_SIZE, + (page * PAGE_ROM_SIZE) + (PAGE_ROM_SIZE - 1), + entry); + m_bank_rom[0]->configure_entry(entry, &base[page * PAGE_ROM_SIZE]); + m_bank_rom[1]->configure_entry(entry, &base[page * PAGE_ROM_SIZE]); + }); + lowspace.install_read_bank(0x4000, 0x5fff, m_bank_rom[0]); + highspace.install_read_bank(0x6000, 0x7fff, m_bank_rom[1]); +} + + +void mbc_8k_device_base::set_bank_rom_low(u16 entry) +{ + entry &= m_bank_mask_rom; + LOG( + "%s: Set ROM bank low = 0x%06X\n", + machine().describe_context(), + entry * PAGE_ROM_SIZE); + m_bank_rom[0]->set_entry(entry); +} + + +void mbc_8k_device_base::set_bank_rom_high(u16 entry) +{ + entry &= m_bank_mask_rom; + LOG( + "%s: Set ROM bank high = 0x%06X\n", + machine().describe_context(), + entry * PAGE_ROM_SIZE); + m_bank_rom[1]->set_entry(entry); +} + + + +//************************************************************************** +// 32 KiB switchable at 0x0000 +//************************************************************************** + +banked_32k_device_base::banked_32k_device_base( + machine_config const &mconfig, + device_type type, + char const *tag, + device_t *owner, + u32 clock) : + device_t(mconfig, type, tag, owner, clock), + device_gb_cart_interface(mconfig, *this), + m_bank_rom(*this, "rom"), + m_bank_bits_rom(0U), + m_bank_mask_rom(0U) +{ +} + + +void banked_32k_device_base::device_start() +{ +} + +bool banked_32k_device_base::check_rom(std::string &message) +{ + memory_region *const romregion(cart_rom_region()); + if (!romregion || !romregion->bytes()) + { + // setting default bank on reset causes a fatal error when no banks are configured + message = "No ROM data found"; + return false; + } + + auto const rombytes(romregion->bytes()); + if ((rombytes & (PAGE_ROM_SIZE - 1)) || ((u32(PAGE_ROM_SIZE) << m_bank_bits_rom) < rombytes)) + { + message = util::string_format( + "Unsupported cartridge ROM size (must be a multiple of 32 KiB and no larger than %u pages)", + 1U << m_bank_bits_rom); + return false; + } + + return true; +} + + +void banked_32k_device_base::install_rom() +{ + configure_bank_rom(); + + logerror("Installing banked ROM at 0x0000-0x7FFF\n"); + cart_space()->install_read_bank(0x0000, 0x7fff, m_bank_rom); +} + + +void banked_32k_device_base::configure_bank_rom() +{ + memory_region *const romregion(cart_rom_region()); + m_bank_mask_rom = device_generic_cart_interface::map_non_power_of_two( + unsigned(romregion->bytes() / PAGE_ROM_SIZE), + [this, base = &romregion->as_u8()] (unsigned entry, unsigned page) + { + LOG( + "Install ROM 0x%06X-0x%06X in bank entry %u\n", + page * PAGE_ROM_SIZE, + (page * PAGE_ROM_SIZE) + (PAGE_ROM_SIZE - 1), + entry); + m_bank_rom->configure_entry(entry, &base[page * PAGE_ROM_SIZE]); + }); +} + + +void banked_32k_device_base::set_bank_rom(u16 entry) +{ + entry &= m_bank_mask_rom; + LOG( + "%s: Set ROM bank = 0x%06X\n", + machine().describe_context(), + entry * PAGE_ROM_SIZE); + m_bank_rom->set_entry(entry); +} + +} // namespace bus::gameboy |