From 3b462970f6434284f04bacf69f2f5e7731d07aec Mon Sep 17 00:00:00 2001 From: scawful Date: Mon, 22 Apr 2024 15:57:19 -0400 Subject: [PATCH] overhaul memory, manage open bus, pal timing, v_pos, h_pos, cartridges, dma channels --- src/app/emu/memory/memory.cc | 209 ++++++++++++++++++++++++ src/app/emu/memory/memory.h | 272 +++++++++++++------------------ src/app/emu/memory/mock_memory.h | 15 ++ 3 files changed, 335 insertions(+), 161 deletions(-) diff --git a/src/app/emu/memory/memory.cc b/src/app/emu/memory/memory.cc index a92dd7b6..5695b6db 100644 --- a/src/app/emu/memory/memory.cc +++ b/src/app/emu/memory/memory.cc @@ -14,6 +14,215 @@ namespace app { namespace emu { namespace memory { +void MemoryImpl::Initialize(const std::vector& romData, bool verbose) { + verbose_ = verbose; + + auto location = 0x7FC0; // GetHeaderOffset(); + romSize = 0x400 << romData[location + 0x17]; + sramSize = 0x400 << romData[location + 0x18]; + rom_.resize(romSize); + + // Copy memory into rom_ + std::copy(romData.begin() + kROMStart, romData.begin() + kROMStart + kROMSize, + rom_.begin()); + + memory_.resize(0x1000000); // 16 MB + + const size_t ROM_CHUNK_SIZE = 0x8000; // 32 KB + + // Clear memory + std::fill(memory_.begin(), memory_.end(), 0); + + // Load ROM data into memory based on LoROM mapping + size_t romSize = romData.size(); + size_t romAddress = 0; + for (size_t bank = 0x00; bank <= 0x3F; ++bank) { + for (size_t offset = 0x8000; offset <= 0xFFFF; offset += ROM_CHUNK_SIZE) { + if (romAddress < romSize) { + std::copy(romData.begin() + romAddress, + romData.begin() + romAddress + ROM_CHUNK_SIZE, + memory_.begin() + (bank << 16) + offset); + romAddress += ROM_CHUNK_SIZE; + } + } + } + + // Copy data into rom_ vector + rom_.resize(kROMSize); + std::copy(memory_.begin() + kROMStart, memory_.begin() + kROMStart + kROMSize, + rom_.begin()); + + // Copy data into ram_ vector + ram_.resize(kRAMSize); + std::copy(memory_.begin() + kRAMStart, memory_.begin() + kRAMStart + kRAMSize, + ram_.begin()); +} + +memory::RomInfo MemoryImpl::ReadRomHeader() { + memory::RomInfo romInfo; + + uint32_t offset = GetHeaderOffset(); + + // Read cartridge title + char title[22]; + for (int i = 0; i < 21; ++i) { + title[i] = ReadByte(offset + i); + } + title[21] = '\0'; // Null-terminate the string + romInfo.title = std::string(title); + + // Read ROM speed and memory map mode + uint8_t romSpeedAndMapMode = ReadByte(offset + 0x15); + romInfo.romSpeed = (memory::RomSpeed)(romSpeedAndMapMode & 0x07); + romInfo.bankSize = (memory::BankSize)((romSpeedAndMapMode >> 5) & 0x01); + + // Read ROM type + romInfo.romType = (memory::RomType)ReadByte(offset + 0x16); + + // Read ROM size + romInfo.romSize = (memory::RomSize)ReadByte(offset + 0x17); + + // Read RAM size + romInfo.sramSize = (memory::SramSize)ReadByte(offset + 0x18); + + // Read country code + romInfo.countryCode = (memory::CountryCode)ReadByte(offset + 0x19); + + // Read license + romInfo.license = (memory::License)ReadByte(offset + 0x1A); + + // Read ROM version + romInfo.version = ReadByte(offset + 0x1B); + + // Read checksum complement + romInfo.checksumComplement = ReadWord(offset + 0x1E); + + // Read checksum + romInfo.checksum = ReadWord(offset + 0x1C); + + // Read NMI VBL vector + romInfo.nmiVblVector = ReadWord(offset + 0x3E); + + // Read reset vector + romInfo.resetVector = ReadWord(offset + 0x3C); + + return romInfo; +} + +uint8_t MemoryImpl::cart_read(uint8_t bank, uint16_t adr) { + switch (type_) { + case 0: + return open_bus_; + case 1: + return cart_readLorom(bank, adr); + case 2: + return cart_readHirom(bank, adr); + case 3: + return cart_readExHirom(bank, adr); + } + return open_bus_; +} + +void MemoryImpl::cart_write(uint8_t bank, uint16_t adr, uint8_t val) { + switch (type_) { + case 0: + break; + case 1: + cart_writeLorom(bank, adr, val); + break; + case 2: + cart_writeHirom(bank, adr, val); + break; + case 3: + cart_writeHirom(bank, adr, val); + break; + } +} + +uint8_t MemoryImpl::cart_readLorom(uint8_t bank, uint16_t adr) { + if (((bank >= 0x70 && bank < 0x7e) || bank >= 0xf0) && adr < 0x8000 && + sramSize > 0) { + // banks 70-7e and f0-ff, adr 0000-7fff + return ram_[(((bank & 0xf) << 15) | adr) & (sramSize - 1)]; + } + bank &= 0x7f; + if (adr >= 0x8000 || bank >= 0x40) { + // adr 8000-ffff in all banks or all addresses in banks 40-7f and c0-ff + return rom_[((bank << 15) | (adr & 0x7fff)) & (romSize - 1)]; + } + return open_bus_; +} + +void MemoryImpl::cart_writeLorom(uint8_t bank, uint16_t adr, uint8_t val) { + if (((bank >= 0x70 && bank < 0x7e) || bank > 0xf0) && adr < 0x8000 && + sramSize > 0) { + // banks 70-7e and f0-ff, adr 0000-7fff + ram_[(((bank & 0xf) << 15) | adr) & (sramSize - 1)] = val; + } +} + +uint8_t MemoryImpl::cart_readHirom(uint8_t bank, uint16_t adr) { + bank &= 0x7f; + if (bank < 0x40 && adr >= 0x6000 && adr < 0x8000 && sramSize > 0) { + // banks 00-3f and 80-bf, adr 6000-7fff + return ram_[(((bank & 0x3f) << 13) | (adr & 0x1fff)) & (sramSize - 1)]; + } + if (adr >= 0x8000 || bank >= 0x40) { + // adr 8000-ffff in all banks or all addresses in banks 40-7f and c0-ff + return rom_[(((bank & 0x3f) << 16) | adr) & (romSize - 1)]; + } + return open_bus_; +} + +uint8_t MemoryImpl::cart_readExHirom(uint8_t bank, uint16_t adr) { + if ((bank & 0x7f) < 0x40 && adr >= 0x6000 && adr < 0x8000 && sramSize > 0) { + // banks 00-3f and 80-bf, adr 6000-7fff + return ram_[(((bank & 0x3f) << 13) | (adr & 0x1fff)) & (sramSize - 1)]; + } + bool secondHalf = bank < 0x80; + bank &= 0x7f; + if (adr >= 0x8000 || bank >= 0x40) { + // adr 8000-ffff in all banks or all addresses in banks 40-7f and c0-ff + return rom_[(((bank & 0x3f) << 16) | (secondHalf ? 0x400000 : 0) | adr) & + (romSize - 1)]; + } + return open_bus_; +} + +void MemoryImpl::cart_writeHirom(uint8_t bank, uint16_t adr, uint8_t val) { + bank &= 0x7f; + if (bank < 0x40 && adr >= 0x6000 && adr < 0x8000 && sramSize > 0) { + // banks 00-3f and 80-bf, adr 6000-7fff + ram_[(((bank & 0x3f) << 13) | (adr & 0x1fff)) & (sramSize - 1)] = val; + } +} + +uint32_t MemoryImpl::GetMappedAddress(uint32_t address) const { + uint8_t bank = address >> 16; + uint32_t offset = address & 0xFFFF; + + if (bank <= 0x3F) { + if (address <= 0x1FFF) { + return (0x7E << 16) + offset; // Shadow RAM + } else if (address <= 0x5FFF) { + return (bank << 16) + (offset - 0x2000) + 0x2000; // Hardware Registers + } else if (address <= 0x7FFF) { + return offset - 0x6000 + 0x6000; // Expansion RAM + } else { + // Return lorom mapping + return (bank << 16) + (offset - 0x8000) + 0x8000; // ROM + } + } else if (bank == 0x7D) { + return offset + 0x7D0000; // SRAM + } else if (bank == 0x7E || bank == 0x7F) { + return offset + 0x7E0000; // System RAM + } else if (bank >= 0x80) { + // Handle HiROM and mirrored areas + } + + return address; // Return the original address if no mapping is defined +} + void DrawSnesMemoryMapping(const MemoryImpl& memory) { // Using those as a base value to create width/height that are factor of the // size of our font diff --git a/src/app/emu/memory/memory.h b/src/app/emu/memory/memory.h index 7c8ac256..64478351 100644 --- a/src/app/emu/memory/memory.h +++ b/src/app/emu/memory/memory.h @@ -2,11 +2,13 @@ #define MEM_H #include +#include #include #include #include #include "app/emu/debug/log.h" +#include "app/emu/memory/dma_channel.h" // LoROM (Mode 20): @@ -91,20 +93,16 @@ class RomInfo { uint16_t resetVector; }; -class Observer { - public: - virtual ~Observer() = default; - virtual void Notify(uint32_t address, uint16_t data) = 0; -}; +typedef struct CpuCallbacks { + std::function read_byte; + std::function write_byte; + std::function idle; +} CpuCallbacks; constexpr uint32_t kROMStart = 0x008000; constexpr uint32_t kROMSize = 0x200000; constexpr uint32_t kRAMStart = 0x7E0000; constexpr uint32_t kRAMSize = 0x20000; -constexpr uint32_t kVRAMStart = 0x210000; -constexpr uint32_t kVRAMSize = 0x10000; -constexpr uint32_t kOAMStart = 0x218000; -constexpr uint32_t kOAMSize = 0x220; /** * @brief Memory interface @@ -136,139 +134,86 @@ class Memory { virtual uint8_t operator[](int i) const = 0; virtual uint8_t at(int i) const = 0; -}; -enum class MemoryMapping { SNES_LOROM = 0, PC_ADDRESS = 1 }; + virtual uint8_t open_bus() const = 0; + virtual void set_open_bus(uint8_t value) = 0; + + virtual bool hdma_init_requested() const = 0; + virtual bool hdma_run_requested() const = 0; + virtual void init_hdma_request() = 0; + virtual void run_hdma_request() = 0; + virtual void set_hdma_run_requested(bool value) = 0; + virtual void set_hdma_init_requested(bool value) = 0; + virtual void set_pal_timing(bool value) = 0; + virtual void set_h_pos(uint16_t value) = 0; + virtual void set_v_pos(uint16_t value) = 0; + + // get h_pos and v_pos + virtual auto h_pos() const -> uint16_t = 0; + virtual auto v_pos() const -> uint16_t = 0; + // get pal timing + virtual auto pal_timing() const -> bool = 0; +}; /** * @class MemoryImpl * @brief Implementation of the Memory interface for emulating memory in a SNES * system. * - * The MemoryImpl class provides methods for initializing and accessing memory - * in a SNES system. It implements the Memory interface and inherits from the - * Loggable class. - * - * The class supports different memory mappings, including LoROM and PC_ADDRESS - * mappings. It provides methods for reading and writing bytes, words, and longs - * from/to memory. It also supports stack operations for pushing and popping - * values. - * - * The class maintains separate vectors for ROM, RAM, VRAM, and OAM memory - * regions. It provides methods for accessing these memory regions and - * retrieving their sizes. - * - * The class also allows adding observers to be notified when memory is read or - * written. - * - * @note This class assumes a 16-bit address space. */ class MemoryImpl : public Memory, public Loggable { public: - void Initialize(const std::vector& romData, bool verbose = false, - MemoryMapping mapping = MemoryMapping::SNES_LOROM) { - verbose_ = verbose; - mapping_ = mapping; - if (mapping == MemoryMapping::PC_ADDRESS) { - memory_.resize(romData.size()); - std::copy(romData.begin(), romData.end(), memory_.begin()); - return; + uint32_t romSize; + uint32_t sramSize; + void Initialize(const std::vector& romData, bool verbose = false); + + uint16_t GetHeaderOffset() { + uint8_t mapMode = rom_[(0x00 << 16) + 0xFFD5]; + uint16_t offset; + + switch (mapMode & 0x07) { + case 0: // LoROM + offset = 0x7FC0; + break; + case 1: // HiROM + offset = 0xFFC0; + break; + case 5: // ExHiROM + offset = 0x40; + break; + default: + throw std::invalid_argument( + "Unable to locate supported ROM mapping mode in the provided ROM " + "file. Please try another ROM file."); } - memory_.resize(0x1000000); // 16 MB - - const size_t ROM_CHUNK_SIZE = 0x8000; // 32 KB - const size_t SRAM_SIZE = 0x10000; // 64 KB - const size_t SYSTEM_RAM_SIZE = 0x20000; // 128 KB - const size_t EXPANSION_RAM_SIZE = 0x2000; // 8 KB - const size_t HARDWARE_REGISTERS_SIZE = 0x4000; // 16 KB - - // Clear memory - std::fill(memory_.begin(), memory_.end(), 0); - - // Load ROM data into memory based on LoROM mapping - size_t romSize = romData.size(); - size_t romAddress = 0; - for (size_t bank = 0x00; bank <= 0x3F; ++bank) { - for (size_t offset = 0x8000; offset <= 0xFFFF; offset += ROM_CHUNK_SIZE) { - if (romAddress < romSize) { - std::copy(romData.begin() + romAddress, - romData.begin() + romAddress + ROM_CHUNK_SIZE, - memory_.begin() + (bank << 16) + offset); - romAddress += ROM_CHUNK_SIZE; - } - } - } - - // Initialize SRAM at banks 0x7D and 0xFD - std::fill(memory_.begin() + (0x7D << 16), memory_.begin() + (0x7E << 16), - 0); - std::fill(memory_.begin() + (0xFD << 16), memory_.begin() + (0xFE << 16), - 0); - - // Initialize System RAM at banks 0x7E and 0x7F - std::fill(memory_.begin() + (0x7E << 16), - memory_.begin() + (0x7E << 16) + SYSTEM_RAM_SIZE, 0); - - // Initialize Shadow RAM at banks 0x00-0x3F and 0x80-0xBF - for (size_t bank = 0x00; bank <= 0xBF; bank += 0x80) { - std::fill(memory_.begin() + (bank << 16), - memory_.begin() + (bank << 16) + 0x2000, 0); - } - - // Initialize Hardware Registers at banks 0x00-0x3F and 0x80-0xBF - for (size_t bank = 0x00; bank <= 0xBF; bank += 0x80) { - std::fill( - memory_.begin() + (bank << 16) + 0x2000, - memory_.begin() + (bank << 16) + 0x2000 + HARDWARE_REGISTERS_SIZE, 0); - } - - // Initialize Expansion RAM at banks 0x00-0x3F and 0x80-0xBF - for (size_t bank = 0x00; bank <= 0xBF; bank += 0x80) { - std::fill(memory_.begin() + (bank << 16) + 0x6000, - memory_.begin() + (bank << 16) + 0x6000 + EXPANSION_RAM_SIZE, - 0); - } - - // Initialize Reset and NMI Vectors at bank 0xFF - std::fill(memory_.begin() + (0xFF << 16) + 0xFF00, - memory_.begin() + (0xFF << 16) + 0xFFFF + 1, 0); - - // Copy data into rom_ vector - rom_.resize(kROMSize); - std::copy(memory_.begin() + kROMStart, - memory_.begin() + kROMStart + kROMSize, rom_.begin()); - - // Copy data into ram_ vector - ram_.resize(kRAMSize); - std::copy(memory_.begin() + kRAMStart, - memory_.begin() + kRAMStart + kRAMSize, ram_.begin()); - - // Copy data into vram_ vector - vram_.resize(kVRAMSize); - std::copy(memory_.begin() + kVRAMStart, - memory_.begin() + kVRAMStart + kVRAMSize, vram_.begin()); - - // Copy data into oam_ vector - oam_.resize(kOAMSize); - std::copy(memory_.begin() + kOAMStart, - memory_.begin() + kOAMStart + kOAMSize, oam_.begin()); + return offset; } + memory::RomInfo ReadRomHeader(); + + uint8_t cart_read(uint8_t bank, uint16_t adr); + void cart_write(uint8_t bank, uint16_t adr, uint8_t val); + + uint8_t cart_readLorom(uint8_t bank, uint16_t adr); + void cart_writeLorom(uint8_t bank, uint16_t adr, uint8_t val); + + uint8_t cart_readHirom(uint8_t bank, uint16_t adr); + uint8_t cart_readExHirom(uint8_t bank, uint16_t adr); + + void cart_writeHirom(uint8_t bank, uint16_t adr, uint8_t val); + uint8_t ReadByte(uint32_t address) const override { uint32_t mapped_address = GetMappedAddress(address); - NotifyObservers(mapped_address, /*data=*/0); return memory_.at(mapped_address); } uint16_t ReadWord(uint32_t address) const override { uint32_t mapped_address = GetMappedAddress(address); - NotifyObservers(mapped_address, /*data=*/0); return static_cast(memory_.at(mapped_address)) | (static_cast(memory_.at(mapped_address + 1)) << 8); } uint32_t ReadWordLong(uint32_t address) const override { uint32_t mapped_address = GetMappedAddress(address); - NotifyObservers(mapped_address, /*data=*/0); return static_cast(memory_.at(mapped_address)) | (static_cast(memory_.at(mapped_address + 1)) << 8) | (static_cast(memory_.at(mapped_address + 2)) << 16); @@ -276,7 +221,6 @@ class MemoryImpl : public Memory, public Loggable { std::vector ReadByteVector(uint32_t address, uint16_t length) const override { uint32_t mapped_address = GetMappedAddress(address); - NotifyObservers(mapped_address, /*data=*/0); return std::vector(memory_.begin() + mapped_address, memory_.begin() + mapped_address + length); } @@ -343,8 +287,6 @@ class MemoryImpl : public Memory, public Loggable { (static_cast(mid) << 8) | low; } - void AddObserver(Observer* observer) { observers_.push_back(observer); } - // Stack Pointer access. uint16_t SP() const override { return SP_; } void SetSP(uint16_t value) override { SP_ = value; } @@ -363,61 +305,69 @@ class MemoryImpl : public Memory, public Loggable { auto begin() const { return memory_.begin(); } auto end() const { return memory_.end(); } auto data() const { return memory_.data(); } + void set_open_bus(uint8_t value) override { open_bus_ = value; } + auto open_bus() const -> uint8_t override { return open_bus_; } + auto hdma_init_requested() const -> bool override { + return hdma_init_requested_; + } + auto hdma_run_requested() const -> bool override { + return hdma_run_requested_; + } + void init_hdma_request() override { hdma_init_requested_ = true; } + void run_hdma_request() override { hdma_run_requested_ = true; } + void set_hdma_run_requested(bool value) override { + hdma_run_requested_ = value; + } + void set_hdma_init_requested(bool value) override { + hdma_init_requested_ = value; + } + void set_pal_timing(bool value) override { pal_timing_ = value; } + void set_h_pos(uint16_t value) override { h_pos_ = value; } + void set_v_pos(uint16_t value) override { v_pos_ = value; } + auto h_pos() const -> uint16_t override { return h_pos_; } + auto v_pos() const -> uint16_t override { return v_pos_; } + auto pal_timing() const -> bool override { return pal_timing_; } + + auto dma_state() -> uint8_t& { return dma_state_; } + void set_dma_state(uint8_t value) { dma_state_ = value; } + auto dma_channels() -> DmaChannel* { return channel; } // Define memory regions std::vector rom_; std::vector ram_; - std::vector vram_; - std::vector oam_; private: - uint32_t GetMappedAddress(uint32_t address) const { - uint8_t bank = address >> 16; - uint32_t offset = address & 0xFFFF; - - if (mapping_ == MemoryMapping::PC_ADDRESS) { - return address; - } - - if (bank <= 0x3F) { - if (address <= 0x1FFF) { - return (0x7E << 16) + offset; // Shadow RAM - } else if (address <= 0x5FFF) { - return (bank << 16) + (offset - 0x2000) + 0x2000; // Hardware Registers - } else if (address <= 0x7FFF) { - return offset - 0x6000 + 0x6000; // Expansion RAM - } else { - // Return lorom mapping - return (bank << 16) + (offset - 0x8000) + 0x8000; // ROM - } - } else if (bank == 0x7D) { - return offset + 0x7D0000; // SRAM - } else if (bank == 0x7E || bank == 0x7F) { - return offset + 0x7E0000; // System RAM - } else if (bank >= 0x80) { - // Handle HiROM and mirrored areas - } - - return address; // Return the original address if no mapping is defined - } - - void NotifyObservers(uint32_t address, uint16_t data) const { - for (auto observer : observers_) { - observer->Notify(address, data); - } - } + uint32_t GetMappedAddress(uint32_t address) const; bool verbose_ = false; - std::vector observers_; + // DMA requests + bool hdma_run_requested_ = false; + bool hdma_init_requested_ = false; - // Memory (64KB) - std::vector memory_; + bool pal_timing_ = false; + + // Frame timing + uint16_t h_pos_ = 0; + uint16_t v_pos_ = 0; + + // Dma State + uint8_t dma_state_ = 0; + + // Dma Channels + DmaChannel channel[8]; + + // Open bus + uint8_t open_bus_ = 0; // Stack Pointer uint16_t SP_ = 0x01FF; - MemoryMapping mapping_ = MemoryMapping::SNES_LOROM; + // Cart Type + uint8_t type_; + + // Memory (64KB) + std::vector memory_; }; void DrawSnesMemoryMapping(const MemoryImpl& memory); diff --git a/src/app/emu/memory/mock_memory.h b/src/app/emu/memory/mock_memory.h index 6a3f68f6..44e8ac5e 100644 --- a/src/app/emu/memory/mock_memory.h +++ b/src/app/emu/memory/mock_memory.h @@ -72,6 +72,21 @@ class MockMemory : public Memory { MOCK_CONST_METHOD1(at, uint8_t(int i)); uint8_t operator[](int i) const override { return memory_[i]; } + MOCK_METHOD0(init_hdma_request, void()); + MOCK_METHOD0(run_hdma_request, void()); + MOCK_METHOD1(set_hdma_run_requested, void(bool value)); + MOCK_METHOD1(set_hdma_init_requested, void(bool value)); + MOCK_CONST_METHOD0(hdma_init_requested, bool()); + MOCK_CONST_METHOD0(hdma_run_requested, bool()); + MOCK_METHOD1(set_pal_timing, void(bool value)); + MOCK_CONST_METHOD0(pal_timing, bool()); + MOCK_CONST_METHOD0(h_pos, uint16_t()); + MOCK_CONST_METHOD0(v_pos, uint16_t()); + MOCK_METHOD1(set_h_pos, void(uint16_t value)); + MOCK_METHOD1(set_v_pos, void(uint16_t value)); + MOCK_METHOD1(set_open_bus, void(uint8_t value)); + MOCK_CONST_METHOD0(open_bus, uint8_t()); + void SetMemoryContents(const std::vector& data) { if (data.size() > memory_.size()) { memory_.resize(data.size());