summaryrefslogtreecommitdiff
path: root/lib/nes/mapper.cc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/nes/mapper.cc')
-rw-r--r--lib/nes/mapper.cc252
1 files changed, 145 insertions, 107 deletions
diff --git a/lib/nes/mapper.cc b/lib/nes/mapper.cc
index ad9ec40..40d11f8 100644
--- a/lib/nes/mapper.cc
+++ b/lib/nes/mapper.cc
@@ -1,134 +1,172 @@
#include "nes/mapper.hh"
#include "nes/cartridge.hh"
-#include <iostream>
namespace nes
{
- mapper::mapper(
- cartridge& cartridge,
- read_cpu_func const read_cpu, write_cpu_func const write_cpu,
- read_ppu_func const read_ppu, write_ppu_func const write_ppu)
- : cartridge_{ cartridge }
- , read_cpu_{ read_cpu }
- , write_cpu_{ write_cpu }
- , read_ppu_{ read_ppu }
- , write_ppu_{ write_ppu }
+ namespace
{
- }
+ // -------------------------------------------------------------------------------------------------------------
+ // NROM
+ // -------------------------------------------------------------------------------------------------------------
- auto mapper::select(cartridge& cartridge) -> mapper
- {
- switch (cartridge.get_mapper_number())
+ class mapper_nrom final : public mapper
{
- case 0x00:
- return mapper{
- cartridge,
- &mapper::nrom_read_cpu, &mapper::nrom_write_cpu,
- &mapper::nrom_read_ppu, &mapper::nrom_write_ppu };
- default:
- std::cerr << "Mapper not implemented: " << std::hex << cartridge.get_mapper_number() << std::endl;
- std::abort();
- }
- }
+ public:
+ explicit mapper_nrom() = default;
- // -----------------------------------------------------------------------------------------------------------------
- // NROM
- // -----------------------------------------------------------------------------------------------------------------
+ auto validate(cartridge& cartridge) -> status override
+ {
+ if (cartridge.get_ram_length() != 0x2000)
+ {
+ return status::error_invalid_ines_data;
+ }
- auto mapper::nrom_read_cpu(address const addr) -> std::uint8_t
- {
- if (addr <= address{ 0x5FFF })
- {
- return 0x0;
- }
- if (addr <= address{ 0x7FFF })
- {
- return cartridge_.get_ram()[addr.get_absolute() - 0x6000];
- }
- if (addr <= address{ 0xBFFF })
- {
- auto const rel = addr.get_absolute() - 0x8000u;
- if (rel >= cartridge_.get_prg_rom_length()) { return 0x0; }
+ if (cartridge.get_prg_rom_length() != 0x4000 && cartridge.get_prg_rom_length() != 0x8000)
+ {
+ return status::error_invalid_ines_data;
+ }
- return cartridge_.get_prg_rom()[rel];
- }
- if (addr <= address{ 0xFFFF })
- {
- auto rel = addr.get_absolute() - 0xC000u;
- if (cartridge_.get_prg_rom_length() > 0x4000) { rel += 0x4000; }
- if (rel >= cartridge_.get_prg_rom_length()) { return 0x0; }
+ if (cartridge.get_chr_rom_length() != 0x2000)
+ {
+ return status::error_invalid_ines_data;
+ }
- return cartridge_.get_prg_rom()[rel];
- }
+ return status::success;
+ }
- return 0x0;
- }
+ auto read_cpu(address const addr, cartridge& cartridge) -> std::uint8_t override
+ {
+ if (addr <= address{ 0x5FFF })
+ {
+ return 0x0;
+ }
+ if (addr <= address{ 0x7FFF })
+ {
+ return cartridge.get_ram()[addr.get_absolute() - 0x6000];
+ }
+ if (addr <= address{ 0xBFFF })
+ {
+ auto const rel = addr.get_absolute() - 0x8000u;
+ return cartridge.get_prg_rom()[rel];
+ }
+ if (addr <= address{ 0xFFFF })
+ {
+ auto rel = addr.get_absolute() - 0xC000u;
+ if (cartridge.get_prg_rom_length() > 0x4000) { rel += 0x4000; }
+ return cartridge.get_prg_rom()[rel];
+ }
- auto mapper::nrom_write_cpu(address const addr, std::uint8_t const value) -> void
- {
- if (addr <= address{ 0x5FFF })
- {
- return;
- }
- if (addr <= address{ 0x7FFF })
- {
- cartridge_.get_ram_mut()[addr.get_absolute() - 0x6000] = value;
- return;
- }
- }
+ return 0x0;
+ }
- auto mapper::nrom_read_ppu(address const addr, std::uint8_t const* vram) -> std::uint8_t
- {
- if (addr <= address{ 0x1FFF })
- {
- auto const rel = addr.get_absolute();
- if (rel >= cartridge_.get_chr_rom_length()) { return 0x0; }
+ auto write_cpu(address const addr, std::uint8_t const value, cartridge& cartridge) -> void override
+ {
+ if (addr <= address{ 0x5FFF })
+ {
+ return;
+ }
+ if (addr <= address{ 0x7FFF })
+ {
+ cartridge.get_ram_mut()[addr.get_absolute() - 0x6000] = value;
+ return;
+ }
+ }
- return cartridge_.get_chr_rom()[rel];
- }
- if (addr <= address{ 0x3EFF })
+ auto read_ppu(address const addr, cartridge& cartridge, std::uint8_t const* vram) -> std::uint8_t override
+ {
+ if (addr <= address{ 0x1FFF })
+ {
+ auto const rel = addr.get_absolute();
+ return cartridge.get_chr_rom()[rel];
+ }
+ if (addr <= address{ 0x3EFF })
+ {
+ return vram[mirrored_vram_address(addr, cartridge)];
+ }
+
+ return 0x0;
+ }
+
+ auto write_ppu(address const addr, std::uint8_t const value, cartridge& cartridge, std::uint8_t* vram) -> void override
+ {
+ if (addr <= address{ 0x1FFF })
+ {
+ return;
+ }
+ if (addr <= address{ 0x3EFF })
+ {
+ vram[mirrored_vram_address(addr, cartridge)] = value;
+ return;
+ }
+ }
+
+ private:
+ static auto mirrored_vram_address(address const addr, cartridge const& cartridge) -> unsigned
+ {
+ auto const rel = addr.get_absolute() % 0x1000u;
+
+ switch (cartridge.get_name_table_arrangement())
+ {
+ case name_table_arrangement::horizontal:
+ // $2000 = $2400, $2800 = $2c00
+ if (rel <= 0x3FF) { return 0x000u + (rel % 0x400u); }
+ if (rel <= 0x7FF) { return 0x000u + (rel % 0x400u); }
+ if (rel <= 0xBFF) { return 0x400u + (rel % 0x400u); }
+ if (rel <= 0xFFF) { return 0x400u + (rel % 0x400u); }
+ break;
+ case name_table_arrangement::vertical:
+ // $2000 = $2800, $2400 = $2c00
+ if (rel <= 0x3FF) { return 0x000u + (rel % 0x400u); }
+ if (rel <= 0x7FF) { return 0x400u + (rel % 0x400u); }
+ if (rel <= 0xBFF) { return 0x000u + (rel % 0x400u); }
+ if (rel <= 0xFFF) { return 0x400u + (rel % 0x400u); }
+ break;
+ }
+
+ return 0;
+ }
+ };
+
+ // -------------------------------------------------------------------------------------------------------------
+ // Invalid
+ // -------------------------------------------------------------------------------------------------------------
+
+ class mapper_invalid final : public mapper
{
- return vram[nrom_mirrored_vram_address(addr)];
- }
+ public:
+ explicit mapper_invalid() = default;
- return 0x0;
- }
+ auto validate(cartridge&) -> status override { return status::error_unsupported_mapper; }
+ auto read_cpu(address, cartridge&) -> std::uint8_t override { return 0; }
+ auto write_cpu(address, std::uint8_t, cartridge&) -> void override {}
+ auto read_ppu(address, cartridge&, std::uint8_t const*) -> std::uint8_t override { return 0; }
+ auto write_ppu(address, std::uint8_t, cartridge&, std::uint8_t*) -> void override {}
+ };
+ } // namespace
- auto mapper::nrom_write_ppu(address const addr, std::uint8_t* vram, std::uint8_t const value) -> void
+ // -----------------------------------------------------------------------------------------------------------------
+ // Selection
+ // -----------------------------------------------------------------------------------------------------------------
+
+ auto mapper::invalid() -> mapper&
{
- if (addr <= address{ 0x1FFF })
- {
- return;
- }
- if (addr <= address{ 0x3EFF })
- {
- vram[nrom_mirrored_vram_address(addr)] = value;
- return;
- }
+ static auto instance = mapper_invalid{};
+ return instance;
}
- auto mapper::nrom_mirrored_vram_address(address const addr) const -> unsigned
+ auto mapper::get(std::uint8_t const number) -> mapper&
{
- auto const rel = addr.get_absolute() % 0x1000u;
-
- switch (cartridge_.get_name_table_arrangement())
+ switch (number)
{
- case name_table_arrangement::horizontal:
- // $2000 = $2400, $2800 = $2c00
- if (rel <= 0x3FF) { return 0x000u + (rel % 0x400u); }
- if (rel <= 0x7FF) { return 0x000u + (rel % 0x400u); }
- if (rel <= 0xBFF) { return 0x400u + (rel % 0x400u); }
- if (rel <= 0xFFF) { return 0x400u + (rel % 0x400u); }
- break;
- case name_table_arrangement::vertical:
- // $2000 = $2800, $2400 = $2c00
- if (rel <= 0x3FF) { return 0x000u + (rel % 0x400u); }
- if (rel <= 0x7FF) { return 0x400u + (rel % 0x400u); }
- if (rel <= 0xBFF) { return 0x000u + (rel % 0x400u); }
- if (rel <= 0xFFF) { return 0x400u + (rel % 0x400u); }
- break;
+ case 0x00:
+ {
+ static auto instance = mapper_nrom{};
+ return instance;
+ }
+ default:
+ {
+ return invalid();
+ }
}
-
- return 0;
}
} // namespace nes \ No newline at end of file