1945 lines
45 KiB
C++
1945 lines
45 KiB
C++
#include "cpu.h"
|
|
|
|
#include <cstdint>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <vector>
|
|
|
|
namespace yaze {
|
|
namespace app {
|
|
namespace emu {
|
|
|
|
void CPU::Update(UpdateMode mode, int stepCount) {
|
|
int cycles = (mode == UpdateMode::Run) ? clock.GetCycleCount() : stepCount;
|
|
|
|
// Execute the calculated number of cycles
|
|
for (int i = 0; i < cycles; i++) {
|
|
if (IsBreakpoint(PC)) {
|
|
break;
|
|
}
|
|
|
|
// Fetch and execute an instruction
|
|
ExecuteInstruction(ReadByte((PB << 16) + PC));
|
|
|
|
// Handle any interrupts, if necessary
|
|
HandleInterrupts();
|
|
|
|
if (mode == UpdateMode::Step) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPU::ExecuteInstruction(uint8_t opcode) {
|
|
uint8_t cycles = 0;
|
|
uint8_t instruction_length = 0;
|
|
uint32_t operand = 0;
|
|
bool immediate = false;
|
|
bool accumulator_mode = GetAccumulatorSize();
|
|
|
|
switch (opcode) {
|
|
case 0x61: // ADC DP Indexed Indirect, X
|
|
{
|
|
cycles = 6;
|
|
if (!m()) cycles++;
|
|
instruction_length = 2;
|
|
operand = ReadByte(DirectPageIndexedIndirectX());
|
|
ADC(operand);
|
|
break;
|
|
}
|
|
case 0x63: // ADC Stack Relative
|
|
{
|
|
cycles = 4;
|
|
if (!m()) cycles++;
|
|
instruction_length = 2;
|
|
operand = ReadByte(StackRelative());
|
|
ADC(operand);
|
|
break;
|
|
}
|
|
case 0x65: // ADC Direct Page
|
|
{
|
|
cycles = 3;
|
|
if (!m()) cycles++;
|
|
instruction_length = 2;
|
|
operand = ReadByte(DirectPage());
|
|
ADC(operand);
|
|
break;
|
|
}
|
|
case 0x67: // ADC DP Indirect Long
|
|
{
|
|
cycles = 6;
|
|
if (!m()) cycles++;
|
|
instruction_length = 2;
|
|
operand = ReadWord(DirectPageIndirectLong());
|
|
ADC(operand);
|
|
break;
|
|
}
|
|
case 0x69: // ADC Immediate
|
|
{
|
|
cycles = 2;
|
|
if (!m()) cycles++;
|
|
if (GetAccumulatorSize()) {
|
|
instruction_length = 2;
|
|
} else {
|
|
instruction_length = 3;
|
|
}
|
|
operand = Immediate();
|
|
immediate = true;
|
|
ADC(operand);
|
|
break;
|
|
}
|
|
case 0x6D: // ADC Absolute
|
|
{
|
|
cycles = 4;
|
|
if (!m()) cycles++;
|
|
instruction_length = 3;
|
|
operand = ReadWord(Absolute());
|
|
ADC(operand);
|
|
break;
|
|
}
|
|
case 0x6F: // ADC Absolute Long
|
|
{
|
|
cycles = 5;
|
|
if (!m()) cycles++;
|
|
instruction_length = 4;
|
|
operand = ReadWord(AbsoluteLong());
|
|
ADC(operand);
|
|
break;
|
|
}
|
|
case 0x71: // ADC DP Indirect Indexed, Y
|
|
{
|
|
cycles = 5;
|
|
if (!m()) cycles++;
|
|
instruction_length = 2;
|
|
operand = ReadByteOrWord(DirectPageIndirectIndexedY());
|
|
ADC(operand);
|
|
break;
|
|
}
|
|
case 0x72: // ADC DP Indirect
|
|
{
|
|
cycles = 5;
|
|
if (!m()) cycles++;
|
|
instruction_length = 2;
|
|
operand = ReadByte(DirectPageIndirect());
|
|
ADC(operand);
|
|
break;
|
|
}
|
|
case 0x73: // ADC SR Indirect Indexed, Y
|
|
{
|
|
cycles = 7;
|
|
if (!m()) cycles++;
|
|
instruction_length = 2;
|
|
operand = ReadByte(StackRelativeIndirectIndexedY());
|
|
ADC(operand);
|
|
break;
|
|
}
|
|
case 0x75: // ADC DP Indexed, X
|
|
{
|
|
cycles = 4;
|
|
if (!m()) cycles++;
|
|
instruction_length = 2;
|
|
operand = ReadByteOrWord(DirectPageIndexedX());
|
|
ADC(operand);
|
|
break;
|
|
}
|
|
case 0x77: // ADC DP Indirect Long Indexed, Y
|
|
{
|
|
cycles = 6;
|
|
if (!m()) cycles++;
|
|
instruction_length = 2;
|
|
operand = ReadByteOrWord(DirectPageIndirectLongIndexedY());
|
|
ADC(operand);
|
|
break;
|
|
}
|
|
case 0x79: // ADC Absolute Indexed, Y
|
|
{
|
|
cycles = 4;
|
|
if (!m()) cycles++;
|
|
instruction_length = 3;
|
|
operand = ReadWord(AbsoluteIndexedY());
|
|
ADC(operand);
|
|
break;
|
|
}
|
|
case 0x7D: // ADC Absolute Indexed, X
|
|
{
|
|
cycles = 4;
|
|
if (!m()) cycles++;
|
|
instruction_length = 3;
|
|
operand = ReadWord(AbsoluteIndexedX());
|
|
ADC(operand);
|
|
break;
|
|
}
|
|
case 0x7F: // ADC Absolute Long Indexed, X
|
|
{
|
|
cycles = 5;
|
|
if (!m()) cycles++;
|
|
instruction_length = 4;
|
|
operand = ReadByteOrWord(AbsoluteLongIndexedX());
|
|
ADC(operand);
|
|
break;
|
|
}
|
|
|
|
case 0x21: // AND DP Indexed Indirect, X
|
|
{
|
|
operand = ReadByteOrWord(DirectPageIndexedIndirectX());
|
|
AND(operand, true); // Not immediate, but value has been retrieved
|
|
break;
|
|
}
|
|
case 0x23: // AND Stack Relative
|
|
{
|
|
operand = StackRelative();
|
|
AND(operand);
|
|
break;
|
|
}
|
|
case 0x25: // AND Direct Page
|
|
{
|
|
operand = DirectPage();
|
|
AND(operand);
|
|
break;
|
|
}
|
|
case 0x27: // AND DP Indirect Long
|
|
{
|
|
operand = DirectPageIndirectLong();
|
|
AND(operand);
|
|
break;
|
|
}
|
|
case 0x29: // AND Immediate
|
|
{
|
|
operand = Immediate();
|
|
immediate = true;
|
|
AND(operand, true);
|
|
break;
|
|
}
|
|
case 0x2D: // AND Absolute
|
|
{
|
|
operand = Absolute();
|
|
AND(operand);
|
|
break;
|
|
}
|
|
case 0x2F: // AND Absolute Long
|
|
{
|
|
operand = AbsoluteLong();
|
|
ANDAbsoluteLong(operand);
|
|
break;
|
|
}
|
|
case 0x31: // AND DP Indirect Indexed, Y
|
|
{
|
|
operand = DirectPageIndirectIndexedY();
|
|
AND(operand);
|
|
break;
|
|
}
|
|
case 0x32: // AND DP Indirect
|
|
{
|
|
operand = DirectPageIndirect();
|
|
AND(operand);
|
|
break;
|
|
}
|
|
case 0x33: // AND SR Indirect Indexed, Y
|
|
{
|
|
operand = StackRelativeIndirectIndexedY();
|
|
AND(operand);
|
|
break;
|
|
}
|
|
case 0x35: // AND DP Indexed, X
|
|
{
|
|
operand = DirectPageIndexedX();
|
|
AND(operand);
|
|
break;
|
|
}
|
|
case 0x37: // AND DP Indirect Long Indexed, Y
|
|
{
|
|
operand = DirectPageIndirectLongIndexedY();
|
|
AND(operand);
|
|
break;
|
|
}
|
|
case 0x39: // AND Absolute Indexed, Y
|
|
{
|
|
operand = AbsoluteIndexedY();
|
|
AND(operand);
|
|
break;
|
|
}
|
|
case 0x3D: // AND Absolute Indexed, X
|
|
{
|
|
operand = AbsoluteIndexedX();
|
|
AND(operand);
|
|
break;
|
|
}
|
|
case 0x3F: // AND Absolute Long Indexed, X
|
|
{
|
|
operand = AbsoluteLongIndexedX();
|
|
AND(operand);
|
|
break;
|
|
}
|
|
|
|
case 0x06: // ASL Direct Page
|
|
{
|
|
operand = DirectPage();
|
|
ASL(operand);
|
|
break;
|
|
}
|
|
case 0x0A: // ASL Accumulator
|
|
{
|
|
A <<= 1;
|
|
A &= 0xFE;
|
|
SetCarryFlag(A & 0x80);
|
|
SetNegativeFlag(A);
|
|
SetZeroFlag(!A);
|
|
break;
|
|
}
|
|
case 0x0E: // ASL Absolute
|
|
{
|
|
operand = Absolute();
|
|
ASL(operand);
|
|
break;
|
|
}
|
|
case 0x16: // ASL DP Indexed, X
|
|
{
|
|
operand = ReadByteOrWord((0x00 << 16) + DirectPageIndexedX());
|
|
ASL(operand);
|
|
break;
|
|
}
|
|
case 0x1E: // ASL Absolute Indexed, X
|
|
{
|
|
operand = AbsoluteIndexedX();
|
|
ASL(operand);
|
|
break;
|
|
}
|
|
|
|
case 0x90: // BCC Branch if carry clear
|
|
{
|
|
operand = FetchByte();
|
|
BCC(operand);
|
|
break;
|
|
}
|
|
|
|
case 0xB0: // BCS Branch if carry set
|
|
{
|
|
operand = FetchByte();
|
|
BCS(operand);
|
|
break;
|
|
}
|
|
|
|
case 0xF0: // BEQ Branch if equal (zero set)
|
|
{
|
|
operand = FetchByte();
|
|
BEQ(operand);
|
|
break;
|
|
}
|
|
|
|
case 0x24: // BIT Direct Page
|
|
{
|
|
operand = DirectPage();
|
|
BIT(operand);
|
|
break;
|
|
}
|
|
case 0x2C: // BIT Absolute
|
|
{
|
|
operand = Absolute();
|
|
BIT(operand);
|
|
break;
|
|
}
|
|
case 0x34: // BIT DP Indexed, X
|
|
{
|
|
operand = DirectPageIndexedX();
|
|
BIT(operand);
|
|
break;
|
|
}
|
|
case 0x3C: // BIT Absolute Indexed, X
|
|
{
|
|
operand = AbsoluteIndexedX();
|
|
BIT(operand);
|
|
break;
|
|
}
|
|
case 0x89: // BIT Immediate
|
|
{
|
|
operand = Immediate();
|
|
BIT(operand);
|
|
immediate = true;
|
|
break;
|
|
}
|
|
|
|
case 0x30: // BMI Branch if minus (negative set)
|
|
{
|
|
operand = FetchByte();
|
|
BMI(operand);
|
|
break;
|
|
}
|
|
|
|
case 0xD0: // BNE Branch if not equal (zero clear)
|
|
{
|
|
operand = FetchSignedByte();
|
|
BNE(operand);
|
|
break;
|
|
}
|
|
|
|
case 0x10: // BPL Branch if plus (negative clear)
|
|
{
|
|
operand = FetchSignedByte();
|
|
BPL(operand);
|
|
break;
|
|
}
|
|
|
|
case 0x80: // BRA Branch always
|
|
{
|
|
operand = FetchByte();
|
|
BRA(operand);
|
|
break;
|
|
}
|
|
|
|
case 0x00: // BRK Break
|
|
{
|
|
BRK();
|
|
std::cout << "BRK" << std::endl;
|
|
// Print all the registers
|
|
std::cout << "A: " << std::hex << std::setw(2) << std::setfill('0')
|
|
<< (int)A << std::endl;
|
|
std::cout << "X: " << std::hex << std::setw(2) << std::setfill('0')
|
|
<< (int)X << std::endl;
|
|
std::cout << "Y: " << std::hex << std::setw(2) << std::setfill('0')
|
|
<< (int)Y << std::endl;
|
|
std::cout << "S: " << std::hex << std::setw(2) << std::setfill('0')
|
|
<< (int)SP() << std::endl;
|
|
std::cout << "PC: " << std::hex << std::setw(4) << std::setfill('0')
|
|
<< (int)PC << std::endl;
|
|
std::cout << "PB: " << std::hex << std::setw(2) << std::setfill('0')
|
|
<< (int)PB << std::endl;
|
|
std::cout << "D: " << std::hex << std::setw(4) << std::setfill('0')
|
|
<< (int)D << std::endl;
|
|
std::cout << "DB: " << std::hex << std::setw(2) << std::setfill('0')
|
|
<< (int)DB << std::endl;
|
|
std::cout << "E: " << std::hex << std::setw(2) << std::setfill('0')
|
|
<< (int)E << std::endl;
|
|
// status registers
|
|
std::cout << "C: " << std::hex << std::setw(2) << std::setfill('0')
|
|
<< (int)status << std::endl;
|
|
break;
|
|
}
|
|
|
|
case 0x82: // BRL Branch always long
|
|
{ // operand = FetchSignedWord();
|
|
operand = FetchWord();
|
|
BRL(operand);
|
|
break;
|
|
}
|
|
|
|
case 0x50: // BVC Branch if overflow clear
|
|
{
|
|
operand = FetchByte();
|
|
BVC(operand);
|
|
break;
|
|
}
|
|
|
|
case 0x70: // BVS Branch if overflow set
|
|
{
|
|
operand = FetchByte();
|
|
BVS(operand);
|
|
break;
|
|
}
|
|
|
|
case 0x18: // CLC Clear carry
|
|
{
|
|
CLC();
|
|
break;
|
|
}
|
|
|
|
case 0xD8: // CLD Clear decimal
|
|
{
|
|
CLD();
|
|
break;
|
|
}
|
|
|
|
case 0x58: // CLI Clear interrupt disable
|
|
{
|
|
CLI();
|
|
break;
|
|
}
|
|
|
|
case 0xB8: // CLV Clear overflow
|
|
{
|
|
CLV();
|
|
break;
|
|
}
|
|
|
|
case 0xC1: // CMP DP Indexed Indirect, X
|
|
{
|
|
operand = ReadByteOrWord(DirectPageIndexedIndirectX());
|
|
CMP(operand);
|
|
break;
|
|
}
|
|
case 0xC3: // CMP Stack Relative
|
|
{
|
|
operand = StackRelative();
|
|
CMP(operand);
|
|
break;
|
|
}
|
|
case 0xC5: // CMP Direct Page
|
|
{
|
|
operand = DirectPage();
|
|
CMP(operand);
|
|
break;
|
|
}
|
|
case 0xC7: // CMP DP Indirect Long
|
|
{
|
|
operand = DirectPageIndirectLong();
|
|
CMP(operand);
|
|
break;
|
|
}
|
|
case 0xC9: // CMP Immediate
|
|
{
|
|
operand = Immediate();
|
|
immediate = true;
|
|
CMP(operand, immediate);
|
|
break;
|
|
}
|
|
case 0xCD: // CMP Absolute
|
|
{
|
|
operand = Absolute(AccessType::Data);
|
|
CMP(operand);
|
|
break;
|
|
}
|
|
case 0xCF: // CMP Absolute Long
|
|
{
|
|
operand = AbsoluteLong();
|
|
CMP(operand);
|
|
break;
|
|
}
|
|
case 0xD1: // CMP DP Indirect Indexed, Y
|
|
{
|
|
operand = DirectPageIndirectIndexedY();
|
|
CMP(operand);
|
|
break;
|
|
}
|
|
case 0xD2: // CMP DP Indirect
|
|
{
|
|
operand = DirectPageIndirect();
|
|
CMP(operand);
|
|
break;
|
|
}
|
|
case 0xD3: // CMP SR Indirect Indexed, Y
|
|
{
|
|
operand = StackRelativeIndirectIndexedY();
|
|
CMP(operand);
|
|
break;
|
|
}
|
|
case 0xD5: // CMP DP Indexed, X
|
|
{
|
|
operand = DirectPageIndexedX();
|
|
CMP(operand);
|
|
break;
|
|
}
|
|
case 0xD7: // CMP DP Indirect Long Indexed, Y
|
|
{
|
|
operand = DirectPageIndirectLongIndexedY();
|
|
CMP(operand);
|
|
break;
|
|
}
|
|
case 0xD9: // CMP Absolute Indexed, Y
|
|
{
|
|
operand = AbsoluteIndexedY();
|
|
CMP(operand);
|
|
break;
|
|
}
|
|
case 0xDD: // CMP Absolute Indexed, X
|
|
{
|
|
operand = AbsoluteIndexedX();
|
|
CMP(operand);
|
|
break;
|
|
}
|
|
case 0xDF: // CMP Absolute Long Indexed, X
|
|
{
|
|
operand = AbsoluteLongIndexedX();
|
|
CMP(operand);
|
|
break;
|
|
}
|
|
|
|
case 0x02: // COP
|
|
{
|
|
COP();
|
|
break;
|
|
}
|
|
|
|
case 0xE0: // CPX Immediate
|
|
{
|
|
operand = Immediate(/*index_size=*/true);
|
|
immediate = true;
|
|
CPX(operand, immediate);
|
|
break;
|
|
}
|
|
case 0xE4: // CPX Direct Page
|
|
{
|
|
operand = DirectPage();
|
|
CPX(operand);
|
|
break;
|
|
}
|
|
case 0xEC: // CPX Absolute
|
|
{
|
|
operand = Absolute();
|
|
CPX(operand);
|
|
break;
|
|
}
|
|
|
|
case 0xC0: // CPY Immediate
|
|
{
|
|
operand = Immediate();
|
|
immediate = true;
|
|
CPY(operand, immediate);
|
|
break;
|
|
}
|
|
case 0xC4: // CPY Direct Page
|
|
{
|
|
operand = DirectPage();
|
|
CPY(operand);
|
|
break;
|
|
}
|
|
case 0xCC: // CPY Absolute
|
|
{
|
|
operand = Absolute();
|
|
CPY(operand);
|
|
break;
|
|
}
|
|
|
|
case 0x3A: // DEC Accumulator
|
|
{
|
|
DEC(A, /*accumulator=*/true);
|
|
break;
|
|
}
|
|
case 0xC6: // DEC Direct Page
|
|
{
|
|
operand = DirectPage();
|
|
DEC(operand);
|
|
break;
|
|
}
|
|
case 0xCE: // DEC Absolute
|
|
{
|
|
operand = Absolute();
|
|
DEC(operand);
|
|
break;
|
|
}
|
|
case 0xD6: // DEC DP Indexed, X
|
|
{
|
|
operand = DirectPageIndexedX();
|
|
DEC(operand);
|
|
break;
|
|
}
|
|
case 0xDE: // DEC Absolute Indexed, X
|
|
{
|
|
operand = AbsoluteIndexedX();
|
|
DEC(operand);
|
|
break;
|
|
}
|
|
|
|
case 0xCA: // DEX
|
|
{
|
|
DEX();
|
|
break;
|
|
}
|
|
|
|
case 0x88: // DEY
|
|
{
|
|
DEY();
|
|
break;
|
|
}
|
|
|
|
case 0x41: // EOR DP Indexed Indirect, X
|
|
{
|
|
operand = DirectPageIndexedIndirectX();
|
|
EOR(operand);
|
|
break;
|
|
}
|
|
case 0x43: // EOR Stack Relative
|
|
{
|
|
operand = StackRelative();
|
|
EOR(operand);
|
|
break;
|
|
}
|
|
case 0x45: // EOR Direct Page
|
|
{
|
|
operand = DirectPage();
|
|
EOR(operand);
|
|
break;
|
|
}
|
|
case 0x47: // EOR DP Indirect Long
|
|
{
|
|
operand = DirectPageIndirectLong();
|
|
EOR(operand);
|
|
break;
|
|
}
|
|
case 0x49: // EOR Immediate
|
|
{
|
|
operand = Immediate();
|
|
immediate = true;
|
|
EOR(operand, immediate);
|
|
break;
|
|
}
|
|
case 0x4D: // EOR Absolute
|
|
{
|
|
operand = Absolute();
|
|
EOR(operand);
|
|
break;
|
|
}
|
|
case 0x4F: // EOR Absolute Long
|
|
{
|
|
operand = AbsoluteLong();
|
|
EOR(operand);
|
|
break;
|
|
}
|
|
case 0x51: // EOR DP Indirect Indexed, Y
|
|
{
|
|
operand = DirectPageIndirectIndexedY();
|
|
EOR(operand);
|
|
break;
|
|
}
|
|
case 0x52: // EOR DP Indirect
|
|
{
|
|
operand = DirectPageIndirect();
|
|
EOR(operand);
|
|
break;
|
|
}
|
|
case 0x53: // EOR SR Indirect Indexed, Y
|
|
{
|
|
operand = StackRelativeIndirectIndexedY();
|
|
EOR(operand);
|
|
break;
|
|
}
|
|
case 0x55: // EOR DP Indexed, X
|
|
{
|
|
operand = DirectPageIndexedX();
|
|
EOR(operand);
|
|
break;
|
|
}
|
|
case 0x57: // EOR DP Indirect Long Indexed, Y
|
|
{
|
|
operand = ReadByteOrWord(DirectPageIndirectLongIndexedY());
|
|
EOR(operand);
|
|
break;
|
|
}
|
|
case 0x59: // EOR Absolute Indexed, Y
|
|
{
|
|
operand = AbsoluteIndexedY();
|
|
EOR(operand);
|
|
break;
|
|
}
|
|
case 0x5D: // EOR Absolute Indexed, X
|
|
{
|
|
operand = AbsoluteIndexedX();
|
|
EOR(operand);
|
|
break;
|
|
}
|
|
case 0x5F: // EOR Absolute Long Indexed, X
|
|
{
|
|
operand = AbsoluteLongIndexedX();
|
|
EOR(operand);
|
|
break;
|
|
}
|
|
|
|
case 0x1A: // INC Accumulator
|
|
{
|
|
INC(A, /*accumulator=*/true);
|
|
break;
|
|
}
|
|
case 0xE6: // INC Direct Page
|
|
{
|
|
operand = DirectPage();
|
|
INC(operand);
|
|
break;
|
|
}
|
|
case 0xEE: // INC Absolute
|
|
{
|
|
operand = Absolute();
|
|
INC(operand);
|
|
break;
|
|
}
|
|
case 0xF6: // INC DP Indexed, X
|
|
{
|
|
operand = DirectPageIndexedX();
|
|
INC(operand);
|
|
break;
|
|
}
|
|
case 0xFE: // INC Absolute Indexed, X
|
|
{
|
|
operand = AbsoluteIndexedX();
|
|
INC(operand);
|
|
break;
|
|
}
|
|
|
|
case 0xE8: // INX
|
|
{
|
|
INX();
|
|
break;
|
|
}
|
|
|
|
case 0xC8: // INY
|
|
{
|
|
INY();
|
|
break;
|
|
}
|
|
|
|
case 0x4C: // JMP Absolute
|
|
{
|
|
JMP(Absolute());
|
|
break;
|
|
}
|
|
case 0x5C: // JMP Absolute Long
|
|
{
|
|
JML(AbsoluteLong());
|
|
break;
|
|
}
|
|
case 0x6C: // JMP Absolute Indirect
|
|
{
|
|
JMP(AbsoluteIndirect());
|
|
break;
|
|
}
|
|
case 0x7C: // JMP Absolute Indexed Indirect
|
|
{
|
|
JMP(AbsoluteIndexedIndirect());
|
|
break;
|
|
}
|
|
case 0xDC: // JMP Absolute Indirect Long
|
|
{
|
|
operand = AbsoluteIndirectLong();
|
|
JMP(operand);
|
|
PB = operand >> 16;
|
|
break;
|
|
}
|
|
|
|
case 0x20: // JSR Absolute
|
|
{
|
|
operand = Absolute(AccessType::Control);
|
|
JSR(operand);
|
|
break;
|
|
}
|
|
|
|
case 0x22: // JSL Absolute Long
|
|
{
|
|
JSL(AbsoluteLong());
|
|
break;
|
|
}
|
|
|
|
case 0xFC: // JSR Absolute Indexed Indirect
|
|
{
|
|
JSR(AbsoluteIndexedIndirect());
|
|
break;
|
|
}
|
|
|
|
case 0xA1: // LDA DP Indexed Indirect, X
|
|
{
|
|
operand = DirectPageIndexedIndirectX();
|
|
LDA(operand);
|
|
break;
|
|
}
|
|
case 0xA3: // LDA Stack Relative
|
|
{
|
|
operand = StackRelative();
|
|
LDA(operand);
|
|
break;
|
|
}
|
|
case 0xA5: // LDA Direct Page
|
|
{
|
|
operand = DirectPage();
|
|
LDA(operand, false, true);
|
|
break;
|
|
}
|
|
case 0xA7: // LDA DP Indirect Long
|
|
{
|
|
operand = DirectPageIndirectLong();
|
|
LDA(operand);
|
|
break;
|
|
}
|
|
case 0xA9: // LDA Immediate
|
|
{
|
|
operand = Immediate();
|
|
immediate = true;
|
|
LDA(operand, immediate);
|
|
break;
|
|
}
|
|
case 0xAD: // LDA Absolute
|
|
{
|
|
operand = Absolute();
|
|
LDA(operand);
|
|
break;
|
|
}
|
|
case 0xAF: // LDA Absolute Long
|
|
{
|
|
operand = AbsoluteLong();
|
|
LDA(operand);
|
|
break;
|
|
}
|
|
case 0xB1: // LDA DP Indirect Indexed, Y
|
|
{
|
|
operand = DirectPageIndirectIndexedY();
|
|
LDA(operand);
|
|
break;
|
|
}
|
|
case 0xB2: // LDA DP Indirect
|
|
{
|
|
operand = DirectPageIndirect();
|
|
LDA(operand);
|
|
break;
|
|
}
|
|
case 0xB3: // LDA SR Indirect Indexed, Y
|
|
{
|
|
operand = StackRelativeIndirectIndexedY();
|
|
LDA(operand);
|
|
break;
|
|
}
|
|
case 0xB5: // LDA DP Indexed, X
|
|
{
|
|
operand = DirectPageIndexedX();
|
|
LDA(operand);
|
|
break;
|
|
}
|
|
case 0xB7: // LDA DP Indirect Long Indexed, Y
|
|
{
|
|
operand = DirectPageIndirectLongIndexedY();
|
|
LDA(operand);
|
|
break;
|
|
}
|
|
case 0xB9: // LDA Absolute Indexed, Y
|
|
{
|
|
operand = AbsoluteIndexedY();
|
|
LDA(operand);
|
|
break;
|
|
}
|
|
case 0xBD: // LDA Absolute Indexed, X
|
|
{
|
|
operand = AbsoluteIndexedX();
|
|
LDA(operand);
|
|
break;
|
|
}
|
|
case 0xBF: // LDA Absolute Long Indexed, X
|
|
{
|
|
operand = AbsoluteLongIndexedX();
|
|
LDA(operand);
|
|
break;
|
|
}
|
|
|
|
case 0xA2: // LDX Immediate
|
|
{
|
|
operand = Immediate();
|
|
immediate = true;
|
|
LDX(operand, immediate);
|
|
break;
|
|
}
|
|
case 0xA6: // LDX Direct Page
|
|
{
|
|
operand = DirectPage();
|
|
LDX(operand);
|
|
break;
|
|
}
|
|
case 0xAE: // LDX Absolute
|
|
{
|
|
operand = Absolute();
|
|
LDX(operand);
|
|
break;
|
|
}
|
|
case 0xB6: // LDX DP Indexed, Y
|
|
{
|
|
operand = DirectPageIndexedY();
|
|
LDX(operand);
|
|
break;
|
|
}
|
|
case 0xBE: // LDX Absolute Indexed, Y
|
|
{
|
|
operand = AbsoluteIndexedY();
|
|
LDX(operand);
|
|
break;
|
|
}
|
|
|
|
case 0xA0: // LDY Immediate
|
|
{
|
|
operand = Immediate();
|
|
immediate = true;
|
|
LDY(operand, immediate);
|
|
break;
|
|
}
|
|
case 0xA4: // LDY Direct Page
|
|
{
|
|
operand = DirectPage();
|
|
LDY(operand);
|
|
break;
|
|
}
|
|
case 0xAC: // LDY Absolute
|
|
{
|
|
operand = Absolute();
|
|
LDY(operand);
|
|
break;
|
|
}
|
|
case 0xB4: // LDY DP Indexed, X
|
|
{
|
|
operand = DirectPageIndexedX();
|
|
LDY(operand);
|
|
break;
|
|
}
|
|
case 0xBC: // LDY Absolute Indexed, X
|
|
{
|
|
operand = AbsoluteIndexedX();
|
|
LDY(operand);
|
|
break;
|
|
}
|
|
|
|
case 0x46: // LSR Direct Page
|
|
{
|
|
operand = DirectPage();
|
|
LSR(operand);
|
|
break;
|
|
}
|
|
case 0x4A: // LSR Accumulator
|
|
{
|
|
LSR(A, /*accumulator=*/true);
|
|
break;
|
|
}
|
|
case 0x4E: // LSR Absolute
|
|
{
|
|
operand = Absolute();
|
|
LSR(operand);
|
|
break;
|
|
}
|
|
case 0x56: // LSR DP Indexed, X
|
|
{
|
|
operand = DirectPageIndexedX();
|
|
LSR(operand);
|
|
break;
|
|
}
|
|
case 0x5E: // LSR Absolute Indexed, X
|
|
{
|
|
operand = AbsoluteIndexedX();
|
|
LSR(operand);
|
|
break;
|
|
}
|
|
|
|
case 0x54:
|
|
// MVN();
|
|
break;
|
|
|
|
case 0xEA: // NOP
|
|
NOP();
|
|
break;
|
|
|
|
case 0x01: // ORA DP Indexed Indirect, X
|
|
operand = DirectPageIndexedIndirectX();
|
|
ORA(operand);
|
|
break;
|
|
case 0x03: // ORA Stack Relative
|
|
operand = StackRelative();
|
|
ORA(operand);
|
|
break;
|
|
case 0x05: // ORA Direct Page
|
|
operand = DirectPage();
|
|
ORA(operand);
|
|
break;
|
|
case 0x07: // ORA DP Indirect Long
|
|
operand = DirectPageIndirectLong();
|
|
ORA(operand);
|
|
break;
|
|
case 0x09: // ORA Immediate
|
|
operand = Immediate();
|
|
immediate = true;
|
|
ORA(operand, immediate);
|
|
break;
|
|
case 0x0D: // ORA Absolute
|
|
operand = Absolute();
|
|
ORA(operand);
|
|
break;
|
|
case 0x0F: // ORA Absolute Long
|
|
operand = AbsoluteLong();
|
|
ORA(operand);
|
|
break;
|
|
case 0x11: // ORA DP Indirect Indexed, Y
|
|
operand = DirectPageIndirectIndexedY();
|
|
ORA(operand);
|
|
break;
|
|
case 0x12: // ORA DP Indirect
|
|
operand = DirectPageIndirect();
|
|
ORA(operand);
|
|
break;
|
|
case 0x13: // ORA SR Indirect Indexed, Y
|
|
operand = StackRelativeIndirectIndexedY();
|
|
ORA(operand);
|
|
break;
|
|
case 0x15: // ORA DP Indexed, X
|
|
operand = DirectPageIndexedX();
|
|
ORA(operand);
|
|
break;
|
|
case 0x17: // ORA DP Indirect Long Indexed, Y
|
|
operand = DirectPageIndirectLongIndexedY();
|
|
ORA(operand);
|
|
break;
|
|
case 0x19: // ORA Absolute Indexed, Y
|
|
operand = AbsoluteIndexedY();
|
|
ORA(operand);
|
|
break;
|
|
case 0x1D: // ORA Absolute Indexed, X
|
|
operand = AbsoluteIndexedX();
|
|
ORA(operand);
|
|
break;
|
|
case 0x1F: // ORA Absolute Long Indexed, X
|
|
operand = AbsoluteLongIndexedX();
|
|
ORA(operand);
|
|
break;
|
|
|
|
case 0xF4: // PEA Push Effective Absolute address
|
|
PEA();
|
|
break;
|
|
|
|
case 0xD4: // PEI Push Effective Indirect address
|
|
PEI();
|
|
break;
|
|
|
|
case 0x62: // PER Push Effective PC Relative Indirect address
|
|
PER();
|
|
break;
|
|
|
|
case 0x48: // PHA Push Accumulator
|
|
PHA();
|
|
break;
|
|
|
|
case 0x8B: // PHB Push Data Bank Register
|
|
PHB();
|
|
break;
|
|
|
|
case 0x0B: // PHD Push Direct Page Register
|
|
PHD();
|
|
break;
|
|
|
|
case 0x4B: // PHK Push Program Bank Register
|
|
PHK();
|
|
break;
|
|
|
|
case 0x08: // PHP Push Processor Status Register
|
|
PHP();
|
|
break;
|
|
|
|
case 0xDA: // PHX Push X register
|
|
PHX();
|
|
break;
|
|
|
|
case 0x5A: // PHY Push Y register
|
|
PHY();
|
|
break;
|
|
|
|
case 0x68: // PLA Pull Accumulator
|
|
PLA();
|
|
break;
|
|
|
|
case 0xAB: // PLB Pull Data Bank Register
|
|
PLB();
|
|
break;
|
|
|
|
case 0x2B: // PLD Pull Direct Page Register
|
|
PLD();
|
|
break;
|
|
|
|
case 0x28: // PLP Pull Processor Status Register
|
|
PLP();
|
|
break;
|
|
|
|
case 0xFA: // PLX Pull X register
|
|
PLX();
|
|
break;
|
|
|
|
case 0x7A: // PLY Pull Y register
|
|
PLY();
|
|
break;
|
|
|
|
case 0xC2: // REP Reset status bits
|
|
operand = FetchByte();
|
|
immediate = true;
|
|
REP();
|
|
break;
|
|
|
|
case 0x26: // ROL Direct Page
|
|
operand = DirectPage();
|
|
ROL(operand);
|
|
break;
|
|
case 0x2A: // ROL Accumulator
|
|
ROL(A, /*accumulator=*/true);
|
|
break;
|
|
case 0x2E: // ROL Absolute
|
|
operand = Absolute();
|
|
ROL(operand);
|
|
break;
|
|
case 0x36: // ROL DP Indexed, X
|
|
operand = DirectPageIndexedX();
|
|
ROL(operand);
|
|
break;
|
|
case 0x3E: // ROL Absolute Indexed, X
|
|
operand = AbsoluteIndexedX();
|
|
ROL(operand);
|
|
break;
|
|
|
|
case 0x66: // ROR Direct Page
|
|
operand = DirectPage();
|
|
ROR(operand);
|
|
break;
|
|
case 0x6A: // ROR Accumulator
|
|
ROR(A, /*accumulator=*/true);
|
|
break;
|
|
case 0x6E: // ROR Absolute
|
|
operand = Absolute();
|
|
ROR(operand);
|
|
break;
|
|
case 0x76: // ROR DP Indexed, X
|
|
operand = DirectPageIndexedX();
|
|
ROR(operand);
|
|
break;
|
|
case 0x7E: // ROR Absolute Indexed, X
|
|
operand = AbsoluteIndexedX();
|
|
ROR(operand);
|
|
break;
|
|
|
|
case 0x40: // RTI Return from interrupt
|
|
RTI();
|
|
break;
|
|
|
|
case 0x6B: // RTL Return from subroutine long
|
|
RTL();
|
|
break;
|
|
|
|
case 0x60: // RTS Return from subroutine
|
|
RTS();
|
|
break;
|
|
|
|
case 0xE1: // SBC DP Indexed Indirect, X
|
|
operand = DirectPageIndexedIndirectX();
|
|
SBC(operand);
|
|
break;
|
|
case 0xE3: // SBC Stack Relative
|
|
operand = StackRelative();
|
|
SBC(operand);
|
|
break;
|
|
case 0xE5: // SBC Direct Page
|
|
operand = DirectPage();
|
|
SBC(operand);
|
|
break;
|
|
case 0xE7: // SBC DP Indirect Long
|
|
operand = DirectPageIndirectLong();
|
|
SBC(operand);
|
|
break;
|
|
case 0xE9: // SBC Immediate
|
|
operand = Immediate();
|
|
immediate = true;
|
|
SBC(operand, immediate);
|
|
break;
|
|
case 0xED: // SBC Absolute
|
|
operand = Absolute();
|
|
SBC(operand);
|
|
break;
|
|
case 0xEF: // SBC Absolute Long
|
|
operand = AbsoluteLong();
|
|
SBC(operand);
|
|
break;
|
|
case 0xF1: // SBC DP Indirect Indexed, Y
|
|
operand = DirectPageIndirectIndexedY();
|
|
SBC(operand);
|
|
break;
|
|
case 0xF2: // SBC DP Indirect
|
|
operand = DirectPageIndirect();
|
|
SBC(operand);
|
|
break;
|
|
case 0xF3: // SBC SR Indirect Indexed, Y
|
|
operand = StackRelativeIndirectIndexedY();
|
|
SBC(operand);
|
|
break;
|
|
case 0xF5: // SBC DP Indexed, X
|
|
operand = DirectPageIndexedX();
|
|
SBC(operand);
|
|
break;
|
|
case 0xF7: // SBC DP Indirect Long Indexed, Y
|
|
operand = DirectPageIndirectLongIndexedY();
|
|
SBC(operand);
|
|
break;
|
|
case 0xF9: // SBC Absolute Indexed, Y
|
|
operand = AbsoluteIndexedY();
|
|
SBC(operand);
|
|
break;
|
|
case 0xFD: // SBC Absolute Indexed, X
|
|
operand = AbsoluteIndexedX();
|
|
SBC(operand);
|
|
break;
|
|
case 0xFF: // SBC Absolute Long Indexed, X
|
|
operand = AbsoluteLongIndexedX();
|
|
SBC(operand);
|
|
break;
|
|
|
|
case 0x38: // SEC Set carry
|
|
SEC();
|
|
break;
|
|
|
|
case 0xF8: // SED Set decimal
|
|
SED();
|
|
break;
|
|
|
|
case 0x78: // SEI Set interrupt disable
|
|
SEI();
|
|
break;
|
|
|
|
case 0xE2: // SEP Set status bits
|
|
operand = FetchByte();
|
|
immediate = true;
|
|
SEP();
|
|
break;
|
|
|
|
case 0x81: // STA DP Indexed Indirect, X
|
|
operand = DirectPageIndexedIndirectX();
|
|
STA(operand);
|
|
break;
|
|
case 0x83: // STA Stack Relative
|
|
operand = StackRelative();
|
|
STA(operand);
|
|
break;
|
|
case 0x85: // STA Direct Page
|
|
operand = DirectPage();
|
|
STA(operand);
|
|
break;
|
|
case 0x87: // STA DP Indirect Long
|
|
operand = DirectPageIndirectLong();
|
|
STA(operand);
|
|
break;
|
|
case 0x8D: // STA Absolute
|
|
operand = Absolute(AccessType::Data);
|
|
STA(operand);
|
|
break;
|
|
case 0x8F: // STA Absolute Long
|
|
operand = AbsoluteLong();
|
|
STA(operand);
|
|
break;
|
|
case 0x91: // STA DP Indirect Indexed, Y
|
|
operand = DirectPageIndirectIndexedY();
|
|
STA(operand);
|
|
break;
|
|
case 0x92: // STA DP Indirect
|
|
operand = DirectPageIndirect();
|
|
STA(operand);
|
|
break;
|
|
case 0x93: // STA SR Indirect Indexed, Y
|
|
operand = StackRelativeIndirectIndexedY();
|
|
STA(operand);
|
|
break;
|
|
case 0x95: // STA DP Indexed, X
|
|
operand = DirectPageIndexedX();
|
|
STA(operand);
|
|
break;
|
|
case 0x97: // STA DP Indirect Long Indexed, Y
|
|
operand = DirectPageIndirectLongIndexedY();
|
|
STA(operand);
|
|
break;
|
|
case 0x99: // STA Absolute Indexed, Y
|
|
operand = AbsoluteIndexedY();
|
|
STA(operand);
|
|
break;
|
|
case 0x9D: // STA Absolute Indexed, X
|
|
operand = AbsoluteIndexedX();
|
|
STA(operand);
|
|
break;
|
|
case 0x9F: // STA Absolute Long Indexed, X
|
|
operand = AbsoluteLongIndexedX();
|
|
STA(operand);
|
|
break;
|
|
|
|
case 0xDB: // STP Stop the processor
|
|
STP();
|
|
break;
|
|
|
|
case 0x86: // STX Direct Page
|
|
operand = DirectPage();
|
|
STX(operand);
|
|
break;
|
|
case 0x8E: // STX Absolute
|
|
operand = Absolute();
|
|
STX(operand);
|
|
break;
|
|
case 0x96: // STX DP Indexed, Y
|
|
operand = DirectPageIndexedY();
|
|
STX(operand);
|
|
break;
|
|
|
|
case 0x84: // STY Direct Page
|
|
operand = DirectPage();
|
|
STY(operand);
|
|
break;
|
|
case 0x8C: // STY Absolute
|
|
operand = Absolute();
|
|
STY(operand);
|
|
break;
|
|
case 0x94: // STY DP Indexed, X
|
|
operand = DirectPageIndexedX();
|
|
STY(operand);
|
|
break;
|
|
|
|
case 0x64: // STZ Direct Page
|
|
operand = DirectPage();
|
|
STZ(operand);
|
|
break;
|
|
case 0x74: // STZ DP Indexed, X
|
|
operand = DirectPageIndexedX();
|
|
STZ(operand);
|
|
break;
|
|
case 0x9C: // STZ Absolute
|
|
operand = Absolute();
|
|
STZ(operand);
|
|
break;
|
|
case 0x9E: // STZ Absolute Indexed, X
|
|
operand = AbsoluteIndexedX();
|
|
STZ(operand);
|
|
break;
|
|
|
|
case 0xAA: // TAX Transfer accumulator to X
|
|
TAX();
|
|
break;
|
|
|
|
case 0xA8: // TAY Transfer accumulator to Y
|
|
TAY();
|
|
break;
|
|
|
|
case 0x5B: // TCD
|
|
TCD();
|
|
break;
|
|
|
|
case 0x1B: // TCS
|
|
TCS();
|
|
break;
|
|
|
|
case 0x7B: // TDC
|
|
TDC();
|
|
break;
|
|
|
|
case 0x14: // TRB Direct Page
|
|
operand = DirectPage();
|
|
TRB(operand);
|
|
break;
|
|
case 0x1C: // TRB Absolute
|
|
operand = Absolute();
|
|
TRB(operand);
|
|
break;
|
|
|
|
case 0x04: // TSB Direct Page
|
|
operand = DirectPage();
|
|
TSB(operand);
|
|
break;
|
|
case 0x0C: // TSB Absolute
|
|
operand = Absolute();
|
|
TSB(operand);
|
|
break;
|
|
|
|
case 0x3B: // TSC
|
|
TSC();
|
|
break;
|
|
|
|
case 0xBA: // TSX Transfer stack pointer to X
|
|
TSX();
|
|
break;
|
|
|
|
case 0x8A: // TXA Transfer X to accumulator
|
|
TXA();
|
|
break;
|
|
|
|
case 0x9A: // TXS Transfer X to stack pointer
|
|
TXS();
|
|
break;
|
|
|
|
case 0x9B: // TXY Transfer X to Y
|
|
TXY();
|
|
break;
|
|
|
|
case 0x98: // TYA Transfer Y to accumulator
|
|
TYA();
|
|
break;
|
|
|
|
case 0xBB: // TYX Transfer Y to X
|
|
TYX();
|
|
break;
|
|
|
|
case 0xCB: // WAI Wait for interrupt
|
|
WAI();
|
|
break;
|
|
|
|
case 0xEB: // XBA Exchange B and A
|
|
XBA();
|
|
break;
|
|
|
|
case 0xFB: // XCE Exchange carry and emulation bits
|
|
XCE();
|
|
break;
|
|
default:
|
|
std::cerr << "Unknown instruction: " << std::hex
|
|
<< static_cast<int>(opcode) << std::endl;
|
|
break;
|
|
}
|
|
|
|
LogInstructions(PC, opcode, operand, immediate, accumulator_mode);
|
|
instruction_length = GetInstructionLength(opcode);
|
|
UpdatePC(instruction_length);
|
|
}
|
|
|
|
void CPU::LogInstructions(uint16_t PC, uint8_t opcode, uint16_t operand,
|
|
bool immediate, bool accumulator_mode) {
|
|
if (flags()->kLogInstructions) {
|
|
std::ostringstream oss;
|
|
oss << "$" << std::uppercase << std::setw(2) << std::setfill('0')
|
|
<< static_cast<int>(PB) << ":" << std::hex << PC << ": 0x"
|
|
<< std::setw(2) << std::setfill('0') << std::hex
|
|
<< static_cast<int>(opcode) << " " << opcode_to_mnemonic.at(opcode)
|
|
<< " ";
|
|
|
|
// Log the operand.
|
|
std::string ops;
|
|
if (operand) {
|
|
if (immediate) {
|
|
ops += "#";
|
|
}
|
|
std::ostringstream oss_ops;
|
|
oss_ops << "$";
|
|
if (accumulator_mode) {
|
|
oss_ops << std::hex << std::setw(2) << std::setfill('0')
|
|
<< static_cast<int>(operand);
|
|
} else {
|
|
oss_ops << std::hex << std::setw(4) << std::setfill('0')
|
|
<< static_cast<int>(operand);
|
|
}
|
|
ops = oss_ops.str();
|
|
}
|
|
|
|
oss << ops << std::endl;
|
|
|
|
InstructionEntry entry(PC, opcode, ops, oss.str());
|
|
instruction_log_.push_back(entry);
|
|
} else {
|
|
// Log the address and opcode.
|
|
std::cout << "\033[1;36m"
|
|
<< "$" << std::uppercase << std::setw(2) << std::setfill('0')
|
|
<< static_cast<int>(PB) << ":" << std::hex << PC;
|
|
std::cout << " \033[1;32m"
|
|
<< ": 0x" << std::hex << static_cast<int>(opcode) << " ";
|
|
std::cout << " \033[1;35m" << opcode_to_mnemonic.at(opcode) << " "
|
|
<< "\033[0m";
|
|
|
|
// Log the operand.
|
|
if (operand) {
|
|
if (immediate) {
|
|
std::cout << "#";
|
|
}
|
|
std::cout << "$";
|
|
if (accumulator_mode) {
|
|
std::cout << std::hex << std::setw(2) << std::setfill('0') << operand;
|
|
} else {
|
|
std::cout << std::hex << std::setw(4) << std::setfill('0')
|
|
<< static_cast<int>(operand);
|
|
}
|
|
}
|
|
std::cout << std::endl;
|
|
}
|
|
}
|
|
|
|
uint8_t CPU::GetInstructionLength(uint8_t opcode) {
|
|
switch (opcode) {
|
|
case 0x00: // BRK
|
|
case 0x02: // COP
|
|
PC = next_pc_;
|
|
return 0;
|
|
|
|
// TODO: Handle JMPs in logging.
|
|
case 0x20: // JSR Absolute
|
|
case 0x4C: // JMP Absolute
|
|
case 0x6C: // JMP Absolute Indirect
|
|
case 0x5C: // JMP Absolute Indexed Indirect
|
|
case 0x22: // JSL Absolute Long
|
|
case 0x7C: // JMP Absolute Indexed Indirect
|
|
case 0xFC: // JSR Absolute Indexed Indirect
|
|
case 0xDC: // JMP Absolute Indirect Long
|
|
case 0x6B: // RTL
|
|
|
|
case 0x82: // BRL Relative Long
|
|
PC = next_pc_;
|
|
return 0;
|
|
|
|
case 0x80: // BRA Relative
|
|
PC += next_pc_;
|
|
return 2;
|
|
|
|
case 0x60: // RTS
|
|
PC = last_call_frame_;
|
|
return 0;
|
|
|
|
// Branch Instructions (BCC, BCS, BNE, BEQ, etc.)
|
|
case 0x90: // BCC near
|
|
if (!GetCarryFlag()) {
|
|
PC = next_pc_;
|
|
return 0;
|
|
} else {
|
|
return 2;
|
|
}
|
|
case 0xB0: // BCS near
|
|
if (GetCarryFlag()) {
|
|
PC = next_pc_;
|
|
return 0;
|
|
} else {
|
|
return 2;
|
|
}
|
|
case 0x30: // BMI near
|
|
if (GetNegativeFlag()) {
|
|
PC = next_pc_;
|
|
return 0;
|
|
} else {
|
|
return 2;
|
|
}
|
|
case 0xF0: // BEQ near
|
|
if (GetZeroFlag()) {
|
|
PC = next_pc_;
|
|
return 0;
|
|
} else {
|
|
return 2;
|
|
}
|
|
|
|
case 0xD0: // BNE Relative
|
|
if (!GetZeroFlag()) {
|
|
PC += next_pc_;
|
|
}
|
|
return 2;
|
|
|
|
case 0x10: // BPL Relative
|
|
if (!GetNegativeFlag()) {
|
|
PC = next_pc_;
|
|
return 0;
|
|
} else {
|
|
return 2;
|
|
}
|
|
|
|
case 0x50: // BVC Relative
|
|
if (!GetOverflowFlag()) {
|
|
PC = next_pc_;
|
|
return 0;
|
|
} else {
|
|
return 2;
|
|
}
|
|
|
|
case 0x70: // BVS Relative
|
|
if (GetOverflowFlag()) {
|
|
PC = next_pc_;
|
|
return 0;
|
|
} else {
|
|
return 2;
|
|
}
|
|
|
|
case 0x18: // CLC
|
|
case 0xD8: // CLD
|
|
case 0x58: // CLI
|
|
case 0xB8: // CLV
|
|
case 0xCA: // DEX
|
|
case 0x88: // DEY
|
|
case 0xE8: // INX
|
|
case 0xC8: // INY
|
|
case 0xEA: // NOP
|
|
case 0x48: // PHA
|
|
case 0x8B: // PHB
|
|
case 0x0B: // PHD
|
|
case 0x4B: // PHK
|
|
case 0x08: // PHP
|
|
case 0xDA: // PHX
|
|
case 0x5A: // PHY
|
|
case 0x68: // PLA
|
|
case 0xAB: // PLB
|
|
case 0x2B: // PLD
|
|
case 0x28: // PLP
|
|
case 0xFA: // PLX
|
|
case 0x7A: // PLY
|
|
case 0x40: // RTI
|
|
case 0x38: // SEC
|
|
case 0xF8: // SED
|
|
case 0xBB: // TYX
|
|
case 0x78: // SEI
|
|
case 0xAA: // TAX
|
|
case 0xA8: // TAY
|
|
case 0xBA: // TSX
|
|
case 0x8A: // TXA
|
|
case 0x9B: // TXY
|
|
case 0x9A: // TXS
|
|
case 0x98: // TYA
|
|
case 0x0A: // ASL Accumulator
|
|
case 0x2A: // ROL Accumulator
|
|
case 0xFB: // XCE
|
|
case 0x5B: // TCD
|
|
case 0x1B: // TCS
|
|
case 0x3A: // DEC Accumulator
|
|
case 0x1A: // INC Accumulator
|
|
case 0x7B: // TDC
|
|
case 0x3B: // TSC
|
|
case 0xEB: // XBA
|
|
case 0xCB: // WAI
|
|
case 0xDB: // STP
|
|
case 0x4A: // LSR Accumulator
|
|
case 0x6A: // ROR Accumulator
|
|
return 1;
|
|
|
|
case 0xC2: // REP
|
|
case 0xE2: // SEP
|
|
case 0xE4: // CPX Direct Page
|
|
case 0xC4: // CPY Direct Page
|
|
case 0xD6: // DEC Direct Page Indexed, X
|
|
case 0x45: // EOR Direct Page
|
|
case 0xA5: // LDA Direct Page
|
|
case 0x05: // ORA Direct Page
|
|
case 0x85: // STA Direct Page
|
|
case 0xC6: // DEC Direct Page
|
|
case 0x97: // STA Direct Page Indexed Y
|
|
case 0x25: // AND Direct Page
|
|
case 0x32: // AND Direct Page Indirect Indexed Y
|
|
case 0x27: // AND Direct Page Indirect Long
|
|
case 0x35: // AND Direct Page Indexed X
|
|
case 0x21: // AND Direct Page Indirect Indexed Y
|
|
case 0x31: // AND Direct Page Indirect Long Indexed Y
|
|
case 0x37: // AND Direct Page Indirect Long Indexed Y
|
|
case 0x23: // AND Direct Page Indirect Indexed X
|
|
case 0x33: // AND Direct Page Indirect Long Indexed Y
|
|
case 0xE6: // INC Direct Page
|
|
case 0x81: // STA Direct Page Indirect, X
|
|
case 0x01: // ORA Direct Page Indirect, X
|
|
case 0x19: // ORA Direct Page Indirect Indexed, Y
|
|
case 0x1D: // ORA Absolute Indexed, X
|
|
case 0x89: // BIT Immediate
|
|
case 0x91: // STA Direct Page Indirect Indexed, Y
|
|
case 0x65: // ADC Direct Page
|
|
case 0x72: // ADC Direct Page Indirect
|
|
case 0x67: // ADC Direct Page Indirect Long
|
|
case 0x75: // ADC Direct Page Indexed, X
|
|
case 0x61: // ADC Direct Page Indirect, X
|
|
case 0x71: // ADC DP Indirect Indexed, Y
|
|
case 0x77: // ADC DP Indirect Long Indexed, Y
|
|
case 0x63: // ADC Stack Relative
|
|
case 0x73: // ADC SR Indirect Indexed, Y
|
|
case 0x06: // ASL Direct Page
|
|
case 0x16: // ASL Direct Page Indexed, X
|
|
case 0xB2: // LDA Direct Page Indirect
|
|
case 0x57: // EOR Direct Page Indirect Long Indexed, Y
|
|
case 0xC1: // CMP Direct Page Indexed Indirect, X
|
|
case 0xC3: // CMP Stack Relative
|
|
case 0xC5: // CMP Direct Page
|
|
case 0x47: // EOR Direct Page Indirect Long
|
|
case 0x55: // EOR Direct Page Indexed, X
|
|
case 0x41: // EOR Direct Page Indirect, X
|
|
case 0x51: // EOR Direct Page Indirect Indexed, Y
|
|
case 0x43: // EOR Direct Page Indirect Indexed, X
|
|
case 0x53: // EOR Direct Page Indirect Long Indexed, Y
|
|
case 0xA1: // LDA Direct Page Indexed Indirect, X
|
|
case 0xA3: // LDA Stack Relative
|
|
case 0xA7: // LDA Direct Page Indirect Long
|
|
case 0xB5: // LDA Direct Page Indexed, X
|
|
case 0xB1: // LDA Direct Page Indirect Indexed, Y
|
|
case 0xB7: // LDA Direct Page Indirect Long Indexed, Y
|
|
case 0xB3: // LDA Direct Page Indirect Indexed, X
|
|
case 0xB6: // LDX Direct Page Indexed, Y
|
|
case 0xB4: // LDY Direct Page Indexed, X
|
|
case 0x46: // LSR Direct Page
|
|
case 0x56: // LSR Direct Page Indexed, X
|
|
case 0xE1: // SBC Direct Page Indexed Indirect, X
|
|
case 0xE3: // SBC Stack Relative
|
|
case 0xE5: // SBC Direct Page
|
|
case 0xE7: // SBC Direct Page Indirect Long
|
|
case 0xF2: // SBC Direct Page Indirect
|
|
case 0xF1: // SBC Direct Page Indirect Indexed, Y
|
|
case 0xF3: // SBC SR Indirect Indexed, Y
|
|
case 0xF5: // SBC Direct Page Indexed, X
|
|
case 0xF7: // SBC Direct Page Indirect Long Indexed, Y
|
|
case 0xF6: // INC Direct Page Indexed, X
|
|
case 0x86: // STX Direct Page
|
|
case 0x84: // STY Direct Page
|
|
case 0x64: // STZ Direct Page
|
|
case 0x74: // STZ Direct Page Indexed, X
|
|
case 0x04: // TSB Direct Page
|
|
case 0x14: // TRB Direct Page
|
|
case 0x44: // MVN
|
|
case 0x54: // MVP
|
|
case 0x24: // BIT Direct Page
|
|
case 0x34: // BIT Direct Page Indexed, X
|
|
case 0x94: // STY Direct Page Indexed, X
|
|
case 0x87: // STA Direct Page Indirect Long
|
|
case 0x92: // STA Direct Page Indirect
|
|
case 0x93: // STA SR Indirect Indexed, Y
|
|
case 0x95: // STA Direct Page Indexed, X
|
|
case 0x96: // STX Direct Page Indexed, Y
|
|
case 0xC7: // CMP Direct Page Indirect Long
|
|
case 0xD7: // CMP DP Indirect Long Indexed, Y
|
|
case 0xD2: // CMP DP Indirect
|
|
case 0xD1: // CMP DP Indirect Indexed, Y
|
|
case 0x03: // ORA Stack Relative
|
|
case 0x13: // ORA SR Indirect Indexed, Y
|
|
case 0x07: // ORA Direct Page Indirect Long
|
|
case 0x11: // ORA DP Indirect Indexed, Y
|
|
case 0x12: // ORA DP Indirect
|
|
case 0x15: // ORA DP Indexed, X
|
|
case 0x17: // ORA DP Indirect Long Indexed, Y
|
|
case 0x26: // ROL Direct Page
|
|
case 0x36: // ROL Direct Page Indexed, X
|
|
case 0x66: // ROR Direct Page
|
|
case 0x76: // ROR Direct Page Indexed, X
|
|
case 0x42: // WDM
|
|
case 0xD3: // CMP Stack Relative Indirect Indexed, Y
|
|
case 0x52: // EOR Direct Page Indirect
|
|
case 0xA4: // LDA Direct Page
|
|
case 0xA6: // LDX Direct Page
|
|
case 0xD4: // PEI
|
|
return 2;
|
|
|
|
case 0x69: // ADC Immediate
|
|
case 0x29: // AND Immediate
|
|
case 0xC9: // CMP Immediate
|
|
case 0x49: // EOR Immediate
|
|
case 0xA9: // LDA Immediate
|
|
case 0x09: // ORA Immediate
|
|
case 0xE9: // SBC Immediate
|
|
return GetAccumulatorSize() ? 2 : 3;
|
|
|
|
case 0xE0: // CPX Immediate
|
|
case 0xC0: // CPY Immediate
|
|
case 0xA2: // LDX Immediate
|
|
case 0xA0: // LDY Immediate
|
|
return GetIndexSize() ? 2 : 3;
|
|
|
|
case 0x0E: // ASL Absolute
|
|
case 0x1E: // ASL Absolute Indexed, X
|
|
case 0x2D: // AND Absolute
|
|
case 0xCD: // CMP Absolute
|
|
case 0xEC: // CPX Absolute
|
|
case 0xCC: // CPY Absolute
|
|
case 0x4D: // EOR Absolute
|
|
case 0xAD: // LDA Absolute
|
|
case 0xAE: // LDX Absolute
|
|
case 0xAC: // LDY Absolute
|
|
case 0x0D: // ORA Absolute
|
|
case 0xED: // SBC Absolute
|
|
case 0x8D: // STA Absolute
|
|
case 0x8E: // STX Absolute
|
|
case 0x8C: // STY Absolute
|
|
case 0xBD: // LDA Absolute Indexed X
|
|
case 0xBC: // LDY Absolute Indexed X
|
|
case 0x3D: // AND Absolute Indexed X
|
|
case 0x39: // AND Absolute Indexed Y
|
|
case 0x9C: // STZ Absolute Indexed X
|
|
case 0x9D: // STA Absolute Indexed X
|
|
case 0x99: // STA Absolute Indexed Y
|
|
case 0x3C: // BIT Absolute Indexed X
|
|
case 0x7D: // ADC Absolute Indexed, X
|
|
case 0x79: // ADC Absolute Indexed, Y
|
|
case 0x6D: // ADC Absolute
|
|
case 0x5D: // EOR Absolute Indexed, X
|
|
case 0x59: // EOR Absolute Indexed, Y
|
|
case 0x83: // STA Stack Relative Indirect Indexed, Y
|
|
case 0xCE: // DEC Absolute
|
|
case 0xD5: // CMP DP Indexed, X
|
|
case 0xD9: // CMP Absolute Indexed, Y
|
|
case 0xDD: // CMP Absolute Indexed, X
|
|
case 0x0C: // TSB Absolute
|
|
case 0x1C: // TRB Absolute
|
|
case 0xF9: // SBC Absolute Indexed, Y
|
|
case 0xFD: // SBC Absolute Indexed, X
|
|
case 0x2C: // BIT Absolute
|
|
case 0x2E: // ROL Absolute
|
|
case 0x3E: // ROL Absolute Indexed, X
|
|
case 0x4E: // LSR Absolute
|
|
case 0x5E: // LSR Absolute Indexed, X
|
|
case 0xDE: // DEC Absolute Indexed, X
|
|
case 0xEE: // INC Absolute
|
|
case 0xB9: // LDA Absolute Indexed, Y
|
|
case 0xBE: // LDX Absolute Indexed, Y
|
|
case 0xFE: // INC Absolute Indexed, X
|
|
case 0xF4: // PEA
|
|
case 0x62: // PER
|
|
case 0x6E: // ROR Absolute
|
|
case 0x7E: // ROR Absolute Indexed, X
|
|
return 3;
|
|
|
|
case 0x6F: // ADC Absolute Long
|
|
case 0x2F: // AND Absolute Long
|
|
case 0xCF: // CMP Absolute Long
|
|
case 0x4F: // EOR Absolute Long
|
|
case 0xAF: // LDA Absolute Long
|
|
case 0x0F: // ORA Absolute Long
|
|
case 0xEF: // SBC Absolute Long
|
|
case 0x8F: // STA Absolute Long
|
|
case 0x7F: // ADC Absolute Long Indexed, X
|
|
case 0x3F: // AND Absolute Long Indexed, X
|
|
case 0xDF: // CMP Absolute Long Indexed, X
|
|
case 0x5F: // EOR Absolute Long Indexed, X
|
|
case 0x9F: // STA Absolute Long Indexed, X
|
|
case 0x1F: // ORA Absolute Long Indexed, X
|
|
case 0xBF: // LDA Absolute Long Indexed, X
|
|
case 0x9E: // STZ Absolute Long Indexed, X
|
|
case 0xFF: // SBC Absolute Long Indexed, X
|
|
return 4;
|
|
|
|
default:
|
|
auto mnemonic = opcode_to_mnemonic.at(opcode);
|
|
std::cerr << "Unknown instruction length: " << std::hex
|
|
<< static_cast<int>(opcode) << ", " << mnemonic << std::endl;
|
|
throw std::runtime_error("Unknown instruction length");
|
|
return 1; // Default to 1 as a safe fallback
|
|
}
|
|
}
|
|
|
|
void CPU::HandleInterrupts() {
|
|
if (GetInterruptFlag()) {
|
|
return;
|
|
}
|
|
|
|
/**
|
|
if (GetIRQFlag()) {
|
|
if (GetEmulationFlag()) {
|
|
PushWord(PC);
|
|
PushByte(status);
|
|
SetInterruptFlag(true);
|
|
SetDecimalFlag(false);
|
|
SetIRQFlag(false);
|
|
SetEmulationFlag(true);
|
|
try {
|
|
PC = memory.ReadWord(0xFFFE);
|
|
} catch (const std::exception& e) {
|
|
std::cout << "IRQ: " << e.what() << std::endl;
|
|
}
|
|
} else {
|
|
PushWord(PC);
|
|
PushByte(status);
|
|
SetInterruptFlag(true);
|
|
SetDecimalFlag(false);
|
|
SetIRQFlag(false);
|
|
SetEmulationFlag(false);
|
|
try {
|
|
PC = memory.ReadWord(0xFFFE);
|
|
} catch (const std::exception& e) {
|
|
std::cout << "IRQ: " << e.what() << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (GetNMIFlag()) {
|
|
if (GetEmulationFlag()) {
|
|
PushWord(PC);
|
|
PushByte(status);
|
|
SetInterruptFlag(true);
|
|
SetDecimalFlag(false);
|
|
SetNMIFlag(false);
|
|
SetEmulationFlag(true);
|
|
try {
|
|
PC = memory.ReadWord(0xFFFA);
|
|
} catch (const std::exception& e) {
|
|
std::cout << "NMI: " << e.what() << std::endl;
|
|
}
|
|
} else {
|
|
PushWord(PC);
|
|
PushByte(status);
|
|
SetInterruptFlag(true);
|
|
SetDecimalFlag(false);
|
|
SetNMIFlag(false);
|
|
SetEmulationFlag(false);
|
|
try {
|
|
PC = memory.ReadWord(0xFFFA);
|
|
} catch (const std::exception& e) {
|
|
std::cout << "NMI: " << e.what() << std::endl;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
} // namespace emu
|
|
} // namespace app
|
|
} // namespace yaze
|