CPU functionally complete
This commit is contained in:
parent
54bc4f27a0
commit
f2810585e4
|
@ -0,0 +1,31 @@
|
||||||
|
cmake_minimum_required(VERSION 3.14)
|
||||||
|
project(nes)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 14)
|
||||||
|
|
||||||
|
find_package(SDL2 REQUIRED)
|
||||||
|
|
||||||
|
add_executable(nes)
|
||||||
|
|
||||||
|
target_sources(
|
||||||
|
nes
|
||||||
|
PRIVATE
|
||||||
|
Source/CPU/CPU.cpp
|
||||||
|
Source/CPU/AddressModes.cpp
|
||||||
|
Source/CPU/Instructions.cpp
|
||||||
|
Source/Main.cpp)
|
||||||
|
|
||||||
|
target_compile_options(
|
||||||
|
nes
|
||||||
|
PRIVATE
|
||||||
|
-Wall)
|
||||||
|
|
||||||
|
target_include_directories(
|
||||||
|
nes
|
||||||
|
PRIVATE
|
||||||
|
Source)
|
||||||
|
|
||||||
|
target_link_libraries(
|
||||||
|
nes
|
||||||
|
PRIVATE
|
||||||
|
SDL2::SDL2)
|
|
@ -0,0 +1,151 @@
|
||||||
|
#include "CPU.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
void CPU::Implicit()
|
||||||
|
{
|
||||||
|
pc += 1;
|
||||||
|
|
||||||
|
// Do nothing
|
||||||
|
fetchedByte = acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::Immediate()
|
||||||
|
{
|
||||||
|
pc += 2;
|
||||||
|
|
||||||
|
fetchedAddress = pc - 1;
|
||||||
|
fetchedByte = ReadMemory(fetchedAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::ZeroPage()
|
||||||
|
{
|
||||||
|
pc += 2;
|
||||||
|
|
||||||
|
fetchedAddress = ReadMemory(pc - 1);
|
||||||
|
fetchedByte = ReadMemory(fetchedAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::ZeroPageX()
|
||||||
|
{
|
||||||
|
pc += 2;
|
||||||
|
|
||||||
|
uint8_t operand = ReadMemory(pc - 1);
|
||||||
|
fetchedAddress = (operand + x) & 0xFFu;
|
||||||
|
fetchedByte = ReadMemory(fetchedAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::ZeroPageY()
|
||||||
|
{
|
||||||
|
pc += 2;
|
||||||
|
|
||||||
|
uint8_t operand = ReadMemory(pc - 1);
|
||||||
|
fetchedAddress = (operand + y) & 0xFFu;
|
||||||
|
fetchedByte = ReadMemory(fetchedAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::Absolute()
|
||||||
|
{
|
||||||
|
pc += 3;
|
||||||
|
|
||||||
|
uint8_t addressLsb = ReadMemory(pc - 2);
|
||||||
|
uint8_t addressMsb = ReadMemory(pc - 1);
|
||||||
|
|
||||||
|
fetchedAddress = ComposeAddress(addressMsb, addressLsb);
|
||||||
|
fetchedByte = ReadMemory(fetchedAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::AbsoluteX()
|
||||||
|
{
|
||||||
|
pc += 3;
|
||||||
|
|
||||||
|
uint8_t addressLsb = ReadMemory(pc - 2);
|
||||||
|
uint8_t addressMsb = ReadMemory(pc - 1);
|
||||||
|
|
||||||
|
uint16_t preAddress = ComposeAddress(addressMsb, addressLsb);
|
||||||
|
fetchedAddress = preAddress + x;
|
||||||
|
fetchedByte = ReadMemory(fetchedAddress);
|
||||||
|
|
||||||
|
pageBoundaryCrossed = IsPageBoundaryCrossed(preAddress, fetchedAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::AbsoluteY()
|
||||||
|
{
|
||||||
|
pc += 3;
|
||||||
|
|
||||||
|
uint8_t addressLsb = ReadMemory(pc - 2);
|
||||||
|
uint8_t addressMsb = ReadMemory(pc - 1);
|
||||||
|
|
||||||
|
uint16_t preAddress = ComposeAddress(addressMsb, addressLsb);
|
||||||
|
fetchedAddress = preAddress + y;
|
||||||
|
fetchedByte = ReadMemory(fetchedAddress);
|
||||||
|
|
||||||
|
pageBoundaryCrossed = IsPageBoundaryCrossed(preAddress, fetchedAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::Indirect()
|
||||||
|
{
|
||||||
|
pc += 3;
|
||||||
|
|
||||||
|
uint8_t indirectLsb = ReadMemory(pc - 2);
|
||||||
|
uint8_t indirectMsb = ReadMemory(pc - 1);
|
||||||
|
|
||||||
|
// NOTE: 6502 BUG - indirect address wraps
|
||||||
|
// $02FF + 1 = $0200
|
||||||
|
|
||||||
|
uint16_t addressIndirect = ComposeAddress(indirectMsb, indirectLsb);
|
||||||
|
uint8_t addressLsb = ReadMemory(addressIndirect);
|
||||||
|
|
||||||
|
// Increment LSB to read second byte - it will wrap
|
||||||
|
++indirectLsb;
|
||||||
|
|
||||||
|
addressIndirect = ComposeAddress(indirectMsb, indirectLsb);
|
||||||
|
uint8_t addressMsb = ReadMemory(addressIndirect);
|
||||||
|
|
||||||
|
fetchedAddress = ComposeAddress(addressMsb, addressLsb);
|
||||||
|
fetchedByte = ReadMemory(fetchedAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::IndirectX()
|
||||||
|
{
|
||||||
|
pc += 2;
|
||||||
|
|
||||||
|
uint8_t addressIndirect = ReadMemory(pc - 1) + x;
|
||||||
|
|
||||||
|
// Modulo to keep within zero-page
|
||||||
|
uint16_t addressLsb = ReadMemory((addressIndirect % 0x100));
|
||||||
|
uint16_t addressMsb = ReadMemory((addressIndirect + 1) % 0x100);
|
||||||
|
|
||||||
|
fetchedAddress = ComposeAddress(addressMsb, addressLsb);
|
||||||
|
fetchedByte = ReadMemory(fetchedAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::IndirectY()
|
||||||
|
{
|
||||||
|
pc += 2;
|
||||||
|
|
||||||
|
uint8_t addressIndirect = ReadMemory(pc - 1);
|
||||||
|
|
||||||
|
// Modulo to keep within zero-page
|
||||||
|
uint8_t addressLsb = ReadMemory(addressIndirect % 0x100);
|
||||||
|
uint8_t addressMsb = ReadMemory((addressIndirect + 1) % 0x100);
|
||||||
|
|
||||||
|
uint16_t preAddress = ComposeAddress(addressMsb, addressLsb);
|
||||||
|
fetchedAddress = preAddress + y;
|
||||||
|
fetchedByte = ReadMemory(fetchedAddress);
|
||||||
|
|
||||||
|
pageBoundaryCrossed = IsPageBoundaryCrossed(preAddress, fetchedAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::Relative()
|
||||||
|
{
|
||||||
|
pc += 2;
|
||||||
|
|
||||||
|
int8_t operand = ReadMemory(pc - 1);
|
||||||
|
|
||||||
|
uint16_t oldPc = pc;
|
||||||
|
|
||||||
|
fetchedAddress = pc + operand;
|
||||||
|
fetchedByte = ReadMemory(fetchedAddress);
|
||||||
|
|
||||||
|
pageBoundaryCrossed = IsPageBoundaryCrossed(oldPc, fetchedAddress);
|
||||||
|
}
|
|
@ -0,0 +1,527 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include "CPU.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
const unsigned int STACK_START_ADDRESS = 0x100u;
|
||||||
|
|
||||||
|
CPU::CPU()
|
||||||
|
{
|
||||||
|
status.SetByte(0x24);
|
||||||
|
|
||||||
|
instructions[0xA9] = Instruction{.cycles = 2, .execute = &CPU::LDA, .fetch = &CPU::Immediate};
|
||||||
|
instructions[0xA5] = Instruction{.cycles = 3, .execute = &CPU::LDA, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0xB5] = Instruction{.cycles = 4, .execute = &CPU::LDA, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0xAD] = Instruction{.cycles = 4, .execute = &CPU::LDA, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0xBD] = Instruction{.cycles = 4, .execute = &CPU::LDA, .fetch = &CPU::AbsoluteX};
|
||||||
|
instructions[0xB9] = Instruction{.cycles = 4, .execute = &CPU::LDA, .fetch = &CPU::AbsoluteY};
|
||||||
|
instructions[0xA1] = Instruction{.cycles = 6, .execute = &CPU::LDA, .fetch = &CPU::IndirectX};
|
||||||
|
instructions[0xB1] = Instruction{.cycles = 5, .execute = &CPU::LDA, .fetch = &CPU::IndirectY};
|
||||||
|
|
||||||
|
instructions[0xA2] = Instruction{.cycles = 2, .execute = &CPU::LDX, .fetch = &CPU::Immediate};
|
||||||
|
instructions[0xA6] = Instruction{.cycles = 3, .execute = &CPU::LDX, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0xB6] = Instruction{.cycles = 4, .execute = &CPU::LDX, .fetch = &CPU::ZeroPageY};
|
||||||
|
instructions[0xAE] = Instruction{.cycles = 4, .execute = &CPU::LDX, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0xBE] = Instruction{.cycles = 4, .execute = &CPU::LDX, .fetch = &CPU::AbsoluteY};
|
||||||
|
|
||||||
|
instructions[0xA0] = Instruction{.cycles = 2, .execute = &CPU::LDY, .fetch = &CPU::Immediate};
|
||||||
|
instructions[0xA4] = Instruction{.cycles = 3, .execute = &CPU::LDY, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0xB4] = Instruction{.cycles = 4, .execute = &CPU::LDY, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0xAC] = Instruction{.cycles = 4, .execute = &CPU::LDY, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0xBC] = Instruction{.cycles = 4, .execute = &CPU::LDY, .fetch = &CPU::AbsoluteX};
|
||||||
|
|
||||||
|
instructions[0x85] = Instruction{.cycles = 3, .execute = &CPU::STA, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0x95] = Instruction{.cycles = 4, .execute = &CPU::STA, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0x8D] = Instruction{.cycles = 4, .execute = &CPU::STA, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0x9D] = Instruction{.cycles = 5, .execute = &CPU::STA, .fetch = &CPU::AbsoluteX};
|
||||||
|
instructions[0x99] = Instruction{.cycles = 5, .execute = &CPU::STA, .fetch = &CPU::AbsoluteY};
|
||||||
|
instructions[0x81] = Instruction{.cycles = 6, .execute = &CPU::STA, .fetch = &CPU::IndirectX};
|
||||||
|
instructions[0x91] = Instruction{.cycles = 6, .execute = &CPU::STA, .fetch = &CPU::IndirectY};
|
||||||
|
|
||||||
|
instructions[0x86] = Instruction{.cycles = 3, .execute = &CPU::STX, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0x96] = Instruction{.cycles = 4, .execute = &CPU::STX, .fetch = &CPU::ZeroPageY};
|
||||||
|
instructions[0x8E] = Instruction{.cycles = 4, .execute = &CPU::STX, .fetch = &CPU::Absolute};
|
||||||
|
|
||||||
|
instructions[0x84] = Instruction{.cycles = 3, .execute = &CPU::STY, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0x94] = Instruction{.cycles = 4, .execute = &CPU::STY, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0x8C] = Instruction{.cycles = 4, .execute = &CPU::STY, .fetch = &CPU::Absolute};
|
||||||
|
|
||||||
|
instructions[0xAA] = Instruction{.cycles = 2, .execute = &CPU::TAX, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0xA8] = Instruction{.cycles = 2, .execute = &CPU::TAY, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x8A] = Instruction{.cycles = 2, .execute = &CPU::TXA, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x98] = Instruction{.cycles = 2, .execute = &CPU::TYA, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0xBA] = Instruction{.cycles = 2, .execute = &CPU::TSX, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x9A] = Instruction{.cycles = 2, .execute = &CPU::TXS, .fetch = &CPU::Implicit};
|
||||||
|
|
||||||
|
instructions[0x48] = Instruction{.cycles = 3, .execute = &CPU::PHA, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x08] = Instruction{.cycles = 3, .execute = &CPU::PHP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x68] = Instruction{.cycles = 4, .execute = &CPU::PLA, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x28] = Instruction{.cycles = 4, .execute = &CPU::PLP, .fetch = &CPU::Implicit};
|
||||||
|
|
||||||
|
instructions[0x29] = Instruction{.cycles = 2, .execute = &CPU::AND, .fetch = &CPU::Immediate};
|
||||||
|
instructions[0x25] = Instruction{.cycles = 3, .execute = &CPU::AND, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0x35] = Instruction{.cycles = 4, .execute = &CPU::AND, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0x2D] = Instruction{.cycles = 4, .execute = &CPU::AND, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0x3D] = Instruction{.cycles = 4, .execute = &CPU::AND, .fetch = &CPU::AbsoluteX};
|
||||||
|
instructions[0x39] = Instruction{.cycles = 4, .execute = &CPU::AND, .fetch = &CPU::AbsoluteY};
|
||||||
|
instructions[0x21] = Instruction{.cycles = 6, .execute = &CPU::AND, .fetch = &CPU::IndirectX};
|
||||||
|
instructions[0x31] = Instruction{.cycles = 5, .execute = &CPU::AND, .fetch = &CPU::IndirectY};
|
||||||
|
|
||||||
|
instructions[0x49] = Instruction{.cycles = 2, .execute = &CPU::EOR, .fetch = &CPU::Immediate};
|
||||||
|
instructions[0x45] = Instruction{.cycles = 3, .execute = &CPU::EOR, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0x55] = Instruction{.cycles = 4, .execute = &CPU::EOR, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0x4D] = Instruction{.cycles = 4, .execute = &CPU::EOR, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0x5D] = Instruction{.cycles = 4, .execute = &CPU::EOR, .fetch = &CPU::AbsoluteX};
|
||||||
|
instructions[0x59] = Instruction{.cycles = 4, .execute = &CPU::EOR, .fetch = &CPU::AbsoluteY};
|
||||||
|
instructions[0x41] = Instruction{.cycles = 6, .execute = &CPU::EOR, .fetch = &CPU::IndirectX};
|
||||||
|
instructions[0x51] = Instruction{.cycles = 5, .execute = &CPU::EOR, .fetch = &CPU::IndirectY};
|
||||||
|
|
||||||
|
instructions[0x09] = Instruction{.cycles = 2, .execute = &CPU::ORA, .fetch = &CPU::Immediate};
|
||||||
|
instructions[0x05] = Instruction{.cycles = 3, .execute = &CPU::ORA, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0x15] = Instruction{.cycles = 4, .execute = &CPU::ORA, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0x0D] = Instruction{.cycles = 4, .execute = &CPU::ORA, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0x1D] = Instruction{.cycles = 4, .execute = &CPU::ORA, .fetch = &CPU::AbsoluteX};
|
||||||
|
instructions[0x19] = Instruction{.cycles = 4, .execute = &CPU::ORA, .fetch = &CPU::AbsoluteY};
|
||||||
|
instructions[0x01] = Instruction{.cycles = 6, .execute = &CPU::ORA, .fetch = &CPU::IndirectX};
|
||||||
|
instructions[0x11] = Instruction{.cycles = 5, .execute = &CPU::ORA, .fetch = &CPU::IndirectY};
|
||||||
|
|
||||||
|
instructions[0x24] = Instruction{.cycles = 3, .execute = &CPU::BIT, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0x2C] = Instruction{.cycles = 4, .execute = &CPU::BIT, .fetch = &CPU::Absolute};
|
||||||
|
|
||||||
|
instructions[0x69] = Instruction{.cycles = 2, .execute = &CPU::ADC, .fetch = &CPU::Immediate};
|
||||||
|
instructions[0x65] = Instruction{.cycles = 3, .execute = &CPU::ADC, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0x75] = Instruction{.cycles = 4, .execute = &CPU::ADC, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0x6D] = Instruction{.cycles = 4, .execute = &CPU::ADC, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0x7D] = Instruction{.cycles = 4, .execute = &CPU::ADC, .fetch = &CPU::AbsoluteX};
|
||||||
|
instructions[0x79] = Instruction{.cycles = 4, .execute = &CPU::ADC, .fetch = &CPU::AbsoluteY};
|
||||||
|
instructions[0x61] = Instruction{.cycles = 6, .execute = &CPU::ADC, .fetch = &CPU::IndirectX};
|
||||||
|
instructions[0x71] = Instruction{.cycles = 5, .execute = &CPU::ADC, .fetch = &CPU::IndirectY};
|
||||||
|
|
||||||
|
instructions[0xE9] = Instruction{.cycles = 2, .execute = &CPU::SBC, .fetch = &CPU::Immediate};
|
||||||
|
instructions[0xE5] = Instruction{.cycles = 3, .execute = &CPU::SBC, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0xF5] = Instruction{.cycles = 4, .execute = &CPU::SBC, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0xED] = Instruction{.cycles = 4, .execute = &CPU::SBC, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0xFD] = Instruction{.cycles = 4, .execute = &CPU::SBC, .fetch = &CPU::AbsoluteX};
|
||||||
|
instructions[0xF9] = Instruction{.cycles = 4, .execute = &CPU::SBC, .fetch = &CPU::AbsoluteY};
|
||||||
|
instructions[0xE1] = Instruction{.cycles = 6, .execute = &CPU::SBC, .fetch = &CPU::IndirectX};
|
||||||
|
instructions[0xF1] = Instruction{.cycles = 5, .execute = &CPU::SBC, .fetch = &CPU::IndirectY};
|
||||||
|
|
||||||
|
instructions[0xC9] = Instruction{.cycles = 2, .execute = &CPU::CMP, .fetch = &CPU::Immediate};
|
||||||
|
instructions[0xC5] = Instruction{.cycles = 3, .execute = &CPU::CMP, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0xD5] = Instruction{.cycles = 4, .execute = &CPU::CMP, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0xCD] = Instruction{.cycles = 4, .execute = &CPU::CMP, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0xDD] = Instruction{.cycles = 4, .execute = &CPU::CMP, .fetch = &CPU::AbsoluteX};
|
||||||
|
instructions[0xD9] = Instruction{.cycles = 4, .execute = &CPU::CMP, .fetch = &CPU::AbsoluteY};
|
||||||
|
instructions[0xC1] = Instruction{.cycles = 6, .execute = &CPU::CMP, .fetch = &CPU::IndirectX};
|
||||||
|
instructions[0xD1] = Instruction{.cycles = 5, .execute = &CPU::CMP, .fetch = &CPU::IndirectY};
|
||||||
|
|
||||||
|
instructions[0xE0] = Instruction{.cycles = 2, .execute = &CPU::CPX, .fetch = &CPU::Immediate};
|
||||||
|
instructions[0xE4] = Instruction{.cycles = 3, .execute = &CPU::CPX, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0xEC] = Instruction{.cycles = 4, .execute = &CPU::CPX, .fetch = &CPU::Absolute};
|
||||||
|
|
||||||
|
instructions[0xC0] = Instruction{.cycles = 2, .execute = &CPU::CPY, .fetch = &CPU::Immediate};
|
||||||
|
instructions[0xC4] = Instruction{.cycles = 3, .execute = &CPU::CPY, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0xCC] = Instruction{.cycles = 4, .execute = &CPU::CPY, .fetch = &CPU::Absolute};
|
||||||
|
|
||||||
|
instructions[0xE6] = Instruction{.cycles = 5, .execute = &CPU::INC, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0xF6] = Instruction{.cycles = 6, .execute = &CPU::INC, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0xEE] = Instruction{.cycles = 6, .execute = &CPU::INC, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0xFE] = Instruction{.cycles = 7, .execute = &CPU::INC, .fetch = &CPU::AbsoluteX};
|
||||||
|
|
||||||
|
instructions[0xE8] = Instruction{.cycles = 2, .execute = &CPU::INX, .fetch = &CPU::Implicit};
|
||||||
|
|
||||||
|
instructions[0xC8] = Instruction{.cycles = 2, .execute = &CPU::INY, .fetch = &CPU::Implicit};
|
||||||
|
|
||||||
|
instructions[0xC6] = Instruction{.cycles = 5, .execute = &CPU::DEC, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0xD6] = Instruction{.cycles = 6, .execute = &CPU::DEC, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0xCE] = Instruction{.cycles = 6, .execute = &CPU::DEC, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0xDE] = Instruction{.cycles = 7, .execute = &CPU::DEC, .fetch = &CPU::AbsoluteX};
|
||||||
|
|
||||||
|
instructions[0xCA] = Instruction{.cycles = 2, .execute = &CPU::DEX, .fetch = &CPU::Implicit};
|
||||||
|
|
||||||
|
instructions[0x88] = Instruction{.cycles = 2, .execute = &CPU::DEY, .fetch = &CPU::Implicit};
|
||||||
|
|
||||||
|
instructions[0x0A] = Instruction{.cycles = 2, .execute = &CPU::ASL, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x06] = Instruction{.cycles = 5, .execute = &CPU::ASL, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0x16] = Instruction{.cycles = 6, .execute = &CPU::ASL, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0x0E] = Instruction{.cycles = 6, .execute = &CPU::ASL, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0x1E] = Instruction{.cycles = 7, .execute = &CPU::ASL, .fetch = &CPU::AbsoluteX};
|
||||||
|
|
||||||
|
instructions[0x4A] = Instruction{.cycles = 2, .execute = &CPU::LSR, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x46] = Instruction{.cycles = 5, .execute = &CPU::LSR, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0x56] = Instruction{.cycles = 6, .execute = &CPU::LSR, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0x4E] = Instruction{.cycles = 6, .execute = &CPU::LSR, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0x5E] = Instruction{.cycles = 7, .execute = &CPU::LSR, .fetch = &CPU::AbsoluteX};
|
||||||
|
|
||||||
|
instructions[0x2A] = Instruction{.cycles = 2, .execute = &CPU::ROL, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x26] = Instruction{.cycles = 5, .execute = &CPU::ROL, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0x36] = Instruction{.cycles = 6, .execute = &CPU::ROL, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0x2E] = Instruction{.cycles = 6, .execute = &CPU::ROL, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0x3E] = Instruction{.cycles = 7, .execute = &CPU::ROL, .fetch = &CPU::AbsoluteX};
|
||||||
|
|
||||||
|
instructions[0x6A] = Instruction{.cycles = 2, .execute = &CPU::ROR, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x66] = Instruction{.cycles = 5, .execute = &CPU::ROR, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0x76] = Instruction{.cycles = 6, .execute = &CPU::ROR, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0x6E] = Instruction{.cycles = 6, .execute = &CPU::ROR, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0x7E] = Instruction{.cycles = 7, .execute = &CPU::ROR, .fetch = &CPU::AbsoluteX};
|
||||||
|
|
||||||
|
instructions[0x4C] = Instruction{.cycles = 3, .execute = &CPU::JMP, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0x6C] = Instruction{.cycles = 5, .execute = &CPU::JMP, .fetch = &CPU::Indirect};
|
||||||
|
|
||||||
|
instructions[0x20] = Instruction{.cycles = 6, .execute = &CPU::JSR, .fetch = &CPU::Absolute};
|
||||||
|
|
||||||
|
instructions[0x60] = Instruction{.cycles = 6, .execute = &CPU::RTS, .fetch = &CPU::Implicit};
|
||||||
|
|
||||||
|
instructions[0x90] = Instruction{.cycles = 2, .execute = &CPU::BCC, .fetch = &CPU::Relative};
|
||||||
|
instructions[0xB0] = Instruction{.cycles = 2, .execute = &CPU::BCS, .fetch = &CPU::Relative};
|
||||||
|
instructions[0xF0] = Instruction{.cycles = 2, .execute = &CPU::BEQ, .fetch = &CPU::Relative};
|
||||||
|
instructions[0x30] = Instruction{.cycles = 2, .execute = &CPU::BMI, .fetch = &CPU::Relative};
|
||||||
|
instructions[0xD0] = Instruction{.cycles = 2, .execute = &CPU::BNE, .fetch = &CPU::Relative};
|
||||||
|
instructions[0x10] = Instruction{.cycles = 2, .execute = &CPU::BPL, .fetch = &CPU::Relative};
|
||||||
|
instructions[0x50] = Instruction{.cycles = 2, .execute = &CPU::BVC, .fetch = &CPU::Relative};
|
||||||
|
instructions[0x70] = Instruction{.cycles = 2, .execute = &CPU::BVS, .fetch = &CPU::Relative};
|
||||||
|
|
||||||
|
instructions[0x18] = Instruction{.cycles = 2, .execute = &CPU::CLC, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0xD8] = Instruction{.cycles = 2, .execute = &CPU::CLD, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x58] = Instruction{.cycles = 2, .execute = &CPU::CLI, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0xB8] = Instruction{.cycles = 2, .execute = &CPU::CLV, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x38] = Instruction{.cycles = 2, .execute = &CPU::SEC, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0xF8] = Instruction{.cycles = 2, .execute = &CPU::SED, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x78] = Instruction{.cycles = 2, .execute = &CPU::SEI, .fetch = &CPU::Implicit};
|
||||||
|
|
||||||
|
instructions[0x00] = Instruction{.cycles = 7, .execute = &CPU::BRK, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0xEA] = Instruction{.cycles = 2, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x40] = Instruction{.cycles = 6, .execute = &CPU::RTI, .fetch = &CPU::Implicit};
|
||||||
|
|
||||||
|
instructions[0x1A] = Instruction{.cycles = 2, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x3A] = Instruction{.cycles = 2, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x5A] = Instruction{.cycles = 2, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x7A] = Instruction{.cycles = 2, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0xDA] = Instruction{.cycles = 2, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0xFA] = Instruction{.cycles = 2, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x80] = Instruction{.cycles = 2, .execute = &CPU::NOP, .fetch = &CPU::Immediate};
|
||||||
|
instructions[0x82] = Instruction{.cycles = 2, .execute = &CPU::NOP, .fetch = &CPU::Immediate};
|
||||||
|
instructions[0x89] = Instruction{.cycles = 2, .execute = &CPU::NOP, .fetch = &CPU::Immediate};
|
||||||
|
instructions[0xC2] = Instruction{.cycles = 2, .execute = &CPU::NOP, .fetch = &CPU::Immediate};
|
||||||
|
instructions[0xE2] = Instruction{.cycles = 2, .execute = &CPU::NOP, .fetch = &CPU::Immediate};
|
||||||
|
instructions[0x04] = Instruction{.cycles = 3, .execute = &CPU::NOP, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0x44] = Instruction{.cycles = 3, .execute = &CPU::NOP, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0x64] = Instruction{.cycles = 3, .execute = &CPU::NOP, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0x14] = Instruction{.cycles = 4, .execute = &CPU::NOP, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0x34] = Instruction{.cycles = 4, .execute = &CPU::NOP, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0x54] = Instruction{.cycles = 4, .execute = &CPU::NOP, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0x74] = Instruction{.cycles = 4, .execute = &CPU::NOP, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0xD4] = Instruction{.cycles = 4, .execute = &CPU::NOP, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0xF4] = Instruction{.cycles = 4, .execute = &CPU::NOP, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0x0C] = Instruction{.cycles = 4, .execute = &CPU::NOP, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0x1C] = Instruction{.cycles = 4, .execute = &CPU::NOP, .fetch = &CPU::AbsoluteX};
|
||||||
|
instructions[0x3C] = Instruction{.cycles = 4, .execute = &CPU::NOP, .fetch = &CPU::AbsoluteX};
|
||||||
|
instructions[0x5C] = Instruction{.cycles = 4, .execute = &CPU::NOP, .fetch = &CPU::AbsoluteX};
|
||||||
|
instructions[0x7C] = Instruction{.cycles = 4, .execute = &CPU::NOP, .fetch = &CPU::AbsoluteX};
|
||||||
|
instructions[0xDC] = Instruction{.cycles = 4, .execute = &CPU::NOP, .fetch = &CPU::AbsoluteX};
|
||||||
|
instructions[0xFC] = Instruction{.cycles = 4, .execute = &CPU::NOP, .fetch = &CPU::AbsoluteX};
|
||||||
|
|
||||||
|
instructions[0x07] = Instruction{.cycles = 5, .execute = &CPU::SLO, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0x17] = Instruction{.cycles = 6, .execute = &CPU::SLO, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0x0F] = Instruction{.cycles = 6, .execute = &CPU::SLO, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0x1F] = Instruction{.cycles = 7, .execute = &CPU::SLO, .fetch = &CPU::AbsoluteX};
|
||||||
|
instructions[0x1B] = Instruction{.cycles = 7, .execute = &CPU::SLO, .fetch = &CPU::AbsoluteY};
|
||||||
|
instructions[0x03] = Instruction{.cycles = 8, .execute = &CPU::SLO, .fetch = &CPU::IndirectX};
|
||||||
|
instructions[0x13] = Instruction{.cycles = 8, .execute = &CPU::SLO, .fetch = &CPU::IndirectY};
|
||||||
|
|
||||||
|
instructions[0x27] = Instruction{.cycles = 5, .execute = &CPU::RLA, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0x37] = Instruction{.cycles = 6, .execute = &CPU::RLA, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0x2F] = Instruction{.cycles = 6, .execute = &CPU::RLA, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0x3F] = Instruction{.cycles = 7, .execute = &CPU::RLA, .fetch = &CPU::AbsoluteX};
|
||||||
|
instructions[0x3B] = Instruction{.cycles = 7, .execute = &CPU::RLA, .fetch = &CPU::AbsoluteY};
|
||||||
|
instructions[0x23] = Instruction{.cycles = 8, .execute = &CPU::RLA, .fetch = &CPU::IndirectX};
|
||||||
|
instructions[0x33] = Instruction{.cycles = 8, .execute = &CPU::RLA, .fetch = &CPU::IndirectY};
|
||||||
|
|
||||||
|
instructions[0x47] = Instruction{.cycles = 5, .execute = &CPU::SRE, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0x57] = Instruction{.cycles = 6, .execute = &CPU::SRE, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0x4F] = Instruction{.cycles = 6, .execute = &CPU::SRE, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0x5F] = Instruction{.cycles = 7, .execute = &CPU::SRE, .fetch = &CPU::AbsoluteX};
|
||||||
|
instructions[0x5B] = Instruction{.cycles = 7, .execute = &CPU::SRE, .fetch = &CPU::AbsoluteY};
|
||||||
|
instructions[0x43] = Instruction{.cycles = 8, .execute = &CPU::SRE, .fetch = &CPU::IndirectX};
|
||||||
|
instructions[0x53] = Instruction{.cycles = 8, .execute = &CPU::SRE, .fetch = &CPU::IndirectY};
|
||||||
|
|
||||||
|
instructions[0x67] = Instruction{.cycles = 5, .execute = &CPU::RRA, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0x77] = Instruction{.cycles = 6, .execute = &CPU::RRA, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0x6F] = Instruction{.cycles = 6, .execute = &CPU::RRA, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0x7F] = Instruction{.cycles = 7, .execute = &CPU::RRA, .fetch = &CPU::AbsoluteX};
|
||||||
|
instructions[0x7B] = Instruction{.cycles = 7, .execute = &CPU::RRA, .fetch = &CPU::AbsoluteY};
|
||||||
|
instructions[0x63] = Instruction{.cycles = 8, .execute = &CPU::RRA, .fetch = &CPU::IndirectX};
|
||||||
|
instructions[0x73] = Instruction{.cycles = 8, .execute = &CPU::RRA, .fetch = &CPU::IndirectY};
|
||||||
|
|
||||||
|
instructions[0x87] = Instruction{.cycles = 3, .execute = &CPU::SAX, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0x97] = Instruction{.cycles = 4, .execute = &CPU::SAX, .fetch = &CPU::ZeroPageY};
|
||||||
|
instructions[0x8F] = Instruction{.cycles = 4, .execute = &CPU::SAX, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0x83] = Instruction{.cycles = 6, .execute = &CPU::SAX, .fetch = &CPU::IndirectX};
|
||||||
|
|
||||||
|
instructions[0xEB] = Instruction{.cycles = 2, .execute = &CPU::SBC, .fetch = &CPU::Immediate};
|
||||||
|
|
||||||
|
instructions[0xAB] = Instruction{.cycles = 2, .execute = &CPU::LAX, .fetch = &CPU::Immediate};
|
||||||
|
instructions[0xA7] = Instruction{.cycles = 3, .execute = &CPU::LAX, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0xB7] = Instruction{.cycles = 4, .execute = &CPU::LAX, .fetch = &CPU::ZeroPageY};
|
||||||
|
instructions[0xAF] = Instruction{.cycles = 4, .execute = &CPU::LAX, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0xBF] = Instruction{.cycles = 4, .execute = &CPU::LAX, .fetch = &CPU::AbsoluteY};
|
||||||
|
instructions[0xA3] = Instruction{.cycles = 6, .execute = &CPU::LAX, .fetch = &CPU::IndirectX};
|
||||||
|
instructions[0xB3] = Instruction{.cycles = 5, .execute = &CPU::LAX, .fetch = &CPU::IndirectY};
|
||||||
|
|
||||||
|
instructions[0xC7] = Instruction{.cycles = 5, .execute = &CPU::DCP, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0xD7] = Instruction{.cycles = 6, .execute = &CPU::DCP, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0xCF] = Instruction{.cycles = 6, .execute = &CPU::DCP, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0xDF] = Instruction{.cycles = 7, .execute = &CPU::DCP, .fetch = &CPU::AbsoluteX};
|
||||||
|
instructions[0xDB] = Instruction{.cycles = 7, .execute = &CPU::DCP, .fetch = &CPU::AbsoluteY};
|
||||||
|
instructions[0xC3] = Instruction{.cycles = 8, .execute = &CPU::DCP, .fetch = &CPU::IndirectX};
|
||||||
|
instructions[0xD3] = Instruction{.cycles = 8, .execute = &CPU::DCP, .fetch = &CPU::IndirectY};
|
||||||
|
|
||||||
|
instructions[0xE7] = Instruction{.cycles = 5, .execute = &CPU::ISC, .fetch = &CPU::ZeroPage};
|
||||||
|
instructions[0xF7] = Instruction{.cycles = 6, .execute = &CPU::ISC, .fetch = &CPU::ZeroPageX};
|
||||||
|
instructions[0xEF] = Instruction{.cycles = 6, .execute = &CPU::ISC, .fetch = &CPU::Absolute};
|
||||||
|
instructions[0xFF] = Instruction{.cycles = 7, .execute = &CPU::ISC, .fetch = &CPU::AbsoluteX};
|
||||||
|
instructions[0xFB] = Instruction{.cycles = 7, .execute = &CPU::ISC, .fetch = &CPU::AbsoluteY};
|
||||||
|
instructions[0xE3] = Instruction{.cycles = 8, .execute = &CPU::ISC, .fetch = &CPU::IndirectX};
|
||||||
|
instructions[0xF3] = Instruction{.cycles = 8, .execute = &CPU::ISC, .fetch = &CPU::IndirectY};
|
||||||
|
|
||||||
|
instructions[0x0B] = Instruction{.cycles = 2, .execute = &CPU::ANC, .fetch = &CPU::Immediate};
|
||||||
|
instructions[0x2B] = Instruction{.cycles = 2, .execute = &CPU::ANC, .fetch = &CPU::Immediate};
|
||||||
|
instructions[0x4B] = Instruction{.cycles = 0, .execute = &CPU::ALR, .fetch = &CPU::Immediate};
|
||||||
|
instructions[0x6B] = Instruction{.cycles = 0, .execute = &CPU::ARR, .fetch = &CPU::Immediate};
|
||||||
|
instructions[0xCB] = Instruction{.cycles = 0, .execute = &CPU::AXS, .fetch = &CPU::Immediate};
|
||||||
|
|
||||||
|
// Unused by NES
|
||||||
|
instructions[0x9C] = Instruction{.cycles = 0, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x9E] = Instruction{.cycles = 0, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x8B] = Instruction{.cycles = 0, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x93] = Instruction{.cycles = 0, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x9B] = Instruction{.cycles = 0, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x9F] = Instruction{.cycles = 0, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0xBB] = Instruction{.cycles = 0, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x02] = Instruction{.cycles = 0, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x12] = Instruction{.cycles = 0, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x22] = Instruction{.cycles = 0, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x32] = Instruction{.cycles = 0, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x42] = Instruction{.cycles = 0, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x52] = Instruction{.cycles = 0, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x62] = Instruction{.cycles = 0, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x72] = Instruction{.cycles = 0, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0x92] = Instruction{.cycles = 0, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0xB2] = Instruction{.cycles = 0, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0xD2] = Instruction{.cycles = 0, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
instructions[0xF2] = Instruction{.cycles = 0, .execute = &CPU::NOP, .fetch = &CPU::Implicit};
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::StackPush(uint8_t val)
|
||||||
|
{
|
||||||
|
// Pointing to free space; get address before decrement
|
||||||
|
uint16_t address = STACK_START_ADDRESS | sp;
|
||||||
|
--sp;
|
||||||
|
|
||||||
|
WriteMemory(address, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CPU::StackPop()
|
||||||
|
{
|
||||||
|
// Pointing to free space; get address after increment
|
||||||
|
++sp;
|
||||||
|
uint16_t address = STACK_START_ADDRESS | sp;
|
||||||
|
|
||||||
|
return ReadMemory(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::WriteMemory(uint16_t address, uint8_t value)
|
||||||
|
{
|
||||||
|
// First five bits of address determines its mapping
|
||||||
|
switch ((address & 0xF800u) >> 11u)
|
||||||
|
{
|
||||||
|
// RAM and mirrors
|
||||||
|
case 0x00:
|
||||||
|
case 0x01:
|
||||||
|
case 0x02:
|
||||||
|
case 0x03:
|
||||||
|
{
|
||||||
|
uint16_t translatedAddress = address & 0x7FFu;
|
||||||
|
|
||||||
|
ram[translatedAddress] = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SRAM and mirrors
|
||||||
|
case 0x0C:
|
||||||
|
case 0x0D:
|
||||||
|
case 0x0E:
|
||||||
|
case 0x0F:
|
||||||
|
{
|
||||||
|
uint16_t translatedAddress = address & 0x1FFFu;
|
||||||
|
|
||||||
|
sram[translatedAddress] = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ROM (Low) and mirrors
|
||||||
|
case 0x10:
|
||||||
|
case 0x11:
|
||||||
|
case 0x12:
|
||||||
|
case 0x13:
|
||||||
|
case 0x14:
|
||||||
|
case 0x15:
|
||||||
|
case 0x16:
|
||||||
|
case 0x17:
|
||||||
|
{
|
||||||
|
uint16_t translatedAddress = address & 0x3FFFu;
|
||||||
|
|
||||||
|
romLow[translatedAddress] = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ROM (High) and mirrors
|
||||||
|
case 0x18:
|
||||||
|
case 0x19:
|
||||||
|
case 0x1A:
|
||||||
|
case 0x1B:
|
||||||
|
case 0x1C:
|
||||||
|
case 0x1D:
|
||||||
|
case 0x1E:
|
||||||
|
case 0x1F:
|
||||||
|
{
|
||||||
|
uint16_t translatedAddress = address & 0x3FFFu;
|
||||||
|
|
||||||
|
romHigh[translatedAddress] = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CPU::ReadMemory(uint16_t address)
|
||||||
|
{
|
||||||
|
uint8_t byte{};
|
||||||
|
|
||||||
|
switch ((address & 0xF800u) >> 11u)
|
||||||
|
{
|
||||||
|
// RAM and mirrors
|
||||||
|
case 0x00:
|
||||||
|
case 0x01:
|
||||||
|
case 0x02:
|
||||||
|
case 0x03:
|
||||||
|
{
|
||||||
|
uint16_t translatedAddress = address & 0x7FFu;
|
||||||
|
|
||||||
|
byte = ram[translatedAddress];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SRAM and mirrors
|
||||||
|
case 0x0C:
|
||||||
|
case 0x0D:
|
||||||
|
case 0x0E:
|
||||||
|
case 0x0F:
|
||||||
|
{
|
||||||
|
uint16_t translatedAddress = address & 0x1FFFu;
|
||||||
|
|
||||||
|
byte = sram[translatedAddress];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ROM (Low) and mirrors
|
||||||
|
case 0x10:
|
||||||
|
case 0x11:
|
||||||
|
case 0x12:
|
||||||
|
case 0x13:
|
||||||
|
case 0x14:
|
||||||
|
case 0x15:
|
||||||
|
case 0x16:
|
||||||
|
case 0x17:
|
||||||
|
{
|
||||||
|
uint16_t translatedAddress = address & 0x3FFFu;
|
||||||
|
|
||||||
|
byte = romLow[translatedAddress];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ROM (High) and mirrors
|
||||||
|
case 0x18:
|
||||||
|
case 0x19:
|
||||||
|
case 0x1A:
|
||||||
|
case 0x1B:
|
||||||
|
case 0x1C:
|
||||||
|
case 0x1D:
|
||||||
|
case 0x1E:
|
||||||
|
case 0x1F:
|
||||||
|
{
|
||||||
|
uint16_t translatedAddress = address & 0x3FFFu;
|
||||||
|
|
||||||
|
byte = romHigh[translatedAddress];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::LoadRom(const char* filename)
|
||||||
|
{
|
||||||
|
std::ifstream file(filename, std::ios::binary | std::ios::ate);
|
||||||
|
|
||||||
|
if (file.is_open())
|
||||||
|
{
|
||||||
|
std::streampos size = file.tellg();
|
||||||
|
char* buffer = new char[size];
|
||||||
|
file.seekg(0x10, std::ios::beg);
|
||||||
|
file.read(buffer, size);
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
for (long i = 0; i < 0x4000; ++i)
|
||||||
|
{
|
||||||
|
WriteMemory(0x8000 + i, buffer[i]);
|
||||||
|
WriteMemory(0xC000 + i, buffer[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::Cycle()
|
||||||
|
{
|
||||||
|
opcode = ReadMemory(pc);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
Log(opcode, pc);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pageBoundaryCrossed = false;
|
||||||
|
|
||||||
|
((*this).*(instructions[opcode].fetch))();
|
||||||
|
|
||||||
|
((*this).*(instructions[opcode].execute))();
|
||||||
|
|
||||||
|
cycles += instructions[opcode].cycles;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::Log(uint8_t instruction, uint16_t pc)
|
||||||
|
{
|
||||||
|
printf("%04X ", pc);
|
||||||
|
|
||||||
|
printf("A:%02X X:%02X Y:%02X P:%02X SP:%02X PPU:000,000 CYC:%lu\n", acc, x, y, status.GetByte(), sp, cycles);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CPU::TestBits(uint8_t value, uint8_t bits)
|
||||||
|
{
|
||||||
|
return (value & bits) == bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CPU::IsNegative(uint8_t value)
|
||||||
|
{
|
||||||
|
// Bit 7 is set
|
||||||
|
return TestBits(value, 1u << 7u);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CPU::IsPageBoundaryCrossed(uint16_t before, uint16_t after)
|
||||||
|
{
|
||||||
|
// Any bits in upper byte indicates a page boundary crossing (pages are 0xFF)
|
||||||
|
return (before & 0xFF00u) != (after & 0xFF00u);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t CPU::ComposeAddress(uint8_t msb, uint8_t lsb)
|
||||||
|
{
|
||||||
|
return (msb << 8u) | lsb;
|
||||||
|
}
|
|
@ -0,0 +1,243 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
|
||||||
|
const unsigned int INSTRUCTION_COUNT = 256;
|
||||||
|
const unsigned int RAM_SIZE = 2048;
|
||||||
|
const unsigned int SRAM_SIZE = 8192;
|
||||||
|
const unsigned int ROM_SIZE = 16384;
|
||||||
|
const unsigned int IRQ_VECTOR_MSB = 0xFFFF;
|
||||||
|
const unsigned int IRQ_VECTOR_LSB = 0xFFFE;
|
||||||
|
|
||||||
|
|
||||||
|
class Status
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void SetByte(uint8_t byte)
|
||||||
|
{
|
||||||
|
negative = (byte & 0x80u) >> 7u;
|
||||||
|
overflow = (byte & 0x40u) >> 6u;
|
||||||
|
b1 = (byte & 0x20u) >> 5u;
|
||||||
|
b0 = (byte & 0x10u) >> 4u;
|
||||||
|
decimal = (byte & 0x08u) >> 3u;
|
||||||
|
interruptDisable = (byte & 0x04u) >> 2u;
|
||||||
|
zero = (byte & 0x02u) >> 1u;
|
||||||
|
carry = (byte & 0x01u);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t GetByte()
|
||||||
|
{
|
||||||
|
return negative << 7u | overflow << 6u | b1 << 5u | b0 << 4u
|
||||||
|
| decimal << 3u | interruptDisable << 2u | zero << 1u | carry;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t carry;
|
||||||
|
uint8_t zero;
|
||||||
|
uint8_t interruptDisable;
|
||||||
|
uint8_t decimal;
|
||||||
|
uint8_t b0;
|
||||||
|
uint8_t b1;
|
||||||
|
uint8_t overflow;
|
||||||
|
uint8_t negative;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class CPU;
|
||||||
|
class Instruction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
uint8_t cycles;
|
||||||
|
|
||||||
|
using CpuFunc = void (CPU::*)();
|
||||||
|
CpuFunc fetch;
|
||||||
|
CpuFunc execute;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class CPU
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CPU();
|
||||||
|
void LoadRom(const char* filename);
|
||||||
|
void Cycle();
|
||||||
|
void Log(uint8_t instruction, uint16_t pc);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void StackPush(uint8_t val);
|
||||||
|
uint8_t StackPop();
|
||||||
|
void WriteMemory(uint16_t address, uint8_t value);
|
||||||
|
uint8_t ReadMemory(uint16_t address);
|
||||||
|
|
||||||
|
|
||||||
|
// --------------- HELPERS --------------- //
|
||||||
|
static bool TestBits(uint8_t value, uint8_t bits);
|
||||||
|
static bool IsNegative(uint8_t value);
|
||||||
|
static bool IsPageBoundaryCrossed(uint16_t before, uint16_t after);
|
||||||
|
static uint16_t ComposeAddress(uint8_t msb, uint8_t lsb);
|
||||||
|
|
||||||
|
|
||||||
|
// --------------- ADDRESS MODES --------------- //
|
||||||
|
// Operand is implied by the instruction
|
||||||
|
void Implicit();
|
||||||
|
|
||||||
|
// Operand is the byte/STA
|
||||||
|
void Immediate();
|
||||||
|
|
||||||
|
// Operand contains a zero-page address (0x00 to 0xFF)
|
||||||
|
// Byte is retrieved from that address in memory
|
||||||
|
void ZeroPage();
|
||||||
|
|
||||||
|
// Operand is added to X to get effective address, with wrap around back to 0x00
|
||||||
|
void ZeroPageX();
|
||||||
|
|
||||||
|
// Operand is added to X to get effective address, with wrap around back to 0x00
|
||||||
|
void ZeroPageY();
|
||||||
|
|
||||||
|
// Operand is the memory location
|
||||||
|
void Absolute();
|
||||||
|
|
||||||
|
// Operand contains an absolute address which can be added to X to get a new address
|
||||||
|
// If the address after adding X crosses a page-boundary, an additional cycle occurs
|
||||||
|
void AbsoluteX();
|
||||||
|
|
||||||
|
// Operand contains an absolute address which can be added to Y to get a new address
|
||||||
|
// If the address after adding Y crosses a page-boundary, an additional cycle occurs
|
||||||
|
void AbsoluteY();
|
||||||
|
|
||||||
|
// Operand contains address of LSB of effective address - MSB is in address + 1
|
||||||
|
void Indirect();
|
||||||
|
|
||||||
|
// Operand contains a zero-page address (0x00 to 0xFF)
|
||||||
|
// Value in X is added to operand to retrieve two bytes of address in memory (addition will wrap around back to 0x00)
|
||||||
|
// Effective address is used to retrieve the byte
|
||||||
|
void IndirectX();
|
||||||
|
|
||||||
|
// Operand contains a zero-page address (0x00 to 0xFF)
|
||||||
|
// Two bytes of address are retrieved from that address in memory
|
||||||
|
// Y is added to that address and the new address is used to retrieve the byte
|
||||||
|
// If the addition of Y causes a page-boundary crossing, an additional cycle occurs
|
||||||
|
void IndirectY();
|
||||||
|
|
||||||
|
// Operand is an offset from current instruction used for branching
|
||||||
|
void Relative();
|
||||||
|
|
||||||
|
|
||||||
|
// --------------- LOGICAL --------------- //
|
||||||
|
void AND();
|
||||||
|
void EOR();
|
||||||
|
void ORA();
|
||||||
|
void BIT();
|
||||||
|
|
||||||
|
// --------------- ARITHMETIC --------------- //
|
||||||
|
void ADC();
|
||||||
|
void SBC();
|
||||||
|
void CMP();
|
||||||
|
void CPX();
|
||||||
|
void CPY();
|
||||||
|
|
||||||
|
// --------------- LOAD/STORE --------------- //
|
||||||
|
void STA();
|
||||||
|
void STX();
|
||||||
|
void STY();
|
||||||
|
void LDA();
|
||||||
|
void LDX();
|
||||||
|
void LDY();
|
||||||
|
|
||||||
|
// --------------- TRANSFERS --------------- //
|
||||||
|
void TXA();
|
||||||
|
void TYA();
|
||||||
|
void TXS();
|
||||||
|
void TAX();
|
||||||
|
void TAY();
|
||||||
|
void TSX();
|
||||||
|
|
||||||
|
// --------------- STACK --------------- //
|
||||||
|
void PHP();
|
||||||
|
void PLP();
|
||||||
|
void PHA();
|
||||||
|
void PLA();
|
||||||
|
|
||||||
|
// --------------- INCREMENTS & DECREMENTS --------------- //
|
||||||
|
void INC();
|
||||||
|
void INX();
|
||||||
|
void INY();
|
||||||
|
void DEC();
|
||||||
|
void DEX();
|
||||||
|
void DEY();
|
||||||
|
|
||||||
|
// --------------- SHIFTS --------------- //
|
||||||
|
void ASL();
|
||||||
|
void LSR();
|
||||||
|
void ROL();
|
||||||
|
void ROR();
|
||||||
|
|
||||||
|
// --------------- JUMPS & CALLS --------------- //
|
||||||
|
void JMP();
|
||||||
|
void JSR();
|
||||||
|
void RTS();
|
||||||
|
|
||||||
|
// --------------- STATUS FLAG CHANGES --------------- //
|
||||||
|
void CLC();
|
||||||
|
void SEC();
|
||||||
|
void CLI();
|
||||||
|
void SEI();
|
||||||
|
void CLV();
|
||||||
|
void CLD();
|
||||||
|
void SED();
|
||||||
|
|
||||||
|
// --------------- BRANCHES --------------- //
|
||||||
|
void BPL();
|
||||||
|
void BMI();
|
||||||
|
void BVC();
|
||||||
|
void BVS();
|
||||||
|
void BCC();
|
||||||
|
void BCS();
|
||||||
|
void BNE();
|
||||||
|
void BEQ();
|
||||||
|
|
||||||
|
// --------------- SYSTEM --------------- //
|
||||||
|
void BRK();
|
||||||
|
void NOP();
|
||||||
|
void RTI();
|
||||||
|
|
||||||
|
// --------------- UNOFFICIAL --------------- //
|
||||||
|
void LAX();
|
||||||
|
void SAX();
|
||||||
|
void DCP();
|
||||||
|
void ISC();
|
||||||
|
void SLO();
|
||||||
|
void RLA();
|
||||||
|
void SRE();
|
||||||
|
void RRA();
|
||||||
|
void ANC();
|
||||||
|
void ALR();
|
||||||
|
void ARR();
|
||||||
|
void AXS();
|
||||||
|
|
||||||
|
// Machine features
|
||||||
|
uint8_t ram[RAM_SIZE]{};
|
||||||
|
uint8_t sram[SRAM_SIZE]{};
|
||||||
|
uint8_t romLow[ROM_SIZE]{};
|
||||||
|
uint8_t romHigh[ROM_SIZE]{};
|
||||||
|
uint16_t pc = 0xC000;
|
||||||
|
uint8_t sp = 0xFD;
|
||||||
|
uint8_t acc{};
|
||||||
|
uint8_t x{};
|
||||||
|
uint8_t y{};
|
||||||
|
Status status{};
|
||||||
|
uint64_t cycles = 7;
|
||||||
|
bool irq{};
|
||||||
|
bool nmi{};
|
||||||
|
|
||||||
|
// Emulator variables
|
||||||
|
bool pageBoundaryCrossed{};
|
||||||
|
uint8_t opcode{};
|
||||||
|
uint16_t fetchedAddress{};
|
||||||
|
uint8_t fetchedByte{};
|
||||||
|
|
||||||
|
Instruction instructions[INSTRUCTION_COUNT]{};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,698 @@
|
||||||
|
#include "CPU/CPU.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
// --------------- ARITHMETIC --------------- //
|
||||||
|
void CPU::ADC()
|
||||||
|
{
|
||||||
|
uint16_t tempSum = acc + fetchedByte;
|
||||||
|
|
||||||
|
tempSum += status.carry;
|
||||||
|
|
||||||
|
status.carry = (tempSum & 0xFF00u) ? 1u : 0u;
|
||||||
|
|
||||||
|
bool accSign = IsNegative(acc);
|
||||||
|
bool valSign = IsNegative(fetchedByte);
|
||||||
|
bool sumSign = IsNegative(tempSum);
|
||||||
|
|
||||||
|
acc = tempSum;
|
||||||
|
|
||||||
|
// If sign of operands does not match sign of result -> overflow
|
||||||
|
status.overflow = ((accSign == valSign) && (accSign != sumSign)) ? 1u : 0u;
|
||||||
|
|
||||||
|
status.zero = acc ? 0u : 1u;
|
||||||
|
status.negative = (IsNegative(acc)) ? 1u : 0u;
|
||||||
|
|
||||||
|
cycles += (pageBoundaryCrossed) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::SBC()
|
||||||
|
{
|
||||||
|
uint16_t tempDiff = acc - fetchedByte;
|
||||||
|
|
||||||
|
tempDiff -= (1u - status.carry);
|
||||||
|
|
||||||
|
status.carry = (tempDiff & 0xFF00) ? 0u : 1u;
|
||||||
|
|
||||||
|
bool accSign = IsNegative(acc);
|
||||||
|
bool valSign = IsNegative(fetchedByte);
|
||||||
|
bool sumSign = IsNegative(tempDiff);
|
||||||
|
|
||||||
|
// Set and truncate most-significant byte of temp sum
|
||||||
|
acc = tempDiff;
|
||||||
|
|
||||||
|
// If sign of operands does not match sign of result -> overflow
|
||||||
|
status.overflow = ((accSign != valSign) && (valSign == sumSign)) ? 1u : 0u;
|
||||||
|
|
||||||
|
status.zero = acc ? 0u : 1u;
|
||||||
|
status.negative = (IsNegative(acc)) ? 1u : 0u;
|
||||||
|
|
||||||
|
cycles += (pageBoundaryCrossed) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::CMP()
|
||||||
|
{
|
||||||
|
status.carry = (acc >= fetchedByte) ? 1u : 0u;
|
||||||
|
status.zero = (acc == fetchedByte) ? 1u : 0u;
|
||||||
|
status.negative = (IsNegative(acc - fetchedByte)) ? 1u : 0u;
|
||||||
|
|
||||||
|
cycles += (pageBoundaryCrossed) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::CPX()
|
||||||
|
{
|
||||||
|
status.carry = (x >= fetchedByte) ? 1u : 0u;
|
||||||
|
status.zero = (x == fetchedByte) ? 1u : 0u;
|
||||||
|
status.negative = (IsNegative(x - fetchedByte)) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::CPY()
|
||||||
|
{
|
||||||
|
status.carry = (y >= fetchedByte) ? 1u : 0u;
|
||||||
|
status.zero = (y == fetchedByte) ? 1u : 0u;
|
||||||
|
status.negative = (IsNegative(y - fetchedByte)) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --------------- BRANCHES --------------- //
|
||||||
|
void CPU::BPL()
|
||||||
|
{
|
||||||
|
if (!status.negative)
|
||||||
|
{
|
||||||
|
pc = fetchedAddress;
|
||||||
|
|
||||||
|
cycles += (pageBoundaryCrossed) ? 2u : 1u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::BMI()
|
||||||
|
{
|
||||||
|
if (status.negative)
|
||||||
|
{
|
||||||
|
pc = fetchedAddress;
|
||||||
|
|
||||||
|
cycles += (pageBoundaryCrossed) ? 2u : 1u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::BVC()
|
||||||
|
{
|
||||||
|
if (!status.overflow)
|
||||||
|
{
|
||||||
|
pc = fetchedAddress;
|
||||||
|
|
||||||
|
cycles += (pageBoundaryCrossed) ? 2u : 1u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::BVS()
|
||||||
|
{
|
||||||
|
if (status.overflow)
|
||||||
|
{
|
||||||
|
pc = fetchedAddress;
|
||||||
|
|
||||||
|
cycles += (pageBoundaryCrossed) ? 2u : 1u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::BCC()
|
||||||
|
{
|
||||||
|
if (!status.carry)
|
||||||
|
{
|
||||||
|
pc = fetchedAddress;
|
||||||
|
|
||||||
|
cycles += (pageBoundaryCrossed) ? 2u : 1u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::BCS()
|
||||||
|
{
|
||||||
|
if (status.carry)
|
||||||
|
{
|
||||||
|
pc = fetchedAddress;
|
||||||
|
|
||||||
|
cycles += (pageBoundaryCrossed) ? 2u : 1u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::BNE()
|
||||||
|
{
|
||||||
|
if (!status.zero)
|
||||||
|
{
|
||||||
|
pc = fetchedAddress;
|
||||||
|
|
||||||
|
cycles += (pageBoundaryCrossed) ? 2u : 1u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::BEQ()
|
||||||
|
{
|
||||||
|
if (status.zero)
|
||||||
|
{
|
||||||
|
pc = fetchedAddress;
|
||||||
|
|
||||||
|
cycles += (pageBoundaryCrossed) ? 2u : 1u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --------------- INCREMENT/DECREMENT --------------- //
|
||||||
|
void CPU::INC()
|
||||||
|
{
|
||||||
|
++fetchedByte;
|
||||||
|
|
||||||
|
WriteMemory(fetchedAddress, fetchedByte);
|
||||||
|
|
||||||
|
status.zero = fetchedByte ? 0u : 1u;
|
||||||
|
status.negative = (IsNegative(fetchedByte)) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::INX()
|
||||||
|
{
|
||||||
|
++x;
|
||||||
|
|
||||||
|
status.zero = x ? 0u : 1u;
|
||||||
|
status.negative = (IsNegative(x)) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::INY()
|
||||||
|
{
|
||||||
|
++y;
|
||||||
|
|
||||||
|
status.zero = y ? 0u : 1u;
|
||||||
|
status.negative = (IsNegative(y)) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::DEC()
|
||||||
|
{
|
||||||
|
--fetchedByte;
|
||||||
|
|
||||||
|
WriteMemory(fetchedAddress, fetchedByte);
|
||||||
|
|
||||||
|
status.zero = fetchedByte ? 0u : 1u;
|
||||||
|
status.negative = (IsNegative(fetchedByte)) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::DEX()
|
||||||
|
{
|
||||||
|
--x;
|
||||||
|
|
||||||
|
status.zero = x ? 0u : 1u;
|
||||||
|
status.negative = (IsNegative(x)) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::DEY()
|
||||||
|
{
|
||||||
|
--y;
|
||||||
|
|
||||||
|
status.zero = y ? 0u : 1u;
|
||||||
|
status.negative = (IsNegative(y)) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --------------- JUMPS & CALLS --------------- //
|
||||||
|
void CPU::JMP()
|
||||||
|
{
|
||||||
|
pc = fetchedAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::JSR()
|
||||||
|
{
|
||||||
|
uint8_t pcMsb = ((pc - 1u) & 0xFF00u) >> 8u;
|
||||||
|
uint8_t pcLsb = (pc - 1u) & 0xFFu;
|
||||||
|
|
||||||
|
StackPush(pcMsb);
|
||||||
|
StackPush(pcLsb);
|
||||||
|
|
||||||
|
pc = fetchedAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::RTS()
|
||||||
|
{
|
||||||
|
uint8_t pcLsb = StackPop();
|
||||||
|
uint8_t pcMsb = StackPop();
|
||||||
|
|
||||||
|
uint16_t address = ComposeAddress(pcMsb, pcLsb) + 1u;
|
||||||
|
|
||||||
|
pc = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --------------- LOAD/STORE --------------- //
|
||||||
|
void CPU::LDA()
|
||||||
|
{
|
||||||
|
cycles += (pageBoundaryCrossed) ? 1u : 0u;
|
||||||
|
|
||||||
|
acc = fetchedByte;
|
||||||
|
|
||||||
|
status.zero = acc ? 0u : 1u;
|
||||||
|
status.negative = (IsNegative(acc)) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::LDX()
|
||||||
|
{
|
||||||
|
cycles += (pageBoundaryCrossed) ? 1u : 0u;
|
||||||
|
|
||||||
|
x = fetchedByte;
|
||||||
|
|
||||||
|
status.zero = x ? 0u : 1u;
|
||||||
|
status.negative = (IsNegative(x)) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::LDY()
|
||||||
|
{
|
||||||
|
cycles += (pageBoundaryCrossed) ? 1u : 0u;
|
||||||
|
|
||||||
|
y = fetchedByte;
|
||||||
|
|
||||||
|
status.zero = y ? 0u : 1u;
|
||||||
|
status.negative = (IsNegative(y)) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::STA()
|
||||||
|
{
|
||||||
|
WriteMemory(fetchedAddress, acc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::STX()
|
||||||
|
{
|
||||||
|
WriteMemory(fetchedAddress, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::STY()
|
||||||
|
{
|
||||||
|
WriteMemory(fetchedAddress, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --------------- LOGICAL --------------- //
|
||||||
|
void CPU::AND()
|
||||||
|
{
|
||||||
|
acc &= fetchedByte;
|
||||||
|
|
||||||
|
cycles += (pageBoundaryCrossed) ? 1u : 0u;
|
||||||
|
status.zero = acc ? 0u : 1u;
|
||||||
|
status.negative = (IsNegative(acc)) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::EOR()
|
||||||
|
{
|
||||||
|
acc ^= fetchedByte;
|
||||||
|
|
||||||
|
cycles += (pageBoundaryCrossed) ? 1u : 0u;
|
||||||
|
status.zero = acc ? 0u : 1u;
|
||||||
|
status.negative = (IsNegative(acc)) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::ORA()
|
||||||
|
{
|
||||||
|
acc |= fetchedByte;
|
||||||
|
|
||||||
|
cycles += (pageBoundaryCrossed) ? 1u : 0u;
|
||||||
|
status.zero = acc ? 0u : 1u;
|
||||||
|
status.negative = (IsNegative(acc)) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::BIT()
|
||||||
|
{
|
||||||
|
status.zero = (acc & fetchedByte) ? 0u : 1u;
|
||||||
|
status.overflow = (TestBits(fetchedByte, (1u << 6u))) ? 1u : 0u;
|
||||||
|
status.negative = (IsNegative(fetchedByte)) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --------------- SHIFTS --------------- //
|
||||||
|
void CPU::ASL()
|
||||||
|
{
|
||||||
|
// Set carry to fetchedByte of MSB pre-shift
|
||||||
|
if (TestBits(fetchedByte, 0x80))
|
||||||
|
{
|
||||||
|
status.carry = 1u;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
status.carry = 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchedByte <<= 1u;
|
||||||
|
|
||||||
|
status.zero = fetchedByte ? 0u : 1u;
|
||||||
|
status.negative = (IsNegative(fetchedByte)) ? 1u : 0u;
|
||||||
|
|
||||||
|
if (instructions[opcode].fetch == &CPU::Implicit)
|
||||||
|
{
|
||||||
|
acc = fetchedByte;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteMemory(fetchedAddress, fetchedByte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::LSR()
|
||||||
|
{
|
||||||
|
// Set carry to fetchedByte of LSB pre-shift
|
||||||
|
if (TestBits(fetchedByte, 0x01))
|
||||||
|
{
|
||||||
|
status.carry = 1u;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
status.carry = 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchedByte >>= 1u;
|
||||||
|
|
||||||
|
status.zero = fetchedByte ? 0u : 1u;
|
||||||
|
status.negative = (IsNegative(fetchedByte)) ? 1u : 0u;
|
||||||
|
|
||||||
|
if (instructions[opcode].fetch == &CPU::Implicit)
|
||||||
|
{
|
||||||
|
acc = fetchedByte;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteMemory(fetchedAddress, fetchedByte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::ROL()
|
||||||
|
{
|
||||||
|
uint8_t oldCarry = status.carry;
|
||||||
|
|
||||||
|
// Set carry to fetchedByte of MSB pre-shift
|
||||||
|
if (TestBits(fetchedByte, 0x80))
|
||||||
|
{
|
||||||
|
status.carry = 1u;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
status.carry = 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchedByte <<= 1u;
|
||||||
|
|
||||||
|
// Put old carry at LSB
|
||||||
|
if (oldCarry)
|
||||||
|
{
|
||||||
|
fetchedByte |= 0x1u;
|
||||||
|
}
|
||||||
|
|
||||||
|
status.zero = fetchedByte ? 0u : 1u;
|
||||||
|
status.negative = (IsNegative(fetchedByte)) ? 1u : 0u;
|
||||||
|
|
||||||
|
if (instructions[opcode].fetch == &CPU::Implicit)
|
||||||
|
{
|
||||||
|
acc = fetchedByte;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteMemory(fetchedAddress, fetchedByte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::ROR()
|
||||||
|
{
|
||||||
|
uint8_t oldCarry = status.carry;
|
||||||
|
|
||||||
|
// Set carry to fetchedByte of LSB pre-shift
|
||||||
|
if (TestBits(fetchedByte, 0x01))
|
||||||
|
{
|
||||||
|
status.carry = 1u;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
status.carry = 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchedByte >>= 1u;
|
||||||
|
|
||||||
|
// Put old carry at MSB
|
||||||
|
if (oldCarry)
|
||||||
|
{
|
||||||
|
fetchedByte |= 0x80u;
|
||||||
|
}
|
||||||
|
|
||||||
|
status.zero = fetchedByte ? 0u : 1u;
|
||||||
|
status.negative = (IsNegative(fetchedByte)) ? 1u : 0u;
|
||||||
|
|
||||||
|
if (instructions[opcode].fetch == &CPU::Implicit)
|
||||||
|
{
|
||||||
|
acc = fetchedByte;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteMemory(fetchedAddress, fetchedByte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --------------- STACK --------------- //
|
||||||
|
void CPU::PHP()
|
||||||
|
{
|
||||||
|
Status statusCopy = status;
|
||||||
|
statusCopy.b1 = 1u;
|
||||||
|
statusCopy.b0 = 1u;
|
||||||
|
|
||||||
|
StackPush(statusCopy.GetByte());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::PLP()
|
||||||
|
{
|
||||||
|
uint8_t stackCopy = StackPop();
|
||||||
|
|
||||||
|
status.SetByte(stackCopy);
|
||||||
|
status.b1 = 1u;
|
||||||
|
status.b0 = 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::PHA()
|
||||||
|
{
|
||||||
|
StackPush(acc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::PLA()
|
||||||
|
{
|
||||||
|
acc = StackPop();
|
||||||
|
|
||||||
|
status.zero = acc ? 0u : 1u;
|
||||||
|
status.negative = (IsNegative(acc)) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --------------- STATUS FLAG CHANGES --------------- //
|
||||||
|
void CPU::CLC()
|
||||||
|
{
|
||||||
|
status.carry = 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::SEC()
|
||||||
|
{
|
||||||
|
status.carry = 1u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::CLI()
|
||||||
|
{
|
||||||
|
status.interruptDisable = 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::SEI()
|
||||||
|
{
|
||||||
|
status.interruptDisable = 1u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::CLV()
|
||||||
|
{
|
||||||
|
status.overflow = 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::CLD()
|
||||||
|
{
|
||||||
|
status.decimal = 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::SED()
|
||||||
|
{
|
||||||
|
status.decimal = 1u;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --------------- SYSTEM --------------- //
|
||||||
|
void CPU::BRK()
|
||||||
|
{
|
||||||
|
uint8_t pcMsb = (pc & 0xFF00u) >> 8u;
|
||||||
|
uint8_t pcLsb = pc & 0xFFu;
|
||||||
|
|
||||||
|
StackPush(pcMsb);
|
||||||
|
StackPush(pcLsb);
|
||||||
|
|
||||||
|
Status statusCopy = status;
|
||||||
|
statusCopy.b1 = 1u;
|
||||||
|
statusCopy.b0 = 0u;
|
||||||
|
|
||||||
|
StackPush(statusCopy.GetByte());
|
||||||
|
|
||||||
|
pcMsb = ReadMemory(IRQ_VECTOR_MSB);
|
||||||
|
pcLsb = ReadMemory(IRQ_VECTOR_LSB);
|
||||||
|
|
||||||
|
pc = ComposeAddress(pcMsb, pcLsb);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::NOP()
|
||||||
|
{
|
||||||
|
// Do nothing
|
||||||
|
cycles += (pageBoundaryCrossed) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::RTI()
|
||||||
|
{
|
||||||
|
uint8_t statusCopy = StackPop();
|
||||||
|
|
||||||
|
status.SetByte(statusCopy);
|
||||||
|
status.b1 = 1u;
|
||||||
|
|
||||||
|
uint8_t pcLsb = StackPop();
|
||||||
|
uint8_t pcMsb = StackPop();
|
||||||
|
|
||||||
|
pc = ComposeAddress(pcMsb, pcLsb);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --------------- TRANSFERS --------------- //
|
||||||
|
void CPU::TXA()
|
||||||
|
{
|
||||||
|
acc = x;
|
||||||
|
|
||||||
|
status.zero = acc ? 0u : 1u;
|
||||||
|
status.negative = (IsNegative(acc)) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::TYA()
|
||||||
|
{
|
||||||
|
acc = y;
|
||||||
|
|
||||||
|
status.zero = acc ? 0u : 1u;
|
||||||
|
status.negative = (IsNegative(acc)) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::TXS()
|
||||||
|
{
|
||||||
|
sp = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::TAY()
|
||||||
|
{
|
||||||
|
y = acc;
|
||||||
|
|
||||||
|
status.zero = y ? 0u : 1u;
|
||||||
|
status.negative = (IsNegative(y)) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::TAX()
|
||||||
|
{
|
||||||
|
x = acc;
|
||||||
|
|
||||||
|
status.zero = x ? 0u : 1u;
|
||||||
|
status.negative = (IsNegative(x)) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::TSX()
|
||||||
|
{
|
||||||
|
x = sp;
|
||||||
|
|
||||||
|
status.zero = x ? 0u : 1u;
|
||||||
|
status.negative = (IsNegative(x)) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --------------- UNOFFICIAL --------------- //
|
||||||
|
void CPU::LAX()
|
||||||
|
{
|
||||||
|
LDA();
|
||||||
|
TAX();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::SAX()
|
||||||
|
{
|
||||||
|
uint8_t byte = acc & x;
|
||||||
|
|
||||||
|
WriteMemory(fetchedAddress, byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::DCP()
|
||||||
|
{
|
||||||
|
DEC();
|
||||||
|
CMP();
|
||||||
|
|
||||||
|
cycles -= (pageBoundaryCrossed) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::ISC()
|
||||||
|
{
|
||||||
|
INC();
|
||||||
|
SBC();
|
||||||
|
|
||||||
|
cycles -= (pageBoundaryCrossed) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::SLO()
|
||||||
|
{
|
||||||
|
ASL();
|
||||||
|
ORA();
|
||||||
|
|
||||||
|
cycles -= (pageBoundaryCrossed) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::RLA()
|
||||||
|
{
|
||||||
|
ROL();
|
||||||
|
AND();
|
||||||
|
|
||||||
|
cycles -= (pageBoundaryCrossed) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::SRE()
|
||||||
|
{
|
||||||
|
LSR();
|
||||||
|
EOR();
|
||||||
|
|
||||||
|
cycles -= (pageBoundaryCrossed) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::RRA()
|
||||||
|
{
|
||||||
|
ROR();
|
||||||
|
ADC();
|
||||||
|
|
||||||
|
cycles -= (pageBoundaryCrossed) ? 1u : 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::ANC()
|
||||||
|
{
|
||||||
|
AND();
|
||||||
|
|
||||||
|
status.carry = (fetchedByte & 0x80u) >> 7u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::ALR()
|
||||||
|
{
|
||||||
|
AND();
|
||||||
|
LSR();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::ARR()
|
||||||
|
{
|
||||||
|
AND();
|
||||||
|
ROR();
|
||||||
|
|
||||||
|
status.carry = (fetchedByte & 0x40u) >> 6u;
|
||||||
|
status.overflow = ((fetchedByte & 0x40u) >> 6u) ^ ((fetchedByte & 0x20u) >> 5u);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::AXS()
|
||||||
|
{
|
||||||
|
x = (acc & x) - fetchedByte;
|
||||||
|
|
||||||
|
status.negative = (IsNegative(x)) ? 1u : 0u;
|
||||||
|
status.zero = x ? 0u : 1u;
|
||||||
|
status.carry = (x & 0xFF00u) ? 1u : 0u;
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include "CPU/CPU.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
if (argc != 2)
|
||||||
|
{
|
||||||
|
std::cerr << "Usage: " << argv[0] << "<ROM>\n";
|
||||||
|
std::exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
CPU cpu;
|
||||||
|
|
||||||
|
cpu.LoadRom("nestest.nes");
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
cpu.Cycle();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue