#include "Bus.hpp" #include "Log.hpp" #include #include "controllers/StandardController.hpp" Bus::Bus(Screen* screen) : cpu(this), ppu(this, screen), apu(this), cartridge(this) { LOG_CORE_INFO("Allocating RAM"); RAM = std::vector(0x800); LOG_CORE_INFO("Allocating VRAM"); VRAM = std::vector(0x800); palettes = std::vector(0x20, 0); LOG_CORE_INFO("Inserting cartridge"); cartridge.Load("roms/mario.nes"); LOG_CORE_INFO("Powering up CPU"); cpu.Powerup(); LOG_CORE_INFO("Powering up PPU"); ppu.Powerup(); LOG_CORE_INFO("Powering up APU"); apu.Powerup(); controllerPort.PlugInController(0); } void Bus::Reboot() { cpu.Powerup(); ppu.Powerup(); apu.Powerup(); } void Bus::Reset() { cpu.Reset(); ppu.Reset(); apu.Reset(); } uint8_t Bus::Tick() { controllerPort.Tick(); uint8_t result = cpu.Tick(); // 3 ppu ticks per cpu tick ppu.Tick(); ppu.Tick(); ppu.Tick(); // APU is only ticked every 2 cycles, but that logic // is handled inside the APU class apu.Tick(); return result; } void Bus::PPUTick() { if (ppuClock == 0) { cpu.Tick(); apu.Tick(); } ppu.Tick(); ppuClock = (ppuClock + 1) % 3; } bool Bus::Instruction() { try { while (Tick()); } catch (const std::runtime_error& err) { LOG_CORE_FATAL("Fatal Bus error: {0}", err.what()); cpu.Halt(); return true; } return true; } bool Bus::Frame() { try { while (!ppu.IsFrameDone()) Tick(); } catch (const std::runtime_error& err) { LOG_CORE_FATAL("Fatal Bus error: {0}", err.what()); cpu.Halt(); return true; } return true; } Byte Bus::ReadCPU(Word addr) { if (0x0000 <= addr && addr < 0x2000) { return RAM[addr & 0x7FF]; } else if (0x2000 <= addr && addr < 0x4000) { return ppu.ReadRegister(addr & 0x7); } else if (0x8000 <= addr && addr <= 0xFFFF) { return cartridge.ReadCPU(addr); } else if (0x4000 <= addr && addr <= 0x4017) { switch (addr) { case 0x4016: case 0x4017: return controllerPort.Read(addr); } } return 0x00; } Byte Bus::ReadPPU(Word addr) { addr &= 0x3FFF; if (0x0000 <= addr && addr < 0x2000) { return cartridge.ReadPPU(addr); } else if(0x2000 <= addr && addr < 0x3F00) { if (cartridge.MapCIRAM(addr)) return cartridge.ReadVRAM(addr); return VRAM[addr & 0xFFF]; } else if (0x3F00 <= addr && addr < 0x4000) { if ((addr & 0x3) == 0x00) addr &= 0xF; return palettes[addr & 0x1F]; } return 0x00; } void Bus::WriteCPU(Word addr, Byte val) { if (0x0000 <= addr && addr < 0x2000) { if (addr == 0x0348) volatile int jdfkdf = 3; RAM[addr & 0x7FF] = val; } else if (0x2000 <= addr && addr < 0x4000) { ppu.WriteRegister(addr & 0x7, val); } else if (0x8000 <= addr && addr <= 0xFFFF) { cartridge.WriteCPU(addr, val); } else if (0x4000 <= addr && addr <= 0x4017) { switch (addr) { case 0x4016: controllerPort.Write(addr, val); break; case 0x4017: apu.WriteRegister(addr, val); break; } } } void Bus::WritePPU(Word addr, Byte val) { addr &= 0x3FFF; if (0x0000 <= addr && addr < 0x2000) { cartridge.WritePPU(addr, val); } else if (0x2000 <= addr && addr < 0x3F00) { if(cartridge.MapCIRAM(addr)) cartridge.WriteVRAM(addr, val); if (val != 0x00) volatile int jfkd = 3; VRAM[addr & 0xFFF] = val; } else if (0x3F00 <= addr && addr < 0x4000) { if ((addr & 0x3) == 0x00) addr &= 0xF; palettes[addr & 0x1F] = val; } }