389 lines
14 KiB
C++
389 lines
14 KiB
C++
#include "stdafx.h"
|
|
#include "CpuTypes.h"
|
|
#include "Cpu.h"
|
|
#include "MemoryManager.h"
|
|
|
|
Cpu::Cpu(shared_ptr<MemoryManager> memoryManager)
|
|
{
|
|
typedef Cpu C;
|
|
Func opTable[256] = {
|
|
//0 1 2 3 4 5 6 7 8 9 A B C D E F
|
|
&C::BRK, &C::ORA, &C::COP, &C::ORA, &C::TSB, &C::ORA, &C::ASL, &C::ORA, &C::PHP, &C::ORA, &C::ASL, &C::PHD, &C::TSB, &C::ORA, &C::ASL, &C::ORA, // 0
|
|
&C::BPL, &C::ORA, &C::ORA, &C::ORA, &C::TRB, &C::ORA, &C::ASL, &C::ORA, &C::CLC, &C::ORA, &C::INC, &C::TCS, &C::TRB, &C::ORA, &C::ASL, &C::ORA, // 1
|
|
&C::JSR, &C::AND, &C::JSL, &C::AND, &C::BIT, &C::AND, &C::ROL, &C::AND, &C::PLP, &C::AND, &C::ROL, &C::PLD, &C::BIT, &C::AND, &C::ROL, &C::AND, // 2
|
|
&C::BMI, &C::AND, &C::AND, &C::AND, &C::BIT, &C::AND, &C::ROL, &C::AND, &C::SEC, &C::AND, &C::DEC, &C::TSC, &C::BIT, &C::AND, &C::ROL, &C::AND, // 3
|
|
&C::RTI, &C::EOR, &C::WDM, &C::EOR, &C::MVP, &C::EOR, &C::LSR, &C::EOR, &C::PHA, &C::EOR, &C::LSR, &C::PHK, &C::JMP, &C::EOR, &C::LSR, &C::EOR, // 4
|
|
&C::BVC, &C::EOR, &C::EOR, &C::EOR, &C::MVN, &C::EOR, &C::LSR, &C::EOR, &C::CLI, &C::EOR, &C::PHY, &C::TCD, &C::JML, &C::EOR, &C::LSR, &C::EOR, // 5
|
|
&C::RTS, &C::ADC, &C::PER, &C::ADC, &C::STZ, &C::ADC, &C::ROR, &C::ADC, &C::PLA, &C::ADC, &C::ROR, &C::RTL, &C::JMP, &C::ADC, &C::ROR, &C::ADC, // 6
|
|
&C::BVS, &C::ADC, &C::ADC, &C::ADC, &C::STZ, &C::ADC, &C::ROR, &C::ADC, &C::SEI, &C::ADC, &C::PLY, &C::TDC, &C::JMP, &C::ADC, &C::ROR, &C::ADC, // 7
|
|
&C::BRA, &C::STA, &C::BRL, &C::STA, &C::STY, &C::STA, &C::STX, &C::STA, &C::DEY, &C::BIT, &C::TXA, &C::PHB, &C::STY, &C::STA, &C::STX, &C::STA, // 8
|
|
&C::BCC, &C::STA, &C::STA, &C::STA, &C::STY, &C::STA, &C::STX, &C::STA, &C::TYA, &C::STA, &C::TXS, &C::TXY, &C::STZ, &C::STA, &C::STZ, &C::STA, // 9
|
|
&C::LDY, &C::LDA, &C::LDX, &C::LDA, &C::LDY, &C::LDA, &C::LDX, &C::LDA, &C::TAY, &C::LDA, &C::TAX, &C::PLB, &C::LDY, &C::LDA, &C::LDX, &C::LDA, // A
|
|
&C::BCS, &C::LDA, &C::LDA, &C::LDA, &C::LDY, &C::LDA, &C::LDX, &C::LDA, &C::CLV, &C::LDA, &C::TSX, &C::TYX, &C::LDY, &C::LDA, &C::LDX, &C::LDA, // B
|
|
&C::CPY, &C::CMP, &C::REP, &C::CMP, &C::CPY, &C::CMP, &C::DEC, &C::CMP, &C::INY, &C::CMP, &C::DEX, &C::WAI, &C::CPY, &C::CMP, &C::DEC, &C::CMP, // C
|
|
&C::BNE, &C::CMP, &C::CMP, &C::CMP, &C::PEI, &C::CMP, &C::DEC, &C::CMP, &C::CLD, &C::CMP, &C::PHX, &C::STP, &C::JML, &C::CMP, &C::DEC, &C::CMP, // D
|
|
&C::CPX, &C::SBC, &C::SEP, &C::SBC, &C::CPX, &C::SBC, &C::INC, &C::SBC, &C::INX, &C::SBC, &C::NOP, &C::XBA, &C::CPX, &C::SBC, &C::INC, &C::SBC, // E
|
|
&C::BEQ, &C::SBC, &C::SBC, &C::SBC, &C::PEA, &C::SBC, &C::INC, &C::SBC, &C::SED, &C::SBC, &C::PLX, &C::XCE, &C::JSR, &C::SBC, &C::INC, &C::SBC // F
|
|
};
|
|
|
|
typedef AddrMode M;
|
|
AddrMode addrMode[256] = {
|
|
//0 1 2 3 4 5 6 7 8 9 A B C D E F
|
|
M::Stk, M::DirIdxIndX, M::Stk, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Stk, M::ImmM, M::Acc, M::Stk, M::Abs, M::Abs, M::Abs, M::AbsLng, // 0
|
|
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::Dir, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Acc, M::Imp, M::Abs, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // 1
|
|
M::AbsJmp,M::DirIdxIndX, M::AbsLngJmp,M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Stk, M::ImmM, M::Acc, M::Stk, M::Abs, M::Abs, M::Abs, M::AbsLng, // 2
|
|
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::DirIdxX, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Acc, M::Imp, M::AbsIdxX, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // 3
|
|
M::Stk, M::DirIdxIndX, M::Imm8, M::StkRel, M::BlkMov, M::Dir, M::Dir, M::DirIndLng, M::Stk, M::ImmM, M::Acc, M::Stk, M::AbsJmp, M::Abs, M::Abs, M::AbsLng, // 4
|
|
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::BlkMov, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Stk, M::Imp, M::AbsLngJmp, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // 5
|
|
M::Stk, M::DirIdxIndX, M::RelLng, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Stk, M::ImmM, M::Acc, M::Stk, M::AbsInd, M::Abs, M::Abs, M::AbsLng, // 6
|
|
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::DirIdxX, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Stk, M::Imp, M::AbsIdxXInd, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // 7
|
|
M::Rel, M::DirIdxIndX, M::RelLng, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Imp, M::ImmM, M::Imp, M::Stk, M::Abs, M::Abs, M::Abs, M::AbsLng, // 8
|
|
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::DirIdxX, M::DirIdxX, M::DirIdxY, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Imp, M::Imp, M::Abs, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // 9
|
|
M::ImmX, M::DirIdxIndX, M::ImmX, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Imp, M::ImmM, M::Imp, M::Stk, M::Abs, M::Abs, M::Abs, M::AbsLng, // A
|
|
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::DirIdxX, M::DirIdxX, M::DirIdxY, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Imp, M::Imp, M::AbsIdxX, M::AbsIdxX, M::AbsIdxY, M::AbsLngIdxX, // B
|
|
M::ImmX, M::DirIdxIndX, M::Imm8, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Imp, M::ImmM, M::Imp, M::Imp, M::Abs, M::Abs, M::Abs, M::AbsLng, // C
|
|
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::Dir, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Stk, M::Imp, M::AbsIndLng, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // D
|
|
M::ImmX, M::DirIdxIndX, M::Imm8, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Imp, M::ImmM, M::Imp, M::Imp, M::Abs, M::Abs, M::Abs, M::AbsLng, // E
|
|
M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::Imm16, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Stk, M::Imp, M::AbsIdxXInd, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX // F
|
|
};
|
|
|
|
memcpy(_opTable, opTable, sizeof(opTable));
|
|
memcpy(_addrMode, addrMode, sizeof(addrMode));
|
|
|
|
_memoryManager = memoryManager;
|
|
_state = {};
|
|
_state.PC = ReadDataWord(Cpu::ResetVector);
|
|
_state.SP = 0x1FF;
|
|
_state.EmulationMode = true;
|
|
_nmiFlag = false;
|
|
_irqSource = (uint8_t)IrqSource::None;
|
|
SetFlags(ProcFlags::MemoryMode8);
|
|
SetFlags(ProcFlags::IndexMode8);
|
|
}
|
|
|
|
Cpu::~Cpu()
|
|
{
|
|
}
|
|
|
|
void Cpu::Reset()
|
|
{
|
|
}
|
|
|
|
void Cpu::Exec()
|
|
{
|
|
uint8_t opCode = GetOpCode();
|
|
_instAddrMode = _addrMode[opCode];
|
|
_operand = FetchEffectiveAddress();
|
|
(this->*_opTable[opCode])();
|
|
|
|
opCount++;
|
|
|
|
if(_nmiFlag) {
|
|
ProcessInterrupt(_state.EmulationMode ? Cpu::LegacyNmiVector : Cpu::NmiVector);
|
|
_nmiFlag = false;
|
|
} else if(_irqSource && !CheckFlag(ProcFlags::IrqDisable)) {
|
|
ProcessInterrupt(_state.EmulationMode ? Cpu::LegacyIrqVector : Cpu::IrqVector);
|
|
}
|
|
}
|
|
|
|
void Cpu::SetNmiFlag()
|
|
{
|
|
_nmiFlag = true;
|
|
}
|
|
|
|
void Cpu::SetIrqSource(IrqSource source)
|
|
{
|
|
_irqSource |= (uint8_t)source;
|
|
}
|
|
|
|
bool Cpu::CheckIrqSource(IrqSource source)
|
|
{
|
|
if(_irqSource & (uint8_t)source) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void Cpu::ClearIrqSource(IrqSource source)
|
|
{
|
|
_irqSource &= ~(uint8_t)source;
|
|
}
|
|
|
|
uint32_t Cpu::GetProgramAddress(uint16_t addr)
|
|
{
|
|
return (_state.K << 16) | addr;
|
|
}
|
|
|
|
uint32_t Cpu::GetDataAddress(uint16_t addr)
|
|
{
|
|
return (_state.DBR << 16) | addr;
|
|
}
|
|
|
|
uint8_t Cpu::GetOpCode()
|
|
{
|
|
uint8_t opCode = ReadCode(_state.PC, MemoryOperationType::ExecOpCode);
|
|
_state.PC++;
|
|
return opCode;
|
|
}
|
|
|
|
void Cpu::DummyRead()
|
|
{
|
|
ReadCode(_state.PC, MemoryOperationType::DummyRead);
|
|
}
|
|
|
|
uint8_t Cpu::ReadOperandByte()
|
|
{
|
|
return ReadCode(_state.PC++, MemoryOperationType::ExecOperand);
|
|
}
|
|
|
|
uint16_t Cpu::ReadOperandWord()
|
|
{
|
|
uint8_t lsb = ReadOperandByte();
|
|
uint8_t msb = ReadOperandByte();
|
|
return (msb << 8) | lsb;
|
|
}
|
|
|
|
uint32_t Cpu::ReadOperandLong()
|
|
{
|
|
uint8_t b1 = ReadOperandByte();
|
|
uint8_t b2 = ReadOperandByte();
|
|
uint8_t b3 = ReadOperandByte();
|
|
return (b3 << 16) | (b2 << 8) | b1;
|
|
}
|
|
|
|
uint8_t Cpu::ReadCode(uint16_t addr, MemoryOperationType type)
|
|
{
|
|
_state.CycleCount++;
|
|
return _memoryManager->Read((_state.K << 16) | addr, type);
|
|
}
|
|
|
|
uint16_t Cpu::ReadCodeWord(uint16_t addr, MemoryOperationType type)
|
|
{
|
|
uint8_t lsb = ReadCode(addr);
|
|
uint8_t msb = ReadCode(addr + 1);
|
|
return (msb << 8) | lsb;
|
|
}
|
|
|
|
uint8_t Cpu::ReadData(uint32_t addr, MemoryOperationType type)
|
|
{
|
|
_state.CycleCount++;
|
|
return _memoryManager->Read(addr, type);
|
|
}
|
|
|
|
uint16_t Cpu::ReadDataWord(uint32_t addr, MemoryOperationType type)
|
|
{
|
|
uint8_t lsb = ReadData(addr);
|
|
uint8_t msb = ReadData(addr + 1);
|
|
return (msb << 8) | lsb;
|
|
}
|
|
|
|
uint32_t Cpu::ReadDataLong(uint32_t addr, MemoryOperationType type)
|
|
{
|
|
uint8_t b1 = ReadData(addr);
|
|
uint8_t b2 = ReadData(addr + 1);
|
|
uint8_t b3 = ReadData(addr + 2);
|
|
return (b3 << 16) | (b2 << 8) | b1;
|
|
}
|
|
|
|
void Cpu::Write(uint32_t addr, uint8_t value, MemoryOperationType type)
|
|
{
|
|
_state.CycleCount++;
|
|
_memoryManager->Write(addr, value, type);
|
|
}
|
|
|
|
void Cpu::WriteWord(uint32_t addr, uint16_t value, MemoryOperationType type)
|
|
{
|
|
Write(addr, (uint8_t)value);
|
|
Write(addr + 1, (uint8_t)(value >> 8));
|
|
}
|
|
|
|
uint8_t Cpu::GetByteValue()
|
|
{
|
|
if(_instAddrMode <= AddrMode::ImmM) {
|
|
return (uint8_t)_operand;
|
|
} else {
|
|
return ReadData(_operand);
|
|
}
|
|
}
|
|
|
|
uint16_t Cpu::GetWordValue()
|
|
{
|
|
if(_instAddrMode <= AddrMode::ImmM) {
|
|
return (uint16_t)_operand;
|
|
} else {
|
|
return ReadDataWord(_operand);
|
|
}
|
|
}
|
|
|
|
void Cpu::PushByte(uint8_t value)
|
|
{
|
|
Write(_state.SP, value);
|
|
SetSP(_state.SP - 1);
|
|
}
|
|
|
|
uint8_t Cpu::PopByte()
|
|
{
|
|
SetSP(_state.SP + 1);
|
|
return ReadData(_state.SP);
|
|
}
|
|
|
|
void Cpu::PushWord(uint16_t value)
|
|
{
|
|
PushByte(value >> 8);
|
|
PushByte((uint8_t)value);
|
|
}
|
|
|
|
uint16_t Cpu::PopWord()
|
|
{
|
|
uint8_t lo = PopByte();
|
|
uint8_t hi = PopByte();
|
|
return lo | hi << 8;
|
|
}
|
|
|
|
uint16_t Cpu::GetDirectAddress(uint16_t offset, bool allowEmulationMode)
|
|
{
|
|
if(allowEmulationMode && _state.EmulationMode && (_state.D & 0xFF) == 0) {
|
|
//TODO: Check if new instruction or not (PEI)
|
|
return (uint16_t)((_state.D & 0xFF00) | (offset & 0xFF));
|
|
} else {
|
|
return (uint16_t)(_state.D + offset);
|
|
}
|
|
}
|
|
|
|
uint16_t Cpu::GetDirectAddressIndirectWord(uint16_t offset, bool allowEmulationMode)
|
|
{
|
|
uint8_t lsb = ReadData(GetDirectAddress(offset + 0));
|
|
uint8_t msb = ReadData(GetDirectAddress(offset + 1));
|
|
return (msb << 8) | lsb;
|
|
}
|
|
|
|
uint32_t Cpu::GetDirectAddressIndirectLong(uint16_t offset, bool allowEmulationMode)
|
|
{
|
|
uint8_t b1 = ReadData(GetDirectAddress(offset + 0));
|
|
uint8_t b2 = ReadData(GetDirectAddress(offset + 1));
|
|
uint8_t b3 = ReadData(GetDirectAddress(offset + 2));
|
|
return (b3 << 16) | (b2 << 8) | b1;
|
|
}
|
|
|
|
uint32_t Cpu::FetchEffectiveAddress()
|
|
{
|
|
switch(_instAddrMode) {
|
|
case AddrMode::Abs: return GetDataAddress(ReadOperandWord());
|
|
case AddrMode::AbsIdxX: return (GetDataAddress(ReadOperandWord()) + _state.X) & 0xFFFFFF;
|
|
case AddrMode::AbsIdxY: return (GetDataAddress(ReadOperandWord()) + _state.Y) & 0xFFFFFF;
|
|
case AddrMode::AbsLng: return ReadOperandLong();
|
|
case AddrMode::AbsLngIdxX: return (ReadOperandLong() + _state.X) & 0xFFFFFF;
|
|
|
|
case AddrMode::AbsJmp: return GetProgramAddress(ReadOperandWord());
|
|
case AddrMode::AbsLngJmp: return ReadOperandLong();
|
|
case AddrMode::AbsIdxXInd: return GetProgramAddress(ReadDataWord(GetProgramAddress(ReadOperandWord() + _state.X))); //JMP/JSR
|
|
case AddrMode::AbsInd: return ReadDataWord(ReadOperandWord()); //JMP only
|
|
case AddrMode::AbsIndLng: return ReadDataLong(ReadOperandWord()); //JML only
|
|
|
|
case AddrMode::Acc: DummyRead(); return 0;
|
|
|
|
case AddrMode::BlkMov: return ReadOperandWord();
|
|
|
|
case AddrMode::Dir: return GetDirectAddress(ReadOperandByte());
|
|
case AddrMode::DirIdxX: return GetDirectAddress(ReadOperandByte() + _state.X);
|
|
case AddrMode::DirIdxY: return GetDirectAddress(ReadOperandByte() + _state.Y);
|
|
|
|
case AddrMode::DirInd: return GetDataAddress(GetDirectAddressIndirectWord(ReadOperandByte()));
|
|
case AddrMode::DirIdxIndX: return GetDataAddress(GetDirectAddressIndirectWord(ReadOperandByte() + _state.X));
|
|
case AddrMode::DirIndIdxY: return (GetDataAddress(GetDirectAddressIndirectWord(ReadOperandByte())) + _state.Y) & 0xFFFFFF;
|
|
case AddrMode::DirIndLng: return GetDirectAddressIndirectLong(ReadOperandByte());
|
|
case AddrMode::DirIndLngIdxY: return (GetDirectAddressIndirectLong(ReadOperandByte()) + _state.Y) & 0xFFFFFF;
|
|
|
|
case AddrMode::Imm8: return ReadOperandByte();
|
|
case AddrMode::Imm16: return ReadOperandWord();
|
|
case AddrMode::ImmX: return CheckFlag(ProcFlags::IndexMode8) ? ReadOperandByte() : ReadOperandWord();
|
|
case AddrMode::ImmM: return CheckFlag(ProcFlags::MemoryMode8) ? ReadOperandByte() : ReadOperandWord();
|
|
|
|
case AddrMode::Imp: DummyRead(); return 0;
|
|
|
|
case AddrMode::RelLng: return ReadOperandWord();
|
|
case AddrMode::Rel: return ReadOperandByte();
|
|
|
|
case AddrMode::Stk: return 0;
|
|
case AddrMode::StkRel: return (uint16_t)(ReadOperandByte() + _state.SP);
|
|
|
|
case AddrMode::StkRelIndIdxY: {
|
|
uint16_t addr = (uint16_t)(ReadOperandByte() + _state.SP);
|
|
return (GetDataAddress(ReadDataWord(addr)) + _state.Y) & 0xFFFFFF;
|
|
}
|
|
}
|
|
|
|
throw new std::runtime_error("Unreacheable code");
|
|
}
|
|
|
|
void Cpu::SetSP(uint16_t sp)
|
|
{
|
|
if(_state.EmulationMode) {
|
|
_state.SP = 0x100 | (sp & 0xFF);
|
|
} else {
|
|
_state.SP = sp;
|
|
}
|
|
}
|
|
|
|
void Cpu::SetPS(uint8_t ps)
|
|
{
|
|
_state.PS = ps;
|
|
if(CheckFlag(ProcFlags::IndexMode8)) {
|
|
//Truncate X/Y when 8-bit indexes are enabled
|
|
_state.Y &= 0xFF;
|
|
_state.X &= 0xFF;
|
|
}
|
|
}
|
|
|
|
void Cpu::SetRegister(uint8_t ®, uint8_t value)
|
|
{
|
|
SetZeroNegativeFlags(value);
|
|
reg = value;
|
|
}
|
|
|
|
void Cpu::SetRegister(uint16_t ®, uint16_t value, bool eightBitMode)
|
|
{
|
|
if(eightBitMode) {
|
|
SetZeroNegativeFlags((uint8_t)value);
|
|
reg = (reg & 0xFF00) | (uint8_t)value;
|
|
} else {
|
|
SetZeroNegativeFlags(value);
|
|
reg = value;
|
|
}
|
|
}
|
|
|
|
void Cpu::SetZeroNegativeFlags(uint16_t value)
|
|
{
|
|
ClearFlags(ProcFlags::Zero | ProcFlags::Negative);
|
|
if(value == 0) {
|
|
SetFlags(ProcFlags::Zero);
|
|
} else if(value & 0x8000) {
|
|
SetFlags(ProcFlags::Negative);
|
|
}
|
|
}
|
|
|
|
void Cpu::SetZeroNegativeFlags(uint8_t value)
|
|
{
|
|
ClearFlags(ProcFlags::Zero | ProcFlags::Negative);
|
|
if(value == 0) {
|
|
SetFlags(ProcFlags::Zero);
|
|
} else if(value & 0x80) {
|
|
SetFlags(ProcFlags::Negative);
|
|
}
|
|
}
|
|
|
|
void Cpu::ClearFlags(uint8_t flags)
|
|
{
|
|
_state.PS &= ~flags;
|
|
}
|
|
|
|
void Cpu::SetFlags(uint8_t flags)
|
|
{
|
|
_state.PS |= flags;
|
|
}
|
|
|
|
bool Cpu::CheckFlag(uint8_t flag)
|
|
{
|
|
return (_state.PS & flag) == flag;
|
|
}
|