#include "NES.hpp" #include const unsigned int VIDEO_SCALE = 4u; const unsigned int WINDOW_WIDTH = VIDEO_WIDTH * VIDEO_SCALE; const unsigned int WINDOW_HEIGHT = VIDEO_HEIGHT * VIDEO_SCALE; const unsigned int VIDEO_PITCH = VIDEO_WIDTH * 4u; const unsigned int PPU_CYCLES_PER_CPU_CYCLE = 3u; void DrawDebugLines(uint32_t* buffer) { for (int y = 0; y < VIDEO_HEIGHT; ++y) { for (int x = 0; x < VIDEO_WIDTH; ++x) { if (x == 0 || y == 0) { continue; } if (x % 8 == 0u) { buffer[y * VIDEO_WIDTH + x] = 0xFF00FFFFu; } if (x % 32 == 0u) { buffer[y * VIDEO_WIDTH + x] = 0xFF0000FFu; } if (y % 8 == 0u) { buffer[y * VIDEO_WIDTH + x] = 0xFF00FFFFu; } if (y % 32 == 0u) { buffer[y * VIDEO_WIDTH + x] = 0xFF0000FFu; } } } } NES::NES() : cpu(std::make_unique()), ppu(std::make_unique()), cartridge(std::make_unique()), platform(std::make_unique("NES", WINDOW_WIDTH, WINDOW_HEIGHT, VIDEO_WIDTH, VIDEO_HEIGHT)), input(std::make_unique()) { ppu->nes = this; palette[0] = Color(84, 84, 84); palette[1] = Color(0, 30, 116); palette[2] = Color(8, 16, 144); palette[3] = Color(48, 0, 136); palette[4] = Color(68, 0, 100); palette[5] = Color(92, 0, 48); palette[6] = Color(84, 4, 0); palette[7] = Color(60, 24, 0); palette[8] = Color(32, 42, 0); palette[9] = Color(8, 58, 0); palette[10] = Color(0, 64, 0); palette[11] = Color(0, 60, 0); palette[12] = Color(0, 50, 60); palette[13] = Color(0, 0, 0); palette[14] = Color(0, 0, 0); palette[15] = Color(0, 0, 0); palette[16] = Color(152, 150, 152); palette[17] = Color(8, 76, 196); palette[18] = Color(48, 50, 236); palette[19] = Color(92, 30, 228); palette[20] = Color(136, 20, 176); palette[21] = Color(160, 20, 100); palette[22] = Color(152, 34, 32); palette[23] = Color(120, 60, 0); palette[24] = Color(84, 90, 0); palette[25] = Color(40, 114, 0); palette[26] = Color(8, 124, 0); palette[27] = Color(0, 118, 40); palette[28] = Color(0, 102, 120); palette[29] = Color(0, 0, 0); palette[30] = Color(0, 0, 0); palette[31] = Color(0, 0, 0); palette[32] = Color(236, 238, 236); palette[33] = Color(76, 154, 236); palette[34] = Color(120, 124, 236); palette[35] = Color(176, 98, 236); palette[36] = Color(228, 84, 236); palette[37] = Color(236, 88, 180); palette[38] = Color(236, 106, 100); palette[39] = Color(212, 136, 32); palette[40] = Color(160, 170, 0); palette[41] = Color(116, 196, 0); palette[42] = Color(76, 208, 32); palette[43] = Color(56, 204, 108); palette[44] = Color(56, 180, 204); palette[45] = Color(60, 60, 60); palette[46] = Color(0, 0, 0); palette[47] = Color(0, 0, 0); palette[48] = Color(236, 238, 236); palette[49] = Color(168, 204, 236); palette[50] = Color(188, 188, 236); palette[51] = Color(212, 178, 236); palette[52] = Color(236, 174, 236); palette[53] = Color(236, 174, 212); palette[54] = Color(236, 180, 176); palette[55] = Color(228, 196, 144); palette[56] = Color(204, 210, 120); palette[57] = Color(180, 222, 120); palette[58] = Color(168, 226, 144); palette[59] = Color(152, 226, 180); palette[60] = Color(160, 214, 228); palette[61] = Color(160, 162, 160); palette[62] = Color(0, 0, 0); palette[63] = Color(0, 0, 0); } void NES::InsertCartridge(char const* filename) { cartridge->Load(filename); cpu->nes = this; cpu->Reset(); } void NES::Run() { uint8_t buttons[8]{}; while (!platform->ProcessInput(buttons)) { input->Strobe(buttons); uint64_t cpuCycles = cpu->Cycle(); for (auto i = 0; i < cpuCycles * PPU_CYCLES_PER_CPU_CYCLE; ++i) { ppu->Tick(); } if (ppu->currentScanline == 261) { //DrawDebugLines(ppu->video); platform->Update(ppu->video, VIDEO_PITCH); } } } void NES::Write(BusSource source, uint16_t address, uint8_t value) { switch (source) { case BusSource::CPU: { // CPU RAM and Mirrors if (address < 0x2000u) { uint16_t index = address & 0x7FFu; cpu->ram[index] = value; } // PPU Registers and Mirrors else if (address < 0x4000u) { auto index = static_cast(address % 8u); ppu->WriteRegister(index, value); } // APU and IO else if (address < 0x4020u) { uint16_t index = address & 0x1Fu; // PPU OAM if (index == 0x14u) { uint8_t addrMsb = value; for (uint16_t addrLsb = 0x00; addrLsb <= 0xFF; ++addrLsb) { uint16_t addr = (addrMsb << 8u) | addrLsb; ppu->oam[ppu->regOAMADDR + addrLsb] = Read(BusSource::CPU, addr); } } // Controller else if (index == 0x16u) { if (value & 0x1u) { input->strobe = true; } else { input->strobe = false; input->buttonIndex = 0u; } } } // Expansion I/O else if (address < 0x6000u) { } // SRAM else if (address < 0x8000u) { uint16_t index = address & 0x1FFFu; cpu->sram[index] = value; } break; } case BusSource::PPU: { // Name Tables and Attribute Tables if (address > 0x2000u && address < 0x3000u) { uint16_t index = address & 0x3FFu; ppu->ram[index] = value; } // Mirrors of $2000 - $2EFF else if (address < 0x3F00u) { uint16_t index = address & 0xEFFu; ppu->ram[index] = value; } // Palette RAM Indexes and Mirrors else if (address < 0x3F20u) { uint8_t index = (address & 0x1Cu) >> 2u; uint8_t entry = address & 0x03u; ppu->palette[index][entry] = value; } break; } } } uint8_t NES::Read(BusSource source, uint16_t address) { uint8_t byte{}; switch (source) { case BusSource::CPU: { // CPU RAM and Mirrors if (address < 0x2000u) { uint16_t index = address & 0x7FFu; byte = cpu->ram[index]; } // PPU Registers and Mirrors else if (address < 0x4000u) { auto index = static_cast(address % 8u); byte = ppu->ReadRegister(index); } // APU and IO else if (address < 0x4020u) { uint16_t index = address & 0x1Fu; // Controller if (index == 0x16u) { byte = input->Poll(); } } // Expansion I/O else if (address < 0x6000u) { } // SRAM else if (address < 0x8000u) { uint16_t index = address & 0x1FFFu; byte = cpu->sram[index]; } // CHR-ROM else if (address < 0xC000u) { uint16_t index = address & 0x3FFFu; byte = cartridge->chrROM[index]; } // PRG-ROM else if (address < 0xFFFFu) { uint16_t index = address & 0x3FFFu; byte = cartridge->prgROM[index]; } break; } case BusSource::PPU: { // Cartridge ROM if (address < 0x2000u) { uint16_t index = address & 0x1FFFu; byte = cartridge->chrROM[index]; } // Name Tables and Attribute Tables else if (address < 0x3000u) { uint16_t index = address & 0x3FFu; byte = ppu->ram[index]; } // Mirrors of $2000 - $2EFF else if (address < 0x3F00u) { uint16_t index = address & 0xEFFu; byte = ppu->ram[index]; } // Palette RAM Indexes and Mirrors else if (address < 0x3F20u) { uint8_t index = address & 0x0Cu; uint8_t entry = address & 0x03u; byte = ppu->palette[index][entry]; } break; } } return byte; }