From ffef7101fa3867eca121db5a1bce9694f53e04ff Mon Sep 17 00:00:00 2001 From: PalindromicBreadLoaf Date: Mon, 21 Jul 2025 20:43:53 -0400 Subject: [PATCH] Split project into multiple files --- src/apu.cpp | 29 +++++ src/apu.h | 31 +++++ src/bus.cpp | 40 ++++++ src/bus.h | 29 +++++ src/cpu.cpp | 54 ++++++++ src/cpu.h | 55 ++++++++ src/main.cpp | 344 +------------------------------------------------ src/ppu.cpp | 43 +++++++ src/ppu.h | 43 +++++++ src/system.cpp | 68 ++++++++++ src/system.h | 37 ++++++ 11 files changed, 430 insertions(+), 343 deletions(-) create mode 100644 src/apu.cpp create mode 100644 src/apu.h create mode 100644 src/bus.cpp create mode 100644 src/bus.h create mode 100644 src/cpu.cpp create mode 100644 src/cpu.h create mode 100644 src/ppu.cpp create mode 100644 src/ppu.h create mode 100644 src/system.cpp create mode 100644 src/system.h diff --git a/src/apu.cpp b/src/apu.cpp new file mode 100644 index 0000000..e27f9f3 --- /dev/null +++ b/src/apu.cpp @@ -0,0 +1,29 @@ +// +// Created by Palindromic Bread Loaf on 7/21/25. +// + +#include "apu.h" + +#include + +// APU Implementation +void APU::Reset() { + A = X = Y = 0; + SP = 0xFF; + PC = 0x0000; + PSW = 0x02; + + std::fill(spc_ram, spc_ram + sizeof(spc_ram), 0); +} + +void APU::Step() { + // TODO: Implement SPC700 instruction execution +} + +uint8_t APU::ReadSPC(uint16_t address) { + return spc_ram[address]; +} + +void APU::WriteSPC(uint16_t address, uint8_t value) { + spc_ram[address] = value; +} \ No newline at end of file diff --git a/src/apu.h b/src/apu.h new file mode 100644 index 0000000..9469074 --- /dev/null +++ b/src/apu.h @@ -0,0 +1,31 @@ +// +// Created by Palindromic Bread Loaf on 7/21/25. +// + +#ifndef APU_H +#define APU_H +#include + +// SPC700 APU +class APU { +private: + std::uint8_t spc_ram[0x10000]; // 64KB SPC700 RAM + + // APU registers + uint8_t A, X, Y, SP; + uint16_t PC; + uint8_t PSW; + +public: + APU() { + Reset(); + } + + void Reset(); + void Step(); + + uint8_t ReadSPC(uint16_t address); + void WriteSPC(uint16_t address, uint8_t value); +}; + +#endif //APU_H diff --git a/src/bus.cpp b/src/bus.cpp new file mode 100644 index 0000000..8014d3f --- /dev/null +++ b/src/bus.cpp @@ -0,0 +1,40 @@ +// +// Created by Palindromic Bread Loaf on 7/21/25. +// + +#include "bus.h" + +// Bus class +uint8_t Bus::Read(uint32_t address) { + // TODO: Map properly based on SNES memory map + if (address < 0x2000) { + return wram[address]; + } else if (address >= 0x7E0000 && address < 0x800000) { + return wram[address - 0x7E0000]; + } else if (address >= 0x800000 && cartridge) { + // ROM access + uint32_t rom_addr = address & 0x7FFFFF; + if (rom_addr < cartridge->size()) { + return (*cartridge)[rom_addr]; + } + } + return 0x00; // Open bus +} + +void Bus::Write(uint32_t address, uint8_t value) { + if (address < 0x2000) { + wram[address] = value; + } else if (address >= 0x7E0000 && address < 0x800000) { + wram[address - 0x7E0000] = value; + } + // TODO: Add PPU/APU register writes here +} + +uint16_t Bus::Read16(uint32_t address) { + return Read(address) | (Read(address + 1) << 8); +} + +void Bus::Write16(uint32_t address, uint16_t value) { + Write(address, value & 0xFF); + Write(address + 1, (value >> 8) & 0xFF); +} diff --git a/src/bus.h b/src/bus.h new file mode 100644 index 0000000..b77b967 --- /dev/null +++ b/src/bus.h @@ -0,0 +1,29 @@ +// +// Created by Palindromic Bread Loaf on 7/21/25. +// + +#ifndef BUS_H +#define BUS_H +#include +#include + +// Memory Bus - handles memory mapping +class Bus { +private: + uint8_t wram[0x20000]; // 128KB Work RAM + uint8_t sram[0x8000]; // 32KB Save RAM + std::vector* cartridge; // Cartridge Data + +public: + Bus(std::vector* cart) : cartridge(cart) { + std::fill(wram, wram + sizeof(wram), 0); + std::fill(sram, sram + sizeof(sram), 0); + } + + uint8_t Read(uint32_t address); + void Write(uint32_t address, uint8_t value); + uint16_t Read16(uint32_t address); + void Write16(uint32_t address, uint16_t value); +}; + +#endif //BUS_H diff --git a/src/cpu.cpp b/src/cpu.cpp new file mode 100644 index 0000000..5e49375 --- /dev/null +++ b/src/cpu.cpp @@ -0,0 +1,54 @@ +// +// Created by Palindromic Bread Loaf on 7/21/25. +// + +#include "cpu.h" + +#include + +// CPU Implementation +void CPU::Reset() { + A = X = Y = 0; + SP = 0x01FF; + PC = 0x8000; // Will be loaded from reset vector + P = 0x34; // Start in emulation mode + DB = PB = 0; + D = 0; + cycles = 0; +} + +void CPU::Step() { + ExecuteInstruction(); +} + +void CPU::ExecuteInstruction() { + uint8_t opcode = bus->Read(PC++); + + // TODO: Actual Opcode decoding + switch (opcode) { + case 0xEA: NOP(); break; + case 0xA9: LDA(); break; + + default: + std::cout << "Unknown opcode: 0x" << std::hex << (int)opcode << std::endl; + break; + } + + cycles++; +} + +void CPU::NOP() { + // No operation +} + +void CPU::LDA() { + // Load accumulator - immediate mode + if (P & FLAG_M) { + // 8-bit mode + A = (A & 0xFF00) | bus->Read(PC++); + } else { + // 16-bit mode + A = bus->Read16(PC); + PC += 2; + } +} \ No newline at end of file diff --git a/src/cpu.h b/src/cpu.h new file mode 100644 index 0000000..7052df4 --- /dev/null +++ b/src/cpu.h @@ -0,0 +1,55 @@ +// +// Created by Palindromic Bread Loaf on 7/21/25. +// + +#ifndef CPU_H +#define CPU_H +#include "bus.h" + +// 65816 CPU implementation +class CPU { +private: + // Registers + uint16_t A; // Accumulator + uint16_t X, Y; // Index registers + uint16_t SP; // Stack pointer + uint32_t PC; // Program counter (24-bit) + uint8_t P; // Processor status + uint8_t DB; // Data bank + uint8_t PB; // Program bank + uint16_t D; // Direct page + + Bus* bus; + uint64_t cycles; + + // Status flags + enum Flags { + FLAG_C = 0x01, // Carry + FLAG_Z = 0x02, // Zero + FLAG_I = 0x04, // IRQ disable + FLAG_D = 0x08, // Decimal mode + FLAG_X = 0x10, // Index register size (0=16-bit, 1=8-bit) + FLAG_M = 0x20, // Memory/Accumulator size (0=16-bit, 1=8-bit) + FLAG_V = 0x40, // Overflow + FLAG_N = 0x80 // Negative + }; + +public: + CPU(Bus* memory_bus) : bus(memory_bus) { + Reset(); + } + + void Reset(); + void Step(); + void ExecuteInstruction(); + uint64_t GetCycles() const { return cycles; } + + // Instruction implementations + // TODO: Implement remaining instructions + void NOP(); + void LDA(); + void STA(); + void JMP(); +}; + +#endif //CPU_H diff --git a/src/main.cpp b/src/main.cpp index a5d5cea..90d6931 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,355 +5,13 @@ #include #include #include -#include -#include +#include "system.h" class CPU; class PPU; class APU; class Bus; -// Main SNES System class -class System { -private: - std::unique_ptr cpu; - std::unique_ptr ppu; - std::unique_ptr apu; - std::unique_ptr bus; - - std::vector cartridge_data; - bool running; - -public: - System(); - ~System(); - - bool LoadROM(const std::string& filename); - void Reset(); - void Run(); - void Step(); - void Shutdown(); -}; - -// Memory Bus - handles memory mapping -class Bus { -private: - uint8_t wram[0x20000]; // 128KB Work RAM - uint8_t sram[0x8000]; // 32KB Save RAM - std::vector* cartridge; // Cartridge Data - -public: - Bus(std::vector* cart) : cartridge(cart) { - std::fill(wram, wram + sizeof(wram), 0); - std::fill(sram, sram + sizeof(sram), 0); - } - - uint8_t Read(uint32_t address); - void Write(uint32_t address, uint8_t value); - uint16_t Read16(uint32_t address); - void Write16(uint32_t address, uint16_t value); -}; - -// 65816 CPU implementation -class CPU { -private: - // Registers - uint16_t A; // Accumulator - uint16_t X, Y; // Index registers - uint16_t SP; // Stack pointer - uint32_t PC; // Program counter (24-bit) - uint8_t P; // Processor status - uint8_t DB; // Data bank - uint8_t PB; // Program bank - uint16_t D; // Direct page - - Bus* bus; - uint64_t cycles; - - // Status flags - enum Flags { - FLAG_C = 0x01, // Carry - FLAG_Z = 0x02, // Zero - FLAG_I = 0x04, // IRQ disable - FLAG_D = 0x08, // Decimal mode - FLAG_X = 0x10, // Index register size (0=16-bit, 1=8-bit) - FLAG_M = 0x20, // Memory/Accumulator size (0=16-bit, 1=8-bit) - FLAG_V = 0x40, // Overflow - FLAG_N = 0x80 // Negative - }; - -public: - CPU(Bus* memory_bus) : bus(memory_bus) { - Reset(); - } - - void Reset(); - void Step(); - void ExecuteInstruction(); - uint64_t GetCycles() const { return cycles; } - - // Instruction implementations - // TODO: Implement remaining instructions - void NOP(); - void LDA(); - void STA(); - void JMP(); -}; - -// PPU (Picture Processing Unit) -class PPU { -private: - uint8_t vram[0x10000]; // 64KB Video RAM - uint8_t oam[0x220]; // Object Attribute Memory - uint8_t cgram[0x200]; // Color Generator RAM - - uint16_t scanline; - uint16_t dot; - bool frame_complete; - - // PPU registers - uint8_t brightness; - uint8_t bg_mode; - // TODO: Add remaining registers - -public: - PPU() { - Reset(); - } - - void Reset(); - void Step(); - bool IsFrameComplete() const { return frame_complete; } - void SetFrameComplete(bool complete) { frame_complete = complete; } - - uint8_t ReadVRAM(uint16_t address); - void WriteVRAM(uint16_t address, uint8_t value); - - // TODO: Implement Rendering - void RenderScanline(); - void UpdateScreen(); -}; - -// SPC700 APU -class APU { -private: - uint8_t spc_ram[0x10000]; // 64KB SPC700 RAM - - // APU registers - uint8_t A, X, Y, SP; - uint16_t PC; - uint8_t PSW; - -public: - APU() { - Reset(); - } - - void Reset(); - void Step(); - - uint8_t ReadSPC(uint16_t address); - void WriteSPC(uint16_t address, uint8_t value); -}; - -// Bus class -uint8_t Bus::Read(uint32_t address) { - // TODO: Map properly based on SNES memory map - if (address < 0x2000) { - return wram[address]; - } else if (address >= 0x7E0000 && address < 0x800000) { - return wram[address - 0x7E0000]; - } else if (address >= 0x800000 && cartridge) { - // ROM access - uint32_t rom_addr = address & 0x7FFFFF; - if (rom_addr < cartridge->size()) { - return (*cartridge)[rom_addr]; - } - } - return 0x00; // Open bus -} - -void Bus::Write(uint32_t address, uint8_t value) { - if (address < 0x2000) { - wram[address] = value; - } else if (address >= 0x7E0000 && address < 0x800000) { - wram[address - 0x7E0000] = value; - } - // TODO: Add PPU/APU register writes here -} - -uint16_t Bus::Read16(uint32_t address) { - return Read(address) | (Read(address + 1) << 8); -} - -void Bus::Write16(uint32_t address, uint16_t value) { - Write(address, value & 0xFF); - Write(address + 1, (value >> 8) & 0xFF); -} - -// CPU Implementation -void CPU::Reset() { - A = X = Y = 0; - SP = 0x01FF; - PC = 0x8000; // Will be loaded from reset vector - P = 0x34; // Start in emulation mode - DB = PB = 0; - D = 0; - cycles = 0; -} - -void CPU::Step() { - ExecuteInstruction(); -} - -void CPU::ExecuteInstruction() { - uint8_t opcode = bus->Read(PC++); - - // TODO: Actual Opcode decoding - switch (opcode) { - case 0xEA: NOP(); break; - case 0xA9: LDA(); break; - - default: - std::cout << "Unknown opcode: 0x" << std::hex << (int)opcode << std::endl; - break; - } - - cycles++; -} - -void CPU::NOP() { - // No operation -} - -void CPU::LDA() { - // Load accumulator - immediate mode - if (P & FLAG_M) { - // 8-bit mode - A = (A & 0xFF00) | bus->Read(PC++); - } else { - // 16-bit mode - A = bus->Read16(PC); - PC += 2; - } -} - -// PPU Implementation -void PPU::Reset() { - scanline = 0; - dot = 0; - frame_complete = false; - brightness = 0x0F; - bg_mode = 0; - - std::fill(vram, vram + sizeof(vram), 0); - std::fill(oam, oam + sizeof(oam), 0); - std::fill(cgram, cgram + sizeof(cgram), 0); -} - -void PPU::Step() { - dot++; - if (dot >= 341) { - dot = 0; - scanline++; - - if (scanline >= 262) { - scanline = 0; - frame_complete = true; - } - } - - // TODO: Implement PPU renderer -} - -uint8_t PPU::ReadVRAM(uint16_t address) { - return vram[address & 0xFFFF]; -} - -void PPU::WriteVRAM(uint16_t address, uint8_t value) { - vram[address & 0xFFFF] = value; -} - -// APU Implementation -void APU::Reset() { - A = X = Y = 0; - SP = 0xFF; - PC = 0x0000; - PSW = 0x02; - - std::fill(spc_ram, spc_ram + sizeof(spc_ram), 0); -} - -void APU::Step() { - // TODO: Implement SPC700 instruction execution -} - -uint8_t APU::ReadSPC(uint16_t address) { - return spc_ram[address]; -} - -void APU::WriteSPC(uint16_t address, uint8_t value) { - spc_ram[address] = value; -} - -// SNES System Implementation -System::System() : running(false) { - bus = std::make_unique(&cartridge_data); - cpu = std::make_unique(bus.get()); - ppu = std::make_unique(); - apu = std::make_unique(); -} - -System::~System() { - Shutdown(); -} - -bool System::LoadROM(const std::string& filename) { - std::ifstream file(filename, std::ios::binary); - if (!file) { - std::cout << "Failed to open ROM file: " << filename << std::endl; - return false; - } - - file.seekg(0, std::ios::end); - size_t size = file.tellg(); - file.seekg(0, std::ios::beg); - - cartridge_data.resize(size); - file.read(reinterpret_cast(cartridge_data.data()), size); - - std::cout << "Loaded ROM: " << filename << " (" << size << " bytes)" << std::endl; - return true; -} - -void System::Reset() { - cpu->Reset(); - ppu->Reset(); - apu->Reset(); -} - -void System::Step() { - cpu->Step(); - ppu->Step(); - apu->Step(); -} - -void System::Run() { - running = true; - while (running) { - Step(); - - if (ppu->IsFrameComplete()) { - ppu->SetFrameComplete(false); - // TODO: Handle frame rendering and input - } - } -} - -void System::Shutdown() { - running = false; -} - int main(int argc, char* argv[]) { if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) { std::cout << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl; diff --git a/src/ppu.cpp b/src/ppu.cpp new file mode 100644 index 0000000..9e1aa15 --- /dev/null +++ b/src/ppu.cpp @@ -0,0 +1,43 @@ +// +// Created by Palindromic Bread Loaf on 7/21/25. +// + +#include "ppu.h" + +#include + +// PPU Implementation +void PPU::Reset() { + scanline = 0; + dot = 0; + frame_complete = false; + brightness = 0x0F; + bg_mode = 0; + + std::fill(vram, vram + sizeof(vram), 0); + std::fill(oam, oam + sizeof(oam), 0); + std::fill(cgram, cgram + sizeof(cgram), 0); +} + +void PPU::Step() { + dot++; + if (dot >= 341) { + dot = 0; + scanline++; + + if (scanline >= 262) { + scanline = 0; + frame_complete = true; + } + } + + // TODO: Implement PPU renderer +} + +uint8_t PPU::ReadVRAM(uint16_t address) { + return vram[address & 0xFFFF]; +} + +void PPU::WriteVRAM(uint16_t address, uint8_t value) { + vram[address & 0xFFFF] = value; +} \ No newline at end of file diff --git a/src/ppu.h b/src/ppu.h new file mode 100644 index 0000000..2ff96eb --- /dev/null +++ b/src/ppu.h @@ -0,0 +1,43 @@ +// +// Created by Palindromic Bread Loaf on 7/21/25. +// + +#ifndef PPU_H +#define PPU_H +#include + +// PPU (Picture Processing Unit) +class PPU { +private: + uint8_t vram[0x10000]; // 64KB Video RAM + uint8_t oam[0x220]; // Object Attribute Memory + uint8_t cgram[0x200]; // Color Generator RAM + + uint16_t scanline; + uint16_t dot; + bool frame_complete; + + // PPU registers + uint8_t brightness; + uint8_t bg_mode; + // TODO: Add remaining registers + +public: + PPU() { + Reset(); + } + + void Reset(); + void Step(); + [[nodiscard]] bool IsFrameComplete() const { return frame_complete; } + void SetFrameComplete(bool complete) { frame_complete = complete; } + + std::uint8_t ReadVRAM(uint16_t address); + void WriteVRAM(uint16_t address, uint8_t value); + + // TODO: Implement Rendering + void RenderScanline(); + void UpdateScreen(); +}; + +#endif //PPU_H diff --git a/src/system.cpp b/src/system.cpp new file mode 100644 index 0000000..bb7bed9 --- /dev/null +++ b/src/system.cpp @@ -0,0 +1,68 @@ +// +// Created by Palindromic Bread Loaf on 7/21/25. +// + +#include "system.h" + +#include +#include + +#include "bus.h" + +// SNES System Implementation +System::System() : running(false) { + bus = std::make_unique(&cartridge_data); + cpu = std::make_unique(bus.get()); + ppu = std::make_unique(); + apu = std::make_unique(); +} + +System::~System() { + Shutdown(); +} + +bool System::LoadROM(const std::string& filename) { + std::ifstream file(filename, std::ios::binary); + if (!file) { + std::cout << "Failed to open ROM file: " << filename << std::endl; + return false; + } + + file.seekg(0, std::ios::end); + size_t size = file.tellg(); + file.seekg(0, std::ios::beg); + + cartridge_data.resize(size); + file.read(reinterpret_cast(cartridge_data.data()), size); + + std::cout << "Loaded ROM: " << filename << " (" << size << " bytes)" << std::endl; + return true; +} + +void System::Reset() { + cpu->Reset(); + ppu->Reset(); + apu->Reset(); +} + +void System::Step() { + cpu->Step(); + ppu->Step(); + apu->Step(); +} + +void System::Run() { + running = true; + while (running) { + Step(); + + if (ppu->IsFrameComplete()) { + ppu->SetFrameComplete(false); + // TODO: Handle frame rendering and input + } + } +} + +void System::Shutdown() { + running = false; +} \ No newline at end of file diff --git a/src/system.h b/src/system.h new file mode 100644 index 0000000..06378ef --- /dev/null +++ b/src/system.h @@ -0,0 +1,37 @@ +// +// Created by Palindromic Bread Loaf on 7/21/25. +// + +#ifndef SYSTEM_H +#define SYSTEM_H + +#include +#include "cpu.h" +#include "ppu.h" +#include "apu.h" +#include "bus.h" + + +// Main SNES System class +class System { +private: + std::unique_ptr cpu; + std::unique_ptr ppu; + std::unique_ptr apu; + std::unique_ptr bus; + + std::vector cartridge_data; + bool running; + +public: + System(); + ~System(); + + bool LoadROM(const std::string& filename); + void Reset(); + void Run(); + void Step(); + void Shutdown(); +}; + +#endif //SYSTEM_H