1
0
Fork 0
2019-nes-emulator/Source/NES.cpp

375 lines
7.1 KiB
C++

#include "NES.hpp"
#include <chrono>
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<CPU>()),
ppu(std::make_unique<PPU>()),
cartridge(std::make_unique<Cartridge>()),
platform(std::make_unique<Platform>("NES", WINDOW_WIDTH, WINDOW_HEIGHT, VIDEO_WIDTH, VIDEO_HEIGHT)),
input(std::make_unique<Input>())
{
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<PPU::Register>(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<PPU::Register>(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;
}