Split project into multiple files
This commit is contained in:
29
src/apu.cpp
Normal file
29
src/apu.cpp
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
//
|
||||||
|
// Created by Palindromic Bread Loaf on 7/21/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "apu.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
31
src/apu.h
Normal file
31
src/apu.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
//
|
||||||
|
// Created by Palindromic Bread Loaf on 7/21/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef APU_H
|
||||||
|
#define APU_H
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
// 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
|
||||||
40
src/bus.cpp
Normal file
40
src/bus.cpp
Normal file
@@ -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);
|
||||||
|
}
|
||||||
29
src/bus.h
Normal file
29
src/bus.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
//
|
||||||
|
// Created by Palindromic Bread Loaf on 7/21/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BUS_H
|
||||||
|
#define BUS_H
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// Memory Bus - handles memory mapping
|
||||||
|
class Bus {
|
||||||
|
private:
|
||||||
|
uint8_t wram[0x20000]; // 128KB Work RAM
|
||||||
|
uint8_t sram[0x8000]; // 32KB Save RAM
|
||||||
|
std::vector<uint8_t>* cartridge; // Cartridge Data
|
||||||
|
|
||||||
|
public:
|
||||||
|
Bus(std::vector<uint8_t>* 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
|
||||||
54
src/cpu.cpp
Normal file
54
src/cpu.cpp
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
//
|
||||||
|
// Created by Palindromic Bread Loaf on 7/21/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "cpu.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
55
src/cpu.h
Normal file
55
src/cpu.h
Normal file
@@ -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
|
||||||
344
src/main.cpp
344
src/main.cpp
@@ -5,355 +5,13 @@
|
|||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <vector>
|
#include "system.h"
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
class CPU;
|
class CPU;
|
||||||
class PPU;
|
class PPU;
|
||||||
class APU;
|
class APU;
|
||||||
class Bus;
|
class Bus;
|
||||||
|
|
||||||
// Main SNES System class
|
|
||||||
class System {
|
|
||||||
private:
|
|
||||||
std::unique_ptr<CPU> cpu;
|
|
||||||
std::unique_ptr<PPU> ppu;
|
|
||||||
std::unique_ptr<APU> apu;
|
|
||||||
std::unique_ptr<Bus> bus;
|
|
||||||
|
|
||||||
std::vector<uint8_t> 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<uint8_t>* cartridge; // Cartridge Data
|
|
||||||
|
|
||||||
public:
|
|
||||||
Bus(std::vector<uint8_t>* 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<Bus>(&cartridge_data);
|
|
||||||
cpu = std::make_unique<CPU>(bus.get());
|
|
||||||
ppu = std::make_unique<PPU>();
|
|
||||||
apu = std::make_unique<APU>();
|
|
||||||
}
|
|
||||||
|
|
||||||
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<char*>(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[]) {
|
int main(int argc, char* argv[]) {
|
||||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
|
||||||
std::cout << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl;
|
std::cout << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl;
|
||||||
|
|||||||
43
src/ppu.cpp
Normal file
43
src/ppu.cpp
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
//
|
||||||
|
// Created by Palindromic Bread Loaf on 7/21/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "ppu.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
43
src/ppu.h
Normal file
43
src/ppu.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
//
|
||||||
|
// Created by Palindromic Bread Loaf on 7/21/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef PPU_H
|
||||||
|
#define PPU_H
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
// 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
|
||||||
68
src/system.cpp
Normal file
68
src/system.cpp
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
//
|
||||||
|
// Created by Palindromic Bread Loaf on 7/21/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "system.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "bus.h"
|
||||||
|
|
||||||
|
// SNES System Implementation
|
||||||
|
System::System() : running(false) {
|
||||||
|
bus = std::make_unique<Bus>(&cartridge_data);
|
||||||
|
cpu = std::make_unique<CPU>(bus.get());
|
||||||
|
ppu = std::make_unique<PPU>();
|
||||||
|
apu = std::make_unique<APU>();
|
||||||
|
}
|
||||||
|
|
||||||
|
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<char*>(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;
|
||||||
|
}
|
||||||
37
src/system.h
Normal file
37
src/system.h
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
//
|
||||||
|
// Created by Palindromic Bread Loaf on 7/21/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef SYSTEM_H
|
||||||
|
#define SYSTEM_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include "cpu.h"
|
||||||
|
#include "ppu.h"
|
||||||
|
#include "apu.h"
|
||||||
|
#include "bus.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Main SNES System class
|
||||||
|
class System {
|
||||||
|
private:
|
||||||
|
std::unique_ptr<CPU> cpu;
|
||||||
|
std::unique_ptr<PPU> ppu;
|
||||||
|
std::unique_ptr<APU> apu;
|
||||||
|
std::unique_ptr<Bus> bus;
|
||||||
|
|
||||||
|
std::vector<uint8_t> cartridge_data;
|
||||||
|
bool running;
|
||||||
|
|
||||||
|
public:
|
||||||
|
System();
|
||||||
|
~System();
|
||||||
|
|
||||||
|
bool LoadROM(const std::string& filename);
|
||||||
|
void Reset();
|
||||||
|
void Run();
|
||||||
|
void Step();
|
||||||
|
void Shutdown();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //SYSTEM_H
|
||||||
Reference in New Issue
Block a user