1312 lines
34 KiB
C++
1312 lines
34 KiB
C++
//
|
|
// Created by josh on 1/22/23.
|
|
//
|
|
|
|
#include <map>
|
|
#include "j6502.h"
|
|
#include "Bus.h"
|
|
|
|
using a = j6502;
|
|
|
|
j6502::j6502() {
|
|
|
|
lookup = {
|
|
{"BRK", &a::BRK, &a::IMM, 7},
|
|
{"ORA", &a::ORA, &a::IZX, 6},
|
|
{"???", &a::XXX, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 8},
|
|
{"???", &a::NOP, &a::IMP, 3},
|
|
{"ORA", &a::ORA, &a::ZP0, 3},
|
|
{"ASL", &a::ASL, &a::ZP0, 5},
|
|
{"???", &a::XXX, &a::IMP, 5},
|
|
{"PHP", &a::PHP, &a::IMP, 3},
|
|
{"ORA", &a::ORA, &a::IMM, 2},
|
|
{"ASL", &a::ASL, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 2},
|
|
{"???", &a::NOP, &a::IMP, 4},
|
|
{"ORA", &a::ORA, &a::ABS, 4},
|
|
{"ASL", &a::ASL, &a::ABS, 6},
|
|
{"???", &a::XXX, &a::IMP, 6},
|
|
{"BPL", &a::BPL, &a::REL, 2},
|
|
{"ORA", &a::ORA, &a::IZY, 5},
|
|
{"???", &a::XXX, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 8},
|
|
{"???", &a::NOP, &a::IMP, 4},
|
|
{"ORA", &a::ORA, &a::ZPX, 4},
|
|
{"ASL", &a::ASL, &a::ZPX, 6},
|
|
{"???", &a::XXX, &a::IMP, 6},
|
|
{"CLC", &a::CLC, &a::IMP, 2},
|
|
{"ORA", &a::ORA, &a::ABY, 4},
|
|
{"???", &a::NOP, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 7},
|
|
{"???", &a::NOP, &a::IMP, 4},
|
|
{"ORA", &a::ORA, &a::ABX, 4},
|
|
{"ASL", &a::ASL, &a::ABX, 7},
|
|
{"???", &a::XXX, &a::IMP, 7},
|
|
{"JSR", &a::JSR, &a::ABS, 6},
|
|
{"AND", &a::AND, &a::IZX, 6},
|
|
{"???", &a::XXX, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 8},
|
|
{"BIT", &a::BIT, &a::ZP0, 3},
|
|
{"AND", &a::AND, &a::ZP0, 3},
|
|
{"ROL", &a::ROL, &a::ZP0, 5},
|
|
{"???", &a::XXX, &a::IMP, 5},
|
|
{"PLP", &a::PLP, &a::IMP, 4},
|
|
{"AND", &a::AND, &a::IMM, 2},
|
|
{"ROL", &a::ROL, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 2},
|
|
{"BIT", &a::BIT, &a::ABS, 4},
|
|
{"AND", &a::AND, &a::ABS, 4},
|
|
{"ROL", &a::ROL, &a::ABS, 6},
|
|
{"???", &a::XXX, &a::IMP, 6},
|
|
{"BMI", &a::BMI, &a::REL, 2},
|
|
{"AND", &a::AND, &a::IZY, 5},
|
|
{"???", &a::XXX, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 8},
|
|
{"???", &a::NOP, &a::IMP, 4},
|
|
{"AND", &a::AND, &a::ZPX, 4},
|
|
{"ROL", &a::ROL, &a::ZPX, 6},
|
|
{"???", &a::XXX, &a::IMP, 6},
|
|
{"SEC", &a::SEC, &a::IMP, 2},
|
|
{"AND", &a::AND, &a::ABY, 4},
|
|
{"???", &a::NOP, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 7},
|
|
{"???", &a::NOP, &a::IMP, 4},
|
|
{"AND", &a::AND, &a::ABX, 4},
|
|
{"ROL", &a::ROL, &a::ABX, 7},
|
|
{"???", &a::XXX, &a::IMP, 7},
|
|
{"RTI", &a::RTI, &a::IMP, 6},
|
|
{"EOR", &a::EOR, &a::IZX, 6},
|
|
{"???", &a::XXX, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 8},
|
|
{"???", &a::NOP, &a::IMP, 3},
|
|
{"EOR", &a::EOR, &a::ZP0, 3},
|
|
{"LSR", &a::LSR, &a::ZP0, 5},
|
|
{"???", &a::XXX, &a::IMP, 5},
|
|
{"PHA", &a::PHA, &a::IMP, 3},
|
|
{"EOR", &a::EOR, &a::IMM, 2},
|
|
{"LSR", &a::LSR, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 2},
|
|
{"JMP", &a::JMP, &a::ABS, 3},
|
|
{"EOR", &a::EOR, &a::ABS, 4},
|
|
{"LSR", &a::LSR, &a::ABS, 6},
|
|
{"???", &a::XXX, &a::IMP, 6},
|
|
{"BVC", &a::BVC, &a::REL, 2},
|
|
{"EOR", &a::EOR, &a::IZY, 5},
|
|
{"???", &a::XXX, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 8},
|
|
{"???", &a::NOP, &a::IMP, 4},
|
|
{"EOR", &a::EOR, &a::ZPX, 4},
|
|
{"LSR", &a::LSR, &a::ZPX, 6},
|
|
{"???", &a::XXX, &a::IMP, 6},
|
|
{"CLI", &a::CLI, &a::IMP, 2},
|
|
{"EOR", &a::EOR, &a::ABY, 4},
|
|
{"???", &a::NOP, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 7},
|
|
{"???", &a::NOP, &a::IMP, 4},
|
|
{"EOR", &a::EOR, &a::ABX, 4},
|
|
{"LSR", &a::LSR, &a::ABX, 7},
|
|
{"???", &a::XXX, &a::IMP, 7},
|
|
{"RTS", &a::RTS, &a::IMP, 6},
|
|
{"ADC", &a::ADC, &a::IZX, 6},
|
|
{"???", &a::XXX, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 8},
|
|
{"???", &a::NOP, &a::IMP, 3},
|
|
{"ADC", &a::ADC, &a::ZP0, 3},
|
|
{"ROR", &a::ROR, &a::ZP0, 5},
|
|
{"???", &a::XXX, &a::IMP, 5},
|
|
{"PLA", &a::PLA, &a::IMP, 4},
|
|
{"ADC", &a::ADC, &a::IMM, 2},
|
|
{"ROR", &a::ROR, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 2},
|
|
{"JMP", &a::JMP, &a::IND, 5},
|
|
{"ADC", &a::ADC, &a::ABS, 4},
|
|
{"ROR", &a::ROR, &a::ABS, 6},
|
|
{"???", &a::XXX, &a::IMP, 6},
|
|
{"BVS", &a::BVS, &a::REL, 2},
|
|
{"ADC", &a::ADC, &a::IZY, 5},
|
|
{"???", &a::XXX, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 8},
|
|
{"???", &a::NOP, &a::IMP, 4},
|
|
{"ADC", &a::ADC, &a::ZPX, 4},
|
|
{"ROR", &a::ROR, &a::ZPX, 6},
|
|
{"???", &a::XXX, &a::IMP, 6},
|
|
{"SEI", &a::SEI, &a::IMP, 2},
|
|
{"ADC", &a::ADC, &a::ABY, 4},
|
|
{"???", &a::NOP, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 7},
|
|
{"???", &a::NOP, &a::IMP, 4},
|
|
{"ADC", &a::ADC, &a::ABX, 4},
|
|
{"ROR", &a::ROR, &a::ABX, 7},
|
|
{"???", &a::XXX, &a::IMP, 7},
|
|
{"???", &a::NOP, &a::IMP, 2},
|
|
{"STA", &a::STA, &a::IZX, 6},
|
|
{"???", &a::NOP, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 6},
|
|
{"STY", &a::STY, &a::ZP0, 3},
|
|
{"STA", &a::STA, &a::ZP0, 3},
|
|
{"STX", &a::STX, &a::ZP0, 3},
|
|
{"???", &a::XXX, &a::IMP, 3},
|
|
{"DEY", &a::DEY, &a::IMP, 2},
|
|
{"???", &a::NOP, &a::IMP, 2},
|
|
{"TXA", &a::TXA, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 2},
|
|
{"STY", &a::STY, &a::ABS, 4},
|
|
{"STA", &a::STA, &a::ABS, 4},
|
|
{"STX", &a::STX, &a::ABS, 4},
|
|
{"???", &a::XXX, &a::IMP, 4},
|
|
{"BCC", &a::BCC, &a::REL, 2},
|
|
{"STA", &a::STA, &a::IZY, 6},
|
|
{"???", &a::XXX, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 6},
|
|
{"STY", &a::STY, &a::ZPX, 4},
|
|
{"STA", &a::STA, &a::ZPX, 4},
|
|
{"STX", &a::STX, &a::ZPY, 4},
|
|
{"???", &a::XXX, &a::IMP, 4},
|
|
{"TYA", &a::TYA, &a::IMP, 2},
|
|
{"STA", &a::STA, &a::ABY, 5},
|
|
{"TXS", &a::TXS, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 5},
|
|
{"???", &a::NOP, &a::IMP, 5},
|
|
{"STA", &a::STA, &a::ABX, 5},
|
|
{"???", &a::XXX, &a::IMP, 5},
|
|
{"???", &a::XXX, &a::IMP, 5},
|
|
{"LDY", &a::LDY, &a::IMM, 2},
|
|
{"LDA", &a::LDA, &a::IZX, 6},
|
|
{"LDX", &a::LDX, &a::IMM, 2},
|
|
{"???", &a::XXX, &a::IMP, 6},
|
|
{"LDY", &a::LDY, &a::ZP0, 3},
|
|
{"LDA", &a::LDA, &a::ZP0, 3},
|
|
{"LDX", &a::LDX, &a::ZP0, 3},
|
|
{"???", &a::XXX, &a::IMP, 3},
|
|
{"TAY", &a::TAY, &a::IMP, 2},
|
|
{"LDA", &a::LDA, &a::IMM, 2},
|
|
{"TAX", &a::TAX, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 2},
|
|
{"LDY", &a::LDY, &a::ABS, 4},
|
|
{"LDA", &a::LDA, &a::ABS, 4},
|
|
{"LDX", &a::LDX, &a::ABS, 4},
|
|
{"???", &a::XXX, &a::IMP, 4},
|
|
{"BCS", &a::BCS, &a::REL, 2},
|
|
{"LDA", &a::LDA, &a::IZY, 5},
|
|
{"???", &a::XXX, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 5},
|
|
{"LDY", &a::LDY, &a::ZPX, 4},
|
|
{"LDA", &a::LDA, &a::ZPX, 4},
|
|
{"LDX", &a::LDX, &a::ZPY, 4},
|
|
{"???", &a::XXX, &a::IMP, 4},
|
|
{"CLV", &a::CLV, &a::IMP, 2},
|
|
{"LDA", &a::LDA, &a::ABY, 4},
|
|
{"TSX", &a::TSX, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 4},
|
|
{"LDY", &a::LDY, &a::ABX, 4},
|
|
{"LDA", &a::LDA, &a::ABX, 4},
|
|
{"LDX", &a::LDX, &a::ABY, 4},
|
|
{"???", &a::XXX, &a::IMP, 4},
|
|
{"CPY", &a::CPY, &a::IMM, 2},
|
|
{"CMP", &a::CMP, &a::IZX, 6},
|
|
{"???", &a::NOP, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 8},
|
|
{"CPY", &a::CPY, &a::ZP0, 3},
|
|
{"CMP", &a::CMP, &a::ZP0, 3},
|
|
{"DEC", &a::DEC, &a::ZP0, 5},
|
|
{"???", &a::XXX, &a::IMP, 5},
|
|
{"INY", &a::INY, &a::IMP, 2},
|
|
{"CMP", &a::CMP, &a::IMM, 2},
|
|
{"DEX", &a::DEX, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 2},
|
|
{"CPY", &a::CPY, &a::ABS, 4},
|
|
{"CMP", &a::CMP, &a::ABS, 4},
|
|
{"DEC", &a::DEC, &a::ABS, 6},
|
|
{"???", &a::XXX, &a::IMP, 6},
|
|
{"BNE", &a::BNE, &a::REL, 2},
|
|
{"CMP", &a::CMP, &a::IZY, 5},
|
|
{"???", &a::XXX, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 8},
|
|
{"???", &a::NOP, &a::IMP, 4},
|
|
{"CMP", &a::CMP, &a::ZPX, 4},
|
|
{"DEC", &a::DEC, &a::ZPX, 6},
|
|
{"???", &a::XXX, &a::IMP, 6},
|
|
{"CLD", &a::CLD, &a::IMP, 2},
|
|
{"CMP", &a::CMP, &a::ABY, 4},
|
|
{"NOP", &a::NOP, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 7},
|
|
{"???", &a::NOP, &a::IMP, 4},
|
|
{"CMP", &a::CMP, &a::ABX, 4},
|
|
{"DEC", &a::DEC, &a::ABX, 7},
|
|
{"???", &a::XXX, &a::IMP, 7},
|
|
{"CPX", &a::CPX, &a::IMM, 2},
|
|
{"SBC", &a::SBC, &a::IZX, 6},
|
|
{"???", &a::NOP, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 8},
|
|
{"CPX", &a::CPX, &a::ZP0, 3},
|
|
{"SBC", &a::SBC, &a::ZP0, 3},
|
|
{"INC", &a::INC, &a::ZP0, 5},
|
|
{"???", &a::XXX, &a::IMP, 5},
|
|
{"INX", &a::INX, &a::IMP, 2},
|
|
{"SBC", &a::SBC, &a::IMM, 2},
|
|
{"NOP", &a::NOP, &a::IMP, 2},
|
|
{"???", &a::SBC, &a::IMP, 2},
|
|
{"CPX", &a::CPX, &a::ABS, 4},
|
|
{"SBC", &a::SBC, &a::ABS, 4},
|
|
{"INC", &a::INC, &a::ABS, 6},
|
|
{"???", &a::XXX, &a::IMP, 6},
|
|
{"BEQ", &a::BEQ, &a::REL, 2},
|
|
{"SBC", &a::SBC, &a::IZY, 5},
|
|
{"???", &a::XXX, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 8},
|
|
{"???", &a::NOP, &a::IMP, 4},
|
|
{"SBC", &a::SBC, &a::ZPX, 4},
|
|
{"INC", &a::INC, &a::ZPX, 6},
|
|
{"???", &a::XXX, &a::IMP, 6},
|
|
{"SED", &a::SED, &a::IMP, 2},
|
|
{"SBC", &a::SBC, &a::ABY, 4},
|
|
{"NOP", &a::NOP, &a::IMP, 2},
|
|
{"???", &a::XXX, &a::IMP, 7},
|
|
{"???", &a::NOP, &a::IMP, 4},
|
|
{"SBC", &a::SBC, &a::ABX, 4},
|
|
{"INC", &a::INC, &a::ABX, 7},
|
|
{"???", &a::XXX, &a::IMP, 7},
|
|
};
|
|
}
|
|
|
|
j6502::~j6502() {
|
|
|
|
}
|
|
|
|
// Reads an 8bit byte from the bus, located at the specified 16-bit address
|
|
u8 j6502::read(u16 addr) { return bus->read(addr, false); }
|
|
|
|
void j6502::write(u16 addr, u8 d) { bus->write(addr, d); }
|
|
|
|
u8 j6502::GetFlag(FLAGS f)
|
|
{
|
|
return ((status & f) > 0) ? 1 : 0;
|
|
}
|
|
|
|
void j6502::SetFlag(FLAGS F, bool v) {
|
|
if (v)
|
|
status |= F;
|
|
else
|
|
status &= ~F;
|
|
}
|
|
|
|
u8 j6502::fetch() {
|
|
if (!(lookup[opcode].addrmode == &j6502::IMP))
|
|
fetched = read(addr_abs);
|
|
return fetched;
|
|
}
|
|
|
|
#pragma region Addressing Modes
|
|
|
|
// Address Mode: Implied
|
|
// There is no addtional data required for this instruction.
|
|
// This instruction does something very simple like set a status bit
|
|
u8 j6502::IMP() {
|
|
fetched = a;
|
|
return 0;
|
|
}
|
|
|
|
// Address Mode: Immediate
|
|
// The instruction expects the next byte to be used as a value
|
|
// So prep the read address to point to next byte
|
|
u8 j6502::IMM() {
|
|
addr_abs = pc++;
|
|
return 0;
|
|
}
|
|
|
|
// Address Mode: Zero Page
|
|
// To save program bytes, zero page addressing allows you to absolutely address
|
|
// a location in first 0xFF bytes of address range. Clearly this only requires one
|
|
// byte instead of the usual two.
|
|
u8 j6502::ZP0() {
|
|
addr_abs = read(pc);
|
|
pc++;
|
|
addr_abs &= 0x00FF;
|
|
return 0;
|
|
}
|
|
|
|
// Address Mode: Zero Page X offset
|
|
// Contents of X register is added to the supplied single bit address.
|
|
// Useful for iterating through ranges within the first page;
|
|
u8 j6502::ZPX() {
|
|
addr_abs = (read(pc) + x);
|
|
pc++;
|
|
addr_abs &= 0x00FF;
|
|
return 0;
|
|
}
|
|
|
|
// Address Mode: Zero Page Y offset
|
|
// Ditto, but uses Y register for offset
|
|
u8 j6502::ZPY() {
|
|
addr_abs = (read(pc) + y);
|
|
pc++;
|
|
addr_abs &= 0x00FF;
|
|
return 0;
|
|
}
|
|
|
|
// Address Mode: Relative
|
|
// Exclusive to branch instructions. Address must reside within -128 to +127
|
|
// of the branch instruction, i.e. you can't directly branch to any address
|
|
// in the addressable range
|
|
u8 j6502::REL() {
|
|
addr_rel = read(pc);
|
|
pc++;
|
|
if (addr_rel & 0x80)
|
|
addr_rel |= 0xFF00;
|
|
return 0;
|
|
}
|
|
|
|
// Address Mode: Absolute
|
|
// A full 16-bit address is loaded and used
|
|
u8 j6502::ABS() {
|
|
u16 lo = read(pc);
|
|
pc++;
|
|
u16 hi = read(pc);
|
|
pc++;
|
|
addr_abs = (hi << 8) | lo;
|
|
return 0;
|
|
}
|
|
|
|
// Address Mode: Absolute with X offset
|
|
// Contents of X register is added to the supplied two byte address
|
|
// if the resulting address changes the page, an additional clock cycle is required
|
|
u8 j6502::ABX() {
|
|
u16 lo = read(pc);
|
|
pc++;
|
|
u16 hi = read(pc);
|
|
pc++;
|
|
|
|
addr_abs = (hi << 8) | lo;
|
|
addr_abs += x;
|
|
|
|
if ((addr_abs & 0xFF00) != (hi << 8))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
// Address Mode: Absolute with Y offset
|
|
// Contents of Y register is added to the supplied two byte address
|
|
// if the resulting address changes the page, an additional clock cycle is required
|
|
u8 j6502::ABY() {
|
|
u16 lo = read(pc);
|
|
pc++;
|
|
u16 hi = read(pc);
|
|
pc++;
|
|
addr_abs = (hi << 8) | lo;
|
|
addr_abs += y;
|
|
|
|
if ((addr_abs & 0xFF00) != (hi << 8))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
// REEEEEEE: Pointers!!!
|
|
|
|
// Address Mode: Indirect
|
|
// The supplied 16-bit address is read to get the actual 16-bit address.
|
|
// This instruction is unusual in that it has a hardware bug.
|
|
// to emulate it "accurately", we also emulate the bug
|
|
// If the low byte of the given address is 0xFF, then to read the high byte
|
|
// of the actual address we need to cross a page boundary
|
|
// This doesn't actually work on the chip as designed, instead it wraps back around
|
|
// in the same page, yielding an invalid address
|
|
u8 j6502::IND() {
|
|
u16 ptr_lo = read(pc);
|
|
pc++;
|
|
u16 ptr_hi = read(pc);
|
|
pc++;
|
|
|
|
u16 ptr = (ptr_hi << 8) | ptr_lo;
|
|
|
|
if (ptr_lo == 0x00FF) // Simulate Page Boundary Hardware Bug
|
|
addr_abs = (read(ptr & 0xFF00) << 8) | read(ptr + 0);
|
|
else
|
|
addr_abs = (read(ptr + 1) << 8) | read(ptr + 0);
|
|
}
|
|
|
|
// Address Mode: Indirect X
|
|
// The supplied 8-bit address is offset by X Register to index
|
|
// a location in page 0x00. The actual 16-bit address is read
|
|
// from this location
|
|
u8 j6502::IZX() {
|
|
u16 t = read(pc);
|
|
pc++;
|
|
|
|
u16 lo = read((u16) (t + (u16) x) & 0x00FF);
|
|
u16 hi = read((u16) (t + (u16) x + 1) & 0x00FF);
|
|
|
|
addr_abs = (hi << 8) | lo;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Address Mode: Indirect Y
|
|
// The supplied 8-bit address indexes a location in page 0x00. From
|
|
// here the actual 16-bit address is read, and the contents of
|
|
// Y Register is added to it to offset it. If the offset causes a
|
|
// change in page then an additional clock cycle is required.
|
|
u8 j6502::IZY() {
|
|
u16 t = read(pc);
|
|
pc++;
|
|
|
|
u16 lo = read(t & 0x00FF);
|
|
u16 hi = read((t + 1) & 0x00FF);
|
|
|
|
addr_abs = (hi << 8) | lo;
|
|
addr_abs += y;
|
|
|
|
if ((addr_abs & 0xFF00) != (hi << 8))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Opcodes
|
|
|
|
// Instruction: Clear Carry Flag
|
|
// Function: C = 0
|
|
u8 j6502::CLC() {
|
|
SetFlag(C, false);
|
|
return 0;
|
|
}
|
|
|
|
// Instruction: Clear Decimal Flag
|
|
// Function: D = 0
|
|
u8 j6502::CLD() {
|
|
SetFlag(D, false);
|
|
return 0;
|
|
}
|
|
|
|
// Instruction: Disable Interrupts / Clear Interrupt Flag
|
|
// Function: I = 0
|
|
u8 j6502::CLI() {
|
|
SetFlag(I, false);
|
|
return 0;
|
|
}
|
|
|
|
// Instruction: Clear Overflow Flag
|
|
// Function: V = 0
|
|
u8 j6502::CLV() {
|
|
SetFlag(V, false);
|
|
return 0;
|
|
}
|
|
|
|
// Instruction: Compare Accumulator
|
|
// Function: C <- A >= M Z <- (A - M) == 0
|
|
// Flags Out: N, C, Z
|
|
u8 j6502::CMP() {
|
|
fetch();
|
|
temp = (u16) a - (u16) fetched;
|
|
SetFlag(C, a >= fetched);
|
|
SetFlag(Z, (temp & 0x00FF) == 0x0000);
|
|
SetFlag(N, temp & 0x0080);
|
|
return 1;
|
|
}
|
|
|
|
// Instruction: Compare X Register
|
|
// Function: C <- X >= M Z <- (X - M) == 0
|
|
// Flags Out: N, C, Z
|
|
u8 j6502::CPX() {
|
|
fetch();
|
|
temp = (u16) x - (u16) fetched;
|
|
SetFlag(C, x >= fetched);
|
|
SetFlag(Z, (temp & 0x00FF) == 0x0000);
|
|
SetFlag(N, temp & 0x0080);
|
|
return 0;
|
|
}
|
|
|
|
// Instruction: Compare Y Register
|
|
// Function: C <- Y >= M Z <- (Y - M) == 0
|
|
// Flags Out: M, C, Z
|
|
u8 j6502::CPY() {
|
|
fetch();
|
|
temp = (u16) y - (u16) fetched;
|
|
SetFlag(C, y >= fetched);
|
|
SetFlag(Z, (temp & 0x00FF) == 0x0000);
|
|
SetFlag(N, temp & 0x0080);
|
|
return 0;
|
|
|
|
}
|
|
// Instruction: Decrement Value at Memory Location
|
|
u8 j6502::DEC()
|
|
{
|
|
fetch();
|
|
temp = fetched - 1;
|
|
write(addr_abs, temp & 0x00FF);
|
|
SetFlag(Z, (temp & 0x00FF) == 0x0000);
|
|
SetFlag(N, temp & 0x0080);
|
|
return 0;
|
|
}
|
|
// Instruction: Decrement X Register
|
|
// Function: X = X - 1
|
|
// Flags Out: N, Z
|
|
u8 j6502::DEX()
|
|
{
|
|
x--;
|
|
SetFlag(Z, x == 0x00);
|
|
SetFlag(N, x & 0x80);
|
|
return 0;
|
|
}
|
|
// Instruction: Decrement Y Register
|
|
// Function Y = Y - 1
|
|
// Flags: N, Z
|
|
u8 j6502::DEY()
|
|
{
|
|
y--;
|
|
SetFlag(Z, y = 0x00);
|
|
SetFlag(N, y & 0x80);
|
|
return 0;
|
|
}
|
|
// Instruction: Bitwise Logic XOR
|
|
u8 j6502::EOR()
|
|
{
|
|
fetch();
|
|
a = a ^ fetched;
|
|
SetFlag(Z, a == 0x00);
|
|
SetFlag(N, a & 0x80);
|
|
}
|
|
// Instruction: Increment Value at Memory Locatio
|
|
// Function: M = M + 1
|
|
// Flags out: N, Z
|
|
u8 j6502::INC()
|
|
{
|
|
fetch();
|
|
temp = fetched + 1;
|
|
write(addr_abs, temp & 0x00FF);
|
|
SetFlag(Z, (temp & 0x00FF) == 0x00FF);
|
|
SetFlag(N, temp & 0x0080);
|
|
return 0;
|
|
}
|
|
// Instruction: Increment X Register
|
|
// Function: X = X + 1
|
|
// Flags Out: N, Z
|
|
u8 j6502::INX()
|
|
{
|
|
x++;
|
|
SetFlag(Z, x == 0x00);
|
|
SetFlag(N, x & 0x80);
|
|
return 0;
|
|
}
|
|
// Instruction: Increment Y Register
|
|
// Function: Y = Y + 1
|
|
// Flags: N, Z
|
|
u8 j6502::INY()
|
|
{
|
|
fetch();
|
|
temp = fetched + 1;
|
|
write(addr_abs, temp & 0x00FF);
|
|
SetFlag(Z, (temp & 0x00FF) == 0x0000);
|
|
SetFlag(N, temp & 0x0080);
|
|
return 0;
|
|
}
|
|
// Instruction: Jump To Location
|
|
// Function: pc = address
|
|
u8 j6502::JMP()
|
|
{
|
|
pc = addr_abs;
|
|
return 0;
|
|
}
|
|
// Instruction: Jump To Sub-Routine
|
|
// Function: Push current pc to stack, pc = address
|
|
u8 j6502::JSR()
|
|
{
|
|
pc--;
|
|
write(0x0100 + sp, (pc >> 8) & 0x00FF);
|
|
sp--;
|
|
write(0x0100 + sp, pc & 0x00FF);
|
|
sp--;
|
|
|
|
pc = addr_abs;
|
|
return 0;
|
|
}
|
|
// Instruction: Load The Accumulator
|
|
// Function: A = M
|
|
// Flags Out: N, Z
|
|
u8 j6502::LDA()
|
|
{
|
|
fetch();
|
|
a = fetched;
|
|
SetFlag(Z, a = 0x00);
|
|
SetFlag(N, a & 0x80);
|
|
return 1;
|
|
}
|
|
// Instruction: Load the X Register
|
|
// Function: X = M
|
|
// Flags Out: N, Z
|
|
u8 j6502::LDX()
|
|
{
|
|
fetch();
|
|
x = fetched;
|
|
SetFlag(Z, x == 0x00);
|
|
SetFlag(N, x & 0x80);
|
|
return 1;
|
|
}
|
|
// Instruction: Load the Y Register
|
|
u8 j6502::LDY()
|
|
{
|
|
fetch();
|
|
y = fetched;
|
|
SetFlag(Z, y == 0x00);
|
|
SetFlag(N, y & 0x80);
|
|
}
|
|
|
|
u8 j6502::LSR()
|
|
{
|
|
fetch();
|
|
SetFlag(C, fetched & 0x0001);
|
|
temp = fetched >> 1;
|
|
SetFlag(Z, (temp & 0x00FF) == 0x0000);
|
|
SetFlag(N, temp & 0x0080);
|
|
if (lookup[opcode].addrmode == &j6502::IMP)
|
|
a = temp & 0x00FF;
|
|
else
|
|
write(addr_abs, temp & 0x00FF);
|
|
return 0;
|
|
}
|
|
|
|
u8 j6502::NOP() {
|
|
switch (opcode) {
|
|
case 0x1C:
|
|
case 0x3C:
|
|
case 0x5C:
|
|
case 0x7C:
|
|
case 0xDC:
|
|
case 0xFC:
|
|
return 1;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Instruction: Bitwise Logic OR
|
|
// Function: A = A | M
|
|
// Flags out : N, Z
|
|
u8 j6502::ORA() {
|
|
fetch();
|
|
a = a | fetched;
|
|
SetFlag(Z, a == 0x00);
|
|
SetFlag(N, a & 0x80);
|
|
return 1;
|
|
}
|
|
|
|
// Instruction: Disable Interrupts
|
|
// Instruction: Bitwise Logic AND
|
|
// Function: A = A & M
|
|
// Flags Out: N, Z
|
|
u8 j6502::AND() {
|
|
fetch();
|
|
a = a & fetched;
|
|
SetFlag(Z, a == 0x00);
|
|
SetFlag(N, a & 0x80);
|
|
}
|
|
|
|
// Instruction: Add with Carry In
|
|
// Function: A = A + M + C
|
|
// Flags Out: C, V, N, Z
|
|
//
|
|
// Info: The purpose of this function is to add a value to the accumulator and a carry bit
|
|
// If the result is > 255 there is an overflow, thus setting carry bit.
|
|
// This allows you to chain together ADC instructions to add numbers larger than 8-bits.
|
|
// This in itself is simple, however the 6502 supports the concepts of Negativity/Positivity and Signed Overflow
|
|
u8 j6502::ADC() {
|
|
fetch();
|
|
temp = (u16) a + (u16)fetched + (u16)GetFlag(C);
|
|
SetFlag(C, temp > 255);
|
|
SetFlag(Z, (temp & 0x00FF) == 0);
|
|
SetFlag(V, (~((u16)a ^ (u16)fetched) & ((u16)a ^ (u16)temp)) & 0x0080);
|
|
SetFlag(N, temp & 0x80);
|
|
a = temp & 0x00FF;
|
|
return 1;
|
|
}
|
|
|
|
// Instruction: Subtraction with Borrow In
|
|
// Function: A = A - M - (1-C)
|
|
// Flags out: C, V, N, Z
|
|
u8 j6502::SBC() {
|
|
fetch();
|
|
u16 value = ((u16) fetched) ^ 0x00FF;
|
|
|
|
temp = (u16) a + (u16) value + (u16) GetFlag(C);
|
|
SetFlag(C, temp > 255);
|
|
SetFlag(Z, (temp & 0x00FF) == 0);
|
|
SetFlag(N, temp & 0x80);
|
|
SetFlag(V, (~((u16) a ^ (u16) fetched) & ((u16) a ^ (u16) temp)) & 0x0080);
|
|
a = temp & 0x00FF;
|
|
return 1;
|
|
}
|
|
|
|
// Instruction: Arithmetic Shift Left
|
|
// Function: A = C <- (A << 1) <- 0
|
|
// Flags Out: N, Z, C
|
|
u8 j6502::ASL() {
|
|
fetch();
|
|
u16 temp = (u16) fetched << 1;
|
|
SetFlag(C, (temp & 0xFF00) > 0);
|
|
SetFlag(Z, (temp & 0x00FF) == 0x00);
|
|
SetFlag(N, temp & 0x80);
|
|
|
|
if (lookup[opcode].addrmode = &j6502::IMP)
|
|
a = temp & 0x00FF;
|
|
else
|
|
write(addr_abs, temp & 0x00FF);
|
|
return 0;
|
|
}
|
|
|
|
// Instruction: Branch if Carry Clear
|
|
// Function: if (C==0) pc = address;
|
|
u8 j6502::BCC() {
|
|
if (GetFlag(C) == 0) {
|
|
cycles++;
|
|
addr_abs = pc + addr_rel;
|
|
|
|
if ((addr_abs & 0xFF00) != (pc & 0xFF00))
|
|
cycles++;
|
|
pc = addr_abs;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Instruction: Branch if Carry Set
|
|
// Function: if (C==1) pc = address;
|
|
u8 j6502::BCS() {
|
|
if (GetFlag(C) == 1) {
|
|
cycles++;
|
|
addr_abs = pc + addr_rel;
|
|
|
|
if ((addr_abs & 0xFF00) != (pc & 0xFF00))
|
|
cycles++;
|
|
|
|
pc = addr_abs;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Instruction: Branch if Equal
|
|
// Function: if(Z == 1) pc = address;
|
|
u8 j6502::BEQ() {
|
|
if (GetFlag(Z) == 1) {
|
|
cycles++;
|
|
addr_abs = pc + addr_rel;
|
|
|
|
if ((addr_abs & 0xFF00) != (pc & 0xFF00))
|
|
cycles++;
|
|
|
|
pc = addr_abs;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
u8 j6502::BIT() {
|
|
fetch();
|
|
temp = a & fetched;
|
|
SetFlag(Z, (temp & 0x00FF) == 0x00);
|
|
|
|
}
|
|
|
|
// Instruction: Branch if Negative
|
|
// Function: if (N==1) pc = address
|
|
u8 j6502::BMI() {
|
|
if (GetFlag(N) == 1) {
|
|
cycles++;
|
|
addr_abs = pc + addr_rel;
|
|
|
|
if ((addr_abs & 0xFF00) != (pc & 0xFF00))
|
|
cycles++;
|
|
|
|
pc = addr_abs;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Instruction: Branch if Not Equal
|
|
// Function: if (Z == 0) pc = address;
|
|
u8 j6502::BNE() {
|
|
if (GetFlag(Z) == 0) {
|
|
cycles++;
|
|
addr_abs = pc + addr_rel;
|
|
if ((addr_abs & 0xFF00) != (pc & 0xFF00))
|
|
cycles++;
|
|
pc = addr_abs;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Instruction: Branch if Positive
|
|
// Function: if(N == 0) pc = address;
|
|
u8 j6502::BPL() {
|
|
if (GetFlag(N) == 0) {
|
|
cycles++;
|
|
addr_abs = pc + addr_rel;
|
|
if ((addr_abs & 0xFF00) != (pc & 0xFF00))
|
|
cycles++;
|
|
|
|
pc = addr_abs;
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Instruction: Break
|
|
// Function: Program Sourced Interrupt
|
|
u8 j6502::BRK() {
|
|
pc++;
|
|
SetFlag(I, 1);
|
|
write(0x0100 + sp, (pc >> 8) & 0x00FF);
|
|
sp--;
|
|
write(0x0100 + sp, pc & 0x00FF);
|
|
sp--;
|
|
|
|
SetFlag(B, 1);
|
|
write(0x0100 + sp, status);
|
|
sp--;
|
|
SetFlag(B, 0);
|
|
|
|
pc = (u16) read(0xFFFE) | ((u16) read(0xFFFF) << 8);
|
|
return 0;
|
|
}
|
|
|
|
// Instruction: Branch if overflow clear
|
|
// Function: if(V == 0) pc = address
|
|
u8 j6502::BVC() {
|
|
if (GetFlag(V) == 0) {
|
|
cycles++;
|
|
addr_abs = pc + addr_rel;
|
|
if ((addr_abs & 0xFF00) != (pc & 0xFF00))
|
|
cycles++;
|
|
|
|
pc = addr_abs;
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Instruction: Branch if overflow set
|
|
// Function: if(V == 1) pc = address
|
|
u8 j6502::BVS() {
|
|
if (GetFlag(V) == 1) {
|
|
cycles++;
|
|
addr_abs = pc + addr_rel;
|
|
if ((addr_abs & 0xFF00) != (pc & 0xFF00)) {
|
|
cycles++;
|
|
}
|
|
pc = addr_abs;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Instruction: Push accumulator to stack
|
|
// Function: A - > Stack
|
|
u8 j6502::PHA() {
|
|
write(0x0100 + sp, a);
|
|
sp--;
|
|
return 0;
|
|
}
|
|
// Instruction: Push Status Register to Stack
|
|
// Function: status -> stack
|
|
// Note: Break flag is set to 1 before push
|
|
u8 j6502::PHP() {
|
|
|
|
}
|
|
// Instruction: Pop accumulator off Stack
|
|
// Function: A <- stack
|
|
// Flags out: N, Z
|
|
u8 j6502::PLA() {
|
|
sp++;
|
|
a = read(0x100 + sp);
|
|
SetFlag(Z, a == 0x00);
|
|
SetFlag(N, a & 0x80);
|
|
return 0;
|
|
}
|
|
// Instruction: Pop Status Register off Stack
|
|
// Function: Status <- stack
|
|
u8 j6502::PLP()
|
|
{
|
|
sp++;
|
|
status = read(0x0100+sp);
|
|
SetFlag(U, 1);
|
|
return 0;
|
|
}
|
|
|
|
u8 j6502::ROL()
|
|
{
|
|
fetch();
|
|
temp = (u16)(fetched << 1) | GetFlag(C);
|
|
SetFlag(C, temp & 0xFF00);
|
|
SetFlag(Z, (temp & 0x00FF) == 0x0000);
|
|
SetFlag(N, temp & 0x0080);
|
|
if (lookup[opcode].addrmode == &j6502::IMP)
|
|
a = temp & 0x00FF;
|
|
else
|
|
write(addr_abs, temp & 0x00FF);
|
|
return 0;
|
|
}
|
|
|
|
u8 j6502::ROR()
|
|
{
|
|
fetch();
|
|
temp = (u16)(GetFlag(C) << 7) | (fetched >> 1);
|
|
SetFlag(C, fetched & 0x01);
|
|
SetFlag(Z, (temp & 0x00FF) == 0x00);
|
|
SetFlag(N, temp & 0x0080);
|
|
if (lookup[opcode].addrmode == &j6502::IMP)
|
|
a = temp & 0x00FF;
|
|
else
|
|
write(addr_abs, temp & 0x00FF);
|
|
return 0;
|
|
}
|
|
|
|
u8 j6502::RTI() {
|
|
sp++;
|
|
status = read(0x0100 + sp);
|
|
status &= ~B;
|
|
status &= ~U;
|
|
|
|
sp++;
|
|
pc = (u16) read(0x0100 + sp);
|
|
sp++;
|
|
pc |= (u16) read(0x0100 + sp) << 8;
|
|
return 0;
|
|
}
|
|
|
|
u8 j6502::RTS()
|
|
{
|
|
sp++;
|
|
pc = (u16)read(0x0100 + sp);
|
|
sp++;
|
|
pc |= (u16)read(0x0100 + sp) << 8;
|
|
|
|
pc++;
|
|
return 0;
|
|
}
|
|
// Instruction: Set Carry Flag
|
|
// Function: C = 1
|
|
u8 j6502::SEC() {
|
|
SetFlag(C, true);
|
|
return 0;
|
|
}
|
|
// Instruction: Set Decimal Flag
|
|
u8 j6502::SED()
|
|
{
|
|
SetFlag(D, true);
|
|
return 0;
|
|
}
|
|
// Instruction: Set Interrupt Flag / Enable Interrupts
|
|
u8 j6502::SEI()
|
|
{
|
|
SetFlag(I, true);
|
|
return 0;
|
|
}
|
|
// Instruction: Store Accumulator at Address
|
|
u8 j6502::STA()
|
|
{
|
|
write(addr_abs, a);
|
|
return 0;
|
|
}
|
|
// Instruction: Store X Register at Address
|
|
// Function: M = X
|
|
u8 j6502::STX()
|
|
{
|
|
write(addr_abs, x);
|
|
return 0;
|
|
}
|
|
// Instruction: Store Y Register at Address
|
|
// Function: M = Y
|
|
u8 j6502::STY()
|
|
{
|
|
write(addr_abs, y);
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Instruction: Transfer Accumulator to X Register
|
|
// Function: X = A
|
|
// Flags Out: N, Z
|
|
u8 j6502::TAX()
|
|
{
|
|
x = a;
|
|
SetFlag(Z, x == 0x00);
|
|
SetFlag(N, x & 0x80);
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Instruction: Transfer Accumulator to Y Register
|
|
// Function: Y = A
|
|
// Flags Out: N, Z
|
|
u8 j6502::TAY()
|
|
{
|
|
y = a;
|
|
SetFlag(Z, y == 0x00);
|
|
SetFlag(N, y & 0x80);
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Instruction: Transfer Stack Pointer to X Register
|
|
// Function: X = stack pointer
|
|
// Flags Out: N, Z
|
|
u8 j6502::TSX()
|
|
{
|
|
x = sp;
|
|
SetFlag(Z, x == 0x00);
|
|
SetFlag(N, x & 0x80);
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Instruction: Transfer X Register to Accumulator
|
|
// Function: A = X
|
|
// Flags Out: N, Z
|
|
u8 j6502::TXA()
|
|
{
|
|
a = x;
|
|
SetFlag(Z, a == 0x00);
|
|
SetFlag(N, a & 0x80);
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Instruction: Transfer X Register to Stack Pointer
|
|
// Function: stack pointer = X
|
|
u8 j6502::TXS()
|
|
{
|
|
sp = x;
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Instruction: Transfer Y Register to Accumulator
|
|
// Function: A = Y
|
|
// Flags Out: N, Z
|
|
u8 j6502::TYA()
|
|
{
|
|
a = y;
|
|
SetFlag(Z, a == 0x00);
|
|
SetFlag(N, a & 0x80);
|
|
return 0;
|
|
}
|
|
|
|
u8 j6502::XXX() {
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
|
|
// Perform one clock cycle of emulation
|
|
void j6502::clock() {
|
|
if (cycles == 0) {
|
|
opcode = read(pc);
|
|
SetFlag(U, true);
|
|
pc++;
|
|
cycles = lookup[opcode].cycles;
|
|
u8 additional_cycle1 = (this->*lookup[opcode].addrmode)();
|
|
u8 additional_cycle2 = (this->*lookup[opcode].operate)();
|
|
|
|
cycles += (additional_cycle1 & additional_cycle2);
|
|
}
|
|
cycles--;
|
|
}
|
|
|
|
#pragma region Interrupts
|
|
|
|
// Reset Interrupt
|
|
void j6502::reset() {
|
|
a = 0;
|
|
x = 0;
|
|
y = 0;
|
|
sp = 0xFD;
|
|
status = 0x00 | U;
|
|
|
|
addr_abs = 0xFFFC; // Set
|
|
u16 lo = read(addr_abs + 0);
|
|
u16 hi = read(addr_abs + 1);
|
|
|
|
pc = (hi << 8) | lo;
|
|
|
|
addr_rel = 0x0000;
|
|
addr_abs = 0x0000;
|
|
fetched = 0x00;
|
|
|
|
cycles = 8;
|
|
}
|
|
|
|
// Interrupt Request Handler
|
|
void j6502::irq() {
|
|
if (GetFlag(I) == 0) {
|
|
write(0x0100 + sp, (pc >> 8) & 0x00FF);
|
|
sp--;
|
|
write(0x0100 + sp, (pc & 0x00FF));
|
|
sp--;
|
|
SetFlag(B, 0);
|
|
SetFlag(U, 1);
|
|
SetFlag(I, 1);
|
|
write(0x100 + sp, status);
|
|
sp--;
|
|
|
|
addr_abs = 0xFFFE;
|
|
u16 lo = read(addr_abs + 0);
|
|
u16 hi = read(addr_abs + 1);
|
|
pc = (hi << 8) | lo;
|
|
|
|
cycles = 7;
|
|
}
|
|
}
|
|
|
|
// NonMaskable IRQ
|
|
void j6502::nmi() {
|
|
write(0x0100 + sp, (pc >> 8) & 0x00FF);
|
|
sp--;
|
|
write(0x0100 + sp, (pc & 0x00FF));
|
|
sp--;
|
|
SetFlag(B, 0);
|
|
SetFlag(U, 1);
|
|
SetFlag(I, 1);
|
|
write(0x100 + sp, status);
|
|
sp--;
|
|
|
|
addr_abs = 0xFFFA;
|
|
u16 lo = read(addr_abs + 0);
|
|
u16 hi = read(addr_abs + 1);
|
|
pc = (hi << 8) | lo;
|
|
|
|
cycles = 8;
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
|
|
// Return from interrupt
|
|
|
|
|
|
std::map<u16, std::string> j6502::disassemble(u16 start, u16 stop) {
|
|
uint32_t addr = start;
|
|
u8 value = 0x00, lo = 0x00, hi = 0x00;
|
|
std::map<u16, std::string> mapLines;
|
|
u16 line_addr = 0;
|
|
|
|
// A convenient utility to convert variables into
|
|
// hex strings because "modern C++"'s method
|
|
// with streams is atrocious
|
|
auto hex = [](uint32_t n, u8 d)
|
|
{
|
|
std::string s(d, '0');
|
|
for (int i = d - 1; i >= 0; i--, n >>= 4)
|
|
s[i] = "0123456789ABCDEF"[n & 0xF];
|
|
return s;
|
|
};
|
|
// Starting at the specified address we read an instruction
|
|
// byte, which in turn yields information from the lookup table
|
|
// as to how many additional bytes we need to read and what the
|
|
// addressing mode is. I need this info to assemble human readable
|
|
// syntax, which is different depending upon the addressing mode
|
|
|
|
// As the instruction is decoded, a std::string is assembled
|
|
// with the readable output
|
|
while (addr <= (uint32_t)stop)
|
|
{
|
|
line_addr = addr;
|
|
|
|
// Prefix line with instruction address
|
|
std::string sInst = "$" + hex(addr, 4) + ": ";
|
|
|
|
// Read instruction, and get its readable name
|
|
u8 opcode = bus->read(addr, true); addr++;
|
|
sInst += lookup[opcode].name + " ";
|
|
|
|
// Get oprands from desired locations, and form the
|
|
// instruction based upon its addressing mode. These
|
|
// routines mimmick the actual fetch routine of the
|
|
// 6502 in order to get accurate data as part of the
|
|
// instruction
|
|
if (lookup[opcode].addrmode == &j6502::IMP)
|
|
{
|
|
sInst += " {IMP}";
|
|
}
|
|
else if (lookup[opcode].addrmode == &j6502::IMM)
|
|
{
|
|
value = bus->read(addr, true); addr++;
|
|
sInst += "#$" + hex(value, 2) + " {IMM}";
|
|
}
|
|
else if (lookup[opcode].addrmode == &j6502::ZP0)
|
|
{
|
|
lo = bus->read(addr, true); addr++;
|
|
hi = 0x00;
|
|
sInst += "$" + hex(lo, 2) + " {ZP0}";
|
|
}
|
|
else if (lookup[opcode].addrmode == &j6502::ZPX)
|
|
{
|
|
lo = bus->read(addr, true); addr++;
|
|
hi = 0x00;
|
|
sInst += "$" + hex(lo, 2) + ", X {ZPX}";
|
|
}
|
|
else if (lookup[opcode].addrmode == &j6502::ZPY)
|
|
{
|
|
lo = bus->read(addr, true); addr++;
|
|
hi = 0x00;
|
|
sInst += "$" + hex(lo, 2) + ", Y {ZPY}";
|
|
}
|
|
else if (lookup[opcode].addrmode == &j6502::IZX)
|
|
{
|
|
lo = bus->read(addr, true); addr++;
|
|
hi = 0x00;
|
|
sInst += "($" + hex(lo, 2) + ", X) {IZX}";
|
|
}
|
|
else if (lookup[opcode].addrmode == &j6502::IZY)
|
|
{
|
|
lo = bus->read(addr, true); addr++;
|
|
hi = 0x00;
|
|
sInst += "($" + hex(lo, 2) + "), Y {IZY}";
|
|
}
|
|
else if (lookup[opcode].addrmode == &j6502::ABS)
|
|
{
|
|
lo = bus->read(addr, true); addr++;
|
|
hi = bus->read(addr, true); addr++;
|
|
sInst += "$" + hex((u16)(hi << 8) | lo, 4) + " {ABS}";
|
|
}
|
|
else if (lookup[opcode].addrmode == &j6502::ABX)
|
|
{
|
|
lo = bus->read(addr, true); addr++;
|
|
hi = bus->read(addr, true); addr++;
|
|
sInst += "$" + hex((u16)(hi << 8) | lo, 4) + ", X {ABX}";
|
|
}
|
|
else if (lookup[opcode].addrmode == &j6502::ABY)
|
|
{
|
|
lo = bus->read(addr, true); addr++;
|
|
hi = bus->read(addr, true); addr++;
|
|
sInst += "$" + hex((u16)(hi << 8) | lo, 4) + ", Y {ABY}";
|
|
}
|
|
else if (lookup[opcode].addrmode == &j6502::IND)
|
|
{
|
|
lo = bus->read(addr, true); addr++;
|
|
hi = bus->read(addr, true); addr++;
|
|
sInst += "($" + hex((u16)(hi << 8) | lo, 4) + ") {IND}";
|
|
}
|
|
else if (lookup[opcode].addrmode == &j6502::REL)
|
|
{
|
|
value = bus->read(addr, true); addr++;
|
|
sInst += "$" + hex(value, 2) + " [$" + hex(addr + value, 4) + "] {REL}";
|
|
}
|
|
|
|
// Add the formed string to a std::map, using the instruction's
|
|
// address as the key. This makes it convenient to look for later
|
|
// as the instructions are variable in length, so a straight up
|
|
// incremental index is not sufficient.
|
|
mapLines[line_addr] = sInst;
|
|
}
|
|
|
|
return mapLines;
|
|
}
|
|
|
|
bool j6502::complete() {
|
|
return cycles == 0;
|
|
}
|
|
|
|
|
|
|