#ifndef MEM_H #define MEM_H #include #include #include #include #include "app/emu/debug/log.h" // LoROM (Mode 20): // Banks Offset Purpose // 00-3F 0000-1FFF LowRAM (shadowed from 7E) // 2000-2FFF PPU1, APU // 3000-3FFF SFX, DSP, etc. // 4000-41FF Controller // 4200-5FFF PPU2, DMA, etc. // 6000-7FFF Expansion RAM (reserved) // 8000-FFFF 32k ROM Chunk // 40-7C 0000-7FFF 32k ROM Chunk // 8000-FFFF 32k ROM Chunk // 7D 0000-FFFF SRAM // 7E 0000-1FFF LowRAM // 2000-FFFF System RAM // 7F 0000-FFFF System RAM namespace yaze { namespace app { namespace emu { enum ROMSpeed { SLOW_ROM = 0x00, FAST_ROM = 0x07 }; enum BankSize { LOW_ROM = 0x00, HI_ROM = 0x01 }; enum ROMType { ROM_DEFAULT = 0x00, ROM_RAM = 0x01, ROM_SRAM = 0x02, ROM_DSP1 = 0x03, ROM_DSP1_RAM = 0x04, ROM_DSP1_SRAM = 0x05, FX = 0x06 }; enum ROMSize { SIZE_2_MBIT = 0x08, SIZE_4_MBIT = 0x09, SIZE_8_MBIT = 0x0A, SIZE_16_MBIT = 0x0B, SIZE_32_MBIT = 0x0C }; enum SRAMSize { NO_SRAM = 0x00, SRAM_16_KBIT = 0x01, SRAM_32_KBIT = 0x02, SRAM_64_KBIT = 0x03 }; enum CountryCode { JAPAN = 0x00, USA = 0x01, EUROPE_OCEANIA_ASIA = 0x02, // ... and other countries }; enum License { INVALID = 0, NINTENDO = 1, ZAMUSE = 5, CAPCOM = 8, // ... and other licenses }; class ROMInfo { public: std::string title; ROMSpeed romSpeed; BankSize bankSize; ROMType romType; ROMSize romSize; SRAMSize sramSize; CountryCode countryCode; License license; uint8_t version; uint16_t checksumComplement; uint16_t checksum; uint16_t nmiVblVector; uint16_t resetVector; }; class Observer { public: virtual ~Observer() = default; virtual void Notify(uint32_t address, uint8_t data) = 0; }; 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; // memory.h class Memory { public: virtual ~Memory() = default; virtual uint8_t ReadByte(uint32_t address) const = 0; virtual uint16_t ReadWord(uint32_t address) const = 0; virtual uint32_t ReadWordLong(uint32_t address) const = 0; virtual std::vector ReadByteVector(uint32_t address, uint16_t length) const = 0; virtual void WriteByte(uint32_t address, uint8_t value) = 0; virtual void WriteWord(uint32_t address, uint16_t value) = 0; virtual void PushByte(uint8_t value) = 0; virtual uint8_t PopByte() = 0; virtual void PushWord(uint16_t value) = 0; virtual uint16_t PopWord() = 0; virtual void PushLong(uint32_t value) = 0; virtual uint32_t PopLong() = 0; virtual uint16_t SP() const = 0; virtual void SetSP(uint16_t value) = 0; virtual void ClearMemory() = 0; 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 }; class MemoryImpl : public Memory, public Loggable { public: void Initialize(const std::vector& romData, MemoryMapping mapping = MemoryMapping::SNES_LOROM) { mapping_ = mapping; if (mapping == MemoryMapping::PC_ADDRESS) { memory_.resize(romData.size()); std::copy(romData.begin(), romData.end(), memory_.begin()); return; } 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()); } 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); } 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); } void WriteByte(uint32_t address, uint8_t value) override { uint32_t mapped_address = GetMappedAddress(address); memory_[mapped_address] = value; } void WriteWord(uint32_t address, uint16_t value) override { uint32_t mapped_address = GetMappedAddress(address); memory_.at(mapped_address) = value & 0xFF; memory_.at(mapped_address + 1) = (value >> 8) & 0xFF; } // Stack operations void PushByte(uint8_t value) override { if (SP_ > 0x0100) { memory_.at(SP_--) = value; } else { // Handle stack underflow std::cout << "Stack underflow!" << std::endl; throw std::runtime_error("Stack underflow!"); } } uint8_t PopByte() override { if (SP_ < 0x1FF) { return memory_.at(++SP_); } else { // Handle stack overflow std::cout << "Stack overflow!" << std::endl; throw std::runtime_error("Stack overflow!"); } } void PushWord(uint16_t value) override { PushByte(value >> 8); PushByte(value & 0xFF); } uint16_t PopWord() override { uint8_t low = PopByte(); uint8_t high = PopByte(); return (static_cast(high) << 8) | low; } void PushLong(uint32_t value) override { PushByte(value >> 16); PushByte(value >> 8); PushByte(value & 0xFF); } uint32_t PopLong() override { uint8_t low = PopByte(); uint8_t mid = PopByte(); uint8_t high = PopByte(); return (static_cast(high) << 16) | (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; } void ClearMemory() override { std::fill(memory_.begin(), memory_.end(), 0); } uint8_t at(int i) const override { return memory_[i]; } uint8_t operator[](int i) const override { if (i > memory_.size()) { std::cout << i << " out of bounds \n"; return memory_[0]; } return memory_[i]; } auto size() const { return memory_.size(); } auto begin() const { return memory_.begin(); } auto end() const { return memory_.end(); } auto data() const { return memory_.data(); } // Define memory regions std::vector rom_; std::vector ram_; std::vector vram_; std::vector oam_; private: uint32_t GetMappedAddress(uint32_t address) const { uint32_t bank = address >> 16; uint32_t offset = address & 0xFFFF; if (mapping_ == MemoryMapping::PC_ADDRESS) { return address; } if (bank <= 0x3F) { if (offset <= 0x1FFF) { return offset; // Shadow RAM } else if (offset <= 0x5FFF) { return offset - 0x2000 + 0x2000; // Hardware Registers } else if (offset <= 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, uint8_t data) const { for (auto observer : observers_) { observer->Notify(address, data); } } std::vector observers_; // Memory (64KB) std::vector memory_; // Stack Pointer uint16_t SP_ = 0x01FF; MemoryMapping mapping_ = MemoryMapping::SNES_LOROM; }; void DrawSnesMemoryMapping(const MemoryImpl& memory); } // namespace emu } // namespace app } // namespace yaze #endif // MEM_H