#ifndef MEM_H #define MEM_H #include #include #include #include #include #include "app/emu/debug/log.h" #include "app/emu/memory/dma_channel.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 { namespace memory { 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; }; 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; /** * @brief Memory interface */ 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 WriteLong(uint32_t address, uint32_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; 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. * */ class MemoryImpl : public Memory, public Loggable { public: uint32_t romSize; uint32_t sramSize; void Initialize(const std::vector& romData, bool verbose = false); uint16_t GetHeaderOffset() { uint8_t mapMode = memory_[(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."); } 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); return memory_.at(mapped_address); } uint16_t ReadWord(uint32_t address) const override { uint32_t mapped_address = GetMappedAddress(address); 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); 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); 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; } void WriteLong(uint32_t address, uint32_t value) override { uint32_t mapped_address = GetMappedAddress(address); memory_.at(mapped_address) = value & 0xFF; memory_.at(mapped_address + 1) = (value >> 8) & 0xFF; memory_.at(mapped_address + 2) = (value >> 16) & 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; } // Stack Pointer access. uint16_t SP() const override { return SP_; } auto mutable_sp() -> uint16_t& { 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(); } 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_; private: uint32_t GetMappedAddress(uint32_t address) const; bool verbose_ = false; // DMA requests bool hdma_run_requested_ = false; bool hdma_init_requested_ = false; 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_ = 0; // Cart Type uint8_t type_ = 1; // Memory (64KB) std::vector memory_; }; void DrawSnesMemoryMapping(const MemoryImpl& memory); } // namespace memory } // namespace emu } // namespace app } // namespace yaze #endif // MEM_H