diff --git a/src/app/emu/cpu.cc b/src/app/emu/cpu.cc index 963c9a45..627569cf 100644 --- a/src/app/emu/cpu.cc +++ b/src/app/emu/cpu.cc @@ -10,16 +10,24 @@ namespace yaze { namespace app { namespace emu { -void CPU::Update() { - auto cycles_to_run = clock.GetCycleCount(); +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_to_run; i++) { + for (int i = 0; i < cycles; i++) { + if (IsBreakpoint(PC)) { + break; + } + // Fetch and execute an instruction ExecuteInstruction(FetchByte()); // Handle any interrupts, if necessary HandleInterrupts(); + + if (mode == UpdateMode::Step) { + break; + } } } @@ -1093,6 +1101,11 @@ void CPU::ExecuteInstruction(uint8_t opcode) { break; } + LogInstructions(PC, opcode, operand, immediate, accumulator_mode); +} + +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') @@ -1160,7 +1173,7 @@ void CPU::HandleInterrupts() {} /** * 65816 Instruction Set * - * TODO: MVN, MVP, STP, WDM + * TODO: STP, WDM */ // ADC: Add with carry diff --git a/src/app/emu/cpu.h b/src/app/emu/cpu.h index 5783e428..556ac11d 100644 --- a/src/app/emu/cpu.h +++ b/src/app/emu/cpu.h @@ -9,6 +9,7 @@ #include "app/core/common.h" #include "app/emu/clock.h" #include "app/emu/debug/log.h" +#include "app/emu/internal/opcodes.h" #include "app/emu/memory/memory.h" namespace yaze { @@ -34,78 +35,94 @@ class InstructionEntry { std::string instruction; // Human-readable instruction text }; -const std::unordered_map opcode_to_mnemonic = { - {0x00, "BRK"}, {0x01, "ORA"}, {0x02, "COP"}, {0x03, "ORA"}, {0x04, "TSB"}, - {0x05, "ORA"}, {0x06, "ASL"}, {0x07, "ORA"}, {0x08, "PHP"}, {0x09, "ORA"}, - {0x0A, "ASL"}, {0x0B, "PHD"}, {0x0C, "TSB"}, {0x0D, "ORA"}, {0x0E, "ASL"}, - {0x0F, "ORA"}, {0x10, "BPL"}, {0x11, "ORA"}, {0x12, "ORA"}, {0x13, "ORA"}, - {0x14, "TRB"}, {0x15, "ORA"}, {0x16, "ASL"}, {0x17, "ORA"}, {0x18, "CLC"}, - {0x19, "ORA"}, {0x1A, "INC"}, {0x1B, "TCS"}, {0x1C, "TRB"}, {0x1D, "ORA"}, - {0x1E, "ASL"}, {0x1F, "ORA"}, {0x20, "JSR"}, {0x21, "AND"}, {0x22, "JSL"}, - {0x23, "AND"}, {0x24, "BIT"}, {0x25, "AND"}, {0x26, "ROL"}, {0x27, "AND"}, - {0x28, "PLP"}, {0x29, "AND"}, {0x2A, "ROL"}, {0x2B, "PLD"}, {0x2C, "BIT"}, - {0x2D, "AND"}, {0x2E, "ROL"}, {0x2F, "AND"}, {0x30, "BMI"}, {0x31, "AND"}, - {0x32, "AND"}, {0x33, "AND"}, {0x34, "BIT"}, {0x35, "AND"}, {0x36, "ROL"}, - {0x37, "AND"}, {0x38, "SEC"}, {0x39, "AND"}, {0x3A, "DEC"}, {0x3B, "TSC"}, - {0x3C, "BIT"}, {0x3D, "AND"}, {0x3E, "ROL"}, {0x3F, "AND"}, {0x40, "RTI"}, - {0x41, "EOR"}, {0x42, "WDM"}, {0x43, "EOR"}, {0x44, "MVP"}, {0x45, "EOR"}, - {0x46, "LSR"}, {0x47, "EOR"}, {0x48, "PHA"}, {0x49, "EOR"}, {0x4A, "LSR"}, - {0x4B, "PHK"}, {0x4C, "JMP"}, {0x4D, "EOR"}, {0x4E, "LSR"}, {0x4F, "EOR"}, - {0x50, "BVC"}, {0x51, "EOR"}, {0x52, "EOR"}, {0x53, "EOR"}, {0x54, "MVN"}, - {0x55, "EOR"}, {0x56, "LSR"}, {0x57, "EOR"}, {0x58, "CLI"}, {0x59, "EOR"}, - {0x5A, "PHY"}, {0x5B, "TCD"}, {0x5C, "JMP"}, {0x5D, "EOR"}, {0x5E, "LSR"}, - {0x5F, "EOR"}, {0x60, "RTS"}, {0x61, "ADC"}, {0x62, "PER"}, {0x63, "ADC"}, - {0x64, "STZ"}, {0x65, "ADC"}, {0x66, "ROR"}, {0x67, "ADC"}, {0x68, "PLA"}, - {0x69, "ADC"}, {0x6A, "ROR"}, {0x6B, "RTL"}, {0x6C, "JMP"}, {0x6D, "ADC"}, - {0x6E, "ROR"}, {0x6F, "ADC"}, {0x70, "BVS"}, {0x71, "ADC"}, {0x72, "ADC"}, - {0x73, "ADC"}, {0x74, "STZ"}, {0x75, "ADC"}, {0x76, "ROR"}, {0x77, "ADC"}, - {0x78, "SEI"}, {0x79, "ADC"}, {0x7A, "PLY"}, {0x7B, "TDC"}, {0x7C, "JMP"}, - {0x7D, "ADC"}, {0x7E, "ROR"}, {0x7F, "ADC"}, {0x80, "BRA"}, {0x81, "STA"}, - {0x82, "BRL"}, {0x83, "STA"}, {0x84, "STY"}, {0x85, "STA"}, {0x86, "STX"}, - {0x87, "STA"}, {0x88, "DEY"}, {0x89, "BIT"}, {0x8A, "TXA"}, {0x8B, "PHB"}, - {0x8C, "STY"}, {0x8D, "STA"}, {0x8E, "STX"}, {0x8F, "STA"}, {0x90, "BCC"}, - {0x91, "STA"}, {0x92, "STA"}, {0x93, "STA"}, {0x94, "STY"}, {0x95, "STA"}, - {0x96, "STX"}, {0x97, "STA"}, {0x98, "TYA"}, {0x99, "STA"}, {0x9A, "TXS"}, - {0x9B, "TXY"}, {0x9C, "STZ"}, {0x9D, "STA"}, {0x9E, "STZ"}, {0x9F, "STA"}, - {0xA0, "LDY"}, {0xA1, "LDA"}, {0xA2, "LDX"}, {0xA3, "LDA"}, {0xA4, "LDY"}, - {0xA5, "LDA"}, {0xA6, "LDX"}, {0xA7, "LDA"}, {0xA8, "TAY"}, {0xA9, "LDA"}, - {0xAA, "TAX"}, {0xAB, "PLB"}, {0xAC, "LDY"}, {0xAD, "LDA"}, {0xAE, "LDX"}, - {0xAF, "LDA"}, {0xB0, "BCS"}, {0xB1, "LDA"}, {0xB2, "LDA"}, {0xB3, "LDA"}, - {0xB4, "LDY"}, {0xB5, "LDA"}, {0xB6, "LDX"}, {0xB7, "LDA"}, {0xB8, "CLV"}, - {0xB9, "LDA"}, {0xBA, "TSX"}, {0xBB, "TYX"}, {0xBC, "LDY"}, {0xBD, "LDA"}, - {0xBE, "LDX"}, {0xBF, "LDA"}, {0xC0, "CPY"}, {0xC1, "CMP"}, {0xC2, "REP"}, - {0xC3, "CMP"}, {0xC4, "CPY"}, {0xC5, "CMP"}, {0xC6, "DEC"}, {0xC7, "CMP"}, - {0xC8, "INY"}, {0xC9, "CMP"}, {0xCA, "DEX"}, {0xCB, "WAI"}, {0xCC, "CPY"}, - {0xCD, "CMP"}, {0xCE, "DEC"}, {0xCF, "CMP"}, {0xD0, "BNE"}, {0xD1, "CMP"}, - {0xD2, "CMP"}, {0xD3, "CMP"}, {0xD4, "PEI"}, {0xD5, "CMP"}, {0xD6, "DEC"}, - {0xD7, "CMP"}, {0xD8, "CLD"}, {0xD9, "CMP"}, {0xDA, "PHX"}, {0xDB, "STP"}, - {0xDC, "JMP"}, {0xDD, "CMP"}, {0xDE, "DEC"}, {0xDF, "CMP"}, {0xE0, "CPX"}, - {0xE1, "SBC"}, {0xE2, "SEP"}, {0xE3, "SBC"}, {0xE4, "CPX"}, {0xE5, "SBC"}, - {0xE6, "INC"}, {0xE7, "SBC"}, {0xE8, "INX"}, {0xE9, "SBC"}, {0xEA, "NOP"}, - {0xEB, "XBA"}, {0xEC, "CPX"}, {0xED, "SBC"}, {0xEE, "INC"}, {0xEF, "SBC"}, - {0xF0, "BEQ"}, {0xF1, "SBC"}, {0xF2, "SBC"}, {0xF3, "SBC"}, {0xF4, "PEA"}, - {0xF5, "SBC"}, {0xF6, "INC"}, {0xF7, "SBC"}, {0xF8, "SED"}, {0xF9, "SBC"}, - {0xFA, "PLX"}, {0xFB, "XCE"}, {0xFC, "JSR"}, {0xFD, "SBC"}, {0xFE, "INC"}, - {0xFF, "SBC"} - -}; - const int kCpuClockSpeed = 21477272; // 21.477272 MHz class CPU : public Memory, public Loggable, public core::ExperimentFlags { public: explicit CPU(Memory& mem, Clock& vclock) : memory(mem), clock(vclock) {} + enum class UpdateMode { Run, Step, Pause }; - void Init() { - clock.SetFrequency(kCpuClockSpeed); - } + void Init() { clock.SetFrequency(kCpuClockSpeed); } + + void Update(UpdateMode mode = UpdateMode::Run, int stepCount = 1); - void Update(); void ExecuteInstruction(uint8_t opcode); + void LogInstructions(uint16_t PC, uint8_t opcode, uint16_t operand, + bool immediate, bool accumulator_mode); void HandleInterrupts(); + bool IsBreakpoint(uint32_t address) { + return std::find(breakpoints_.begin(), breakpoints_.end(), address) != + breakpoints_.end(); + } + + void SetBreakpoint(uint32_t address) { breakpoints_.push_back(address); } + + void ClearBreakpoint(uint32_t address) { + breakpoints_.erase( + std::remove(breakpoints_.begin(), breakpoints_.end(), address), + breakpoints_.end()); + } + + void ClearBreakpoints() { + breakpoints_.clear(); + breakpoints_.shrink_to_fit(); + } + + auto GetBreakpoints() { return breakpoints_; } + + std::vector breakpoints_; std::vector instruction_log_; + // ====================================================== + // Registers + + uint16_t A = 0; // Accumulator + uint16_t X = 0; // X index register + uint16_t Y = 0; // Y index register + uint16_t D = 0; // Direct Page register + uint8_t DB = 0; // Data Bank register + uint8_t PB = 0; // Program Bank register + uint16_t PC = 0; // Program Counter + uint8_t E = 1; // Emulation mode flag + uint8_t status = 0b00110000; // Processor Status (P) + + // Mnemonic Value Binary Description + // N #$80 10000000 Negative + // V #$40 01000000 Overflow + // M #$20 00100000 Accumulator size (0 = 16-bit, 1 = 8-bit) + // X #$10 00010000 Index size (0 = 16-bit, 1 = 8-bit) + // D #$08 00001000 Decimal + // I #$04 00000100 IRQ disable + // Z #$02 00000010 Zero + // C #$01 00000001 Carry + // E 6502 emulation mode + // B #$10 00010000 Break (emulation mode only) + + // Setting flags in the status register + int GetAccumulatorSize() const { return status & 0x20; } + int GetIndexSize() const { return status & 0x10; } + void SetAccumulatorSize(bool set) { SetFlag(0x20, set); } + void SetIndexSize(bool set) { SetFlag(0x10, set); } + + // Set individual flags + void SetNegativeFlag(bool set) { SetFlag(0x80, set); } + void SetOverflowFlag(bool set) { SetFlag(0x40, set); } + void SetBreakFlag(bool set) { SetFlag(0x10, set); } + void SetDecimalFlag(bool set) { SetFlag(0x08, set); } + void SetInterruptFlag(bool set) { SetFlag(0x04, set); } + void SetZeroFlag(bool set) { SetFlag(0x02, set); } + void SetCarryFlag(bool set) { SetFlag(0x01, set); } + + // Get individual flags + bool GetNegativeFlag() const { return GetFlag(0x80); } + bool GetOverflowFlag() const { return GetFlag(0x40); } + bool GetBreakFlag() const { return GetFlag(0x10); } + bool GetDecimalFlag() const { return GetFlag(0x08); } + bool GetInterruptFlag() const { return GetFlag(0x04); } + bool GetZeroFlag() const { return GetFlag(0x02); } + bool GetCarryFlag() const { return GetFlag(0x01); } + // ========================================================================== // Addressing Modes @@ -324,56 +341,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { return memory.ReadWord(SP() + sr + Y); } - // ========================================================================== - // Registers - - uint16_t A = 0; // Accumulator - uint16_t X = 0; // X index register - uint16_t Y = 0; // Y index register - uint16_t D = 0; // Direct Page register - uint8_t DB = 0; // Data Bank register - uint8_t PB = 0; // Program Bank register - uint16_t PC = 0; // Program Counter - uint8_t E = 1; // Emulation mode flag - uint8_t status = 0b00110000; // Processor Status (P) - - // Mnemonic Value Binary Description - // N #$80 10000000 Negative - // V #$40 01000000 Overflow - // M #$20 00100000 Accumulator size (0 = 16-bit, 1 = 8-bit) - // X #$10 00010000 Index size (0 = 16-bit, 1 = 8-bit) - // D #$08 00001000 Decimal - // I #$04 00000100 IRQ disable - // Z #$02 00000010 Zero - // C #$01 00000001 Carry - // E 6502 emulation mode - // B #$10 00010000 Break (emulation mode only) - - // Setting flags in the status register - int GetAccumulatorSize() const { return status & 0x20; } - int GetIndexSize() const { return status & 0x10; } - void SetAccumulatorSize(bool set) { SetFlag(0x20, set); } - void SetIndexSize(bool set) { SetFlag(0x10, set); } - - // Set individual flags - void SetNegativeFlag(bool set) { SetFlag(0x80, set); } - void SetOverflowFlag(bool set) { SetFlag(0x40, set); } - void SetBreakFlag(bool set) { SetFlag(0x10, set); } - void SetDecimalFlag(bool set) { SetFlag(0x08, set); } - void SetInterruptFlag(bool set) { SetFlag(0x04, set); } - void SetZeroFlag(bool set) { SetFlag(0x02, set); } - void SetCarryFlag(bool set) { SetFlag(0x01, set); } - - // Get individual flags - bool GetNegativeFlag() const { return GetFlag(0x80); } - bool GetOverflowFlag() const { return GetFlag(0x40); } - bool GetBreakFlag() const { return GetFlag(0x10); } - bool GetDecimalFlag() const { return GetFlag(0x08); } - bool GetInterruptFlag() const { return GetFlag(0x04); } - bool GetZeroFlag() const { return GetFlag(0x02); } - bool GetCarryFlag() const { return GetFlag(0x01); } - - // ========================================================================== + // ====================================================== // Instructions // ADC: Add with carry