From fd64835d22206213092ee69f6b132639629b5403 Mon Sep 17 00:00:00 2001 From: scawful Date: Mon, 22 Apr 2024 16:59:04 -0400 Subject: [PATCH] Overhaul Cpu, interrupts, cycling, addressing, instructions, etc --- src/app/emu/cpu/cpu.cc | 235 +++++++----------- src/app/emu/cpu/cpu.h | 293 +++++++++++++--------- src/app/emu/cpu/internal/addressing.cc | 34 +-- src/app/emu/cpu/internal/instructions.cc | 299 ++++++++++++++--------- 4 files changed, 469 insertions(+), 392 deletions(-) diff --git a/src/app/emu/cpu/cpu.cc b/src/app/emu/cpu/cpu.cc index 980b5f94..0bddf99a 100644 --- a/src/app/emu/cpu/cpu.cc +++ b/src/app/emu/cpu/cpu.cc @@ -6,10 +6,33 @@ #include #include +#include "app/emu/cpu/internal/opcodes.h" + namespace yaze { namespace app { namespace emu { +void Cpu::Reset(bool hard) { + if (hard) { + A = 0; + X = 0; + Y = 0; + PC = 0; + PB = 0; + D = 0; + DB = 0; + E = 0; + status = 0; + irq_wanted_ = false; + } + + reset_wanted_ = true; + stopped_ = false; + waiting_ = false; + nmi_wanted_ = false; + int_wanted_ = false; +} + void Cpu::Update(UpdateMode mode, int stepCount) { int cycles = (mode == UpdateMode::Run) ? clock.GetCycleCount() : stepCount; @@ -22,17 +45,80 @@ void Cpu::Update(UpdateMode mode, int stepCount) { // Fetch and execute an instruction ExecuteInstruction(ReadByte((PB << 16) + PC)); - // Handle any interrupts, if necessary - HandleInterrupts(); - if (mode == UpdateMode::Step) { break; } } } +void Cpu::RunOpcode() { + if (reset_wanted_) { + reset_wanted_ = false; + // reset: brk/interrupt without writes + auto sp = SP(); + + ReadByte((PB << 16) | PC); + callbacks_.idle(false); + ReadByte(0x100 | (sp-- & 0xff)); + ReadByte(0x100 | (sp-- & 0xff)); + ReadByte(0x100 | (sp-- & 0xff)); + sp = (sp & 0xff) | 0x100; + + SetSP(sp); + + SetInterruptFlag(true); + SetInterruptFlag(true); + SetDecimalFlag(false); + SetFlags(status); // updates x and m flags, clears + // upper half of x and y if needed + PB = 0; + PC = ReadWord(0xfffc); + return; + } + if (stopped_) { + callbacks_.idle(true); + return; + } + if (waiting_) { + if (irq_wanted_ || nmi_wanted_) { + waiting_ = false; + callbacks_.idle(false); + CheckInt(); + callbacks_.idle(false); + return; + } else { + callbacks_.idle(true); + return; + } + } + // not stopped or waiting, execute a opcode or go to interrupt + if (int_wanted_) { + ReadByte((PB << 16) | PC); + DoInterrupt(); + } else { + // uint8_t opcode = ReadOpcode(); + ExecuteInstruction(ReadByte((PB << 16) | PC)); + } +} + +void Cpu::DoInterrupt() { + callbacks_.idle(false); + PushByte(status); + PushWord(PC); + PushByte(status); + SetInterruptFlag(true); + SetDecimalFlag(false); + PB = 0; + int_wanted_ = false; + if (nmi_wanted_) { + nmi_wanted_ = false; + PC = ReadWord(0xffea); + } else { // irq + PC = ReadWord(0xffee); + } +} + void Cpu::ExecuteInstruction(uint8_t opcode) { - uint8_t cycles = 0; uint8_t instruction_length = 0; uint32_t operand = 0; bool immediate = false; @@ -41,49 +127,30 @@ void Cpu::ExecuteInstruction(uint8_t opcode) { 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); @@ -91,90 +158,60 @@ void Cpu::ExecuteInstruction(uint8_t opcode) { } 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; @@ -390,29 +427,6 @@ void Cpu::ExecuteInstruction(uint8_t opcode) { 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; } @@ -1470,7 +1484,9 @@ void Cpu::ExecuteInstruction(uint8_t opcode) { break; } - LogInstructions(PC, opcode, operand, immediate, accumulator_mode); + if (log_instructions_) { + LogInstructions(PC, opcode, operand, immediate, accumulator_mode); + } instruction_length = GetInstructionLength(opcode); UpdatePC(instruction_length); } @@ -1927,71 +1943,6 @@ uint8_t Cpu::GetInstructionLength(uint8_t opcode) { } } -// TODO: Implement 65816 interrupts. -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 \ No newline at end of file diff --git a/src/app/emu/cpu/cpu.h b/src/app/emu/cpu/cpu.h index d5af069e..cb566516 100644 --- a/src/app/emu/cpu/cpu.h +++ b/src/app/emu/cpu/cpu.h @@ -38,44 +38,30 @@ class InstructionEntry { const int kCpuClockSpeed = 21477272; // 21.477272 MHz -class Cpu : public memory::Memory, - public Loggable, - public core::ExperimentFlags { +class Cpu : public Loggable, public core::ExperimentFlags { public: - explicit Cpu(Memory& mem, Clock& vclock) : memory(mem), clock(vclock) {} + explicit Cpu(memory::Memory& mem, Clock& vclock, + memory::CpuCallbacks& callbacks) + : memory(mem), clock(vclock), callbacks_(callbacks) {} enum class UpdateMode { Run, Step, Pause }; void Init(bool verbose = false) { clock.SetFrequency(kCpuClockSpeed); } - + void Reset(bool hard = false); void Update(UpdateMode mode = UpdateMode::Run, int stepCount = 1); + void RunOpcode(); + void ExecuteInstruction(uint8_t opcode); void LogInstructions(uint16_t PC, uint8_t opcode, uint16_t operand, bool immediate, bool accumulator_mode); void UpdatePC(uint8_t instruction_length) { PC += instruction_length; } - - uint8_t GetInstructionLength(uint8_t opcode); - uint16_t SP() const override { return memory.SP(); } - void SetSP(uint16_t value) override { memory.SetSP(value); } - void set_next_pc(uint16_t value) { next_pc_ = value; } void UpdateClock(int delta_time) { clock.UpdateClock(delta_time); } - 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_; } + void SetIrq(bool state) { irq_wanted_ = state; } + void Nmi() { nmi_wanted_ = true; } + + uint8_t GetInstructionLength(uint8_t opcode); std::vector breakpoints_; std::vector instruction_log_; @@ -90,7 +76,7 @@ class Cpu : public memory::Memory, // 0xFFF8,F9 - ABORT 0xFFE8,E9 - ABORT // 0xFFE6,E7 - BRK // 0xFFF4,F5 - COP 0xFFE4,E5 - COP - void HandleInterrupts(); + void DoInterrupt(); // ====================================================== // Registers @@ -117,18 +103,21 @@ class Cpu : public memory::Memory, // E 6502 emulation mode // B #$10 00010000 Break (emulation mode only) + void SetFlags(uint8_t val) { + status = val; + if (E) { + status |= 0x10; + } + if (status & 0x20) { + X &= 0xff; + Y &= 0xff; + } + } + // Setting flags in the status register bool m() { return GetAccumulatorSize() ? 1 : 0; } int GetAccumulatorSize() const { return status & 0x20; } int GetIndexSize() const { return status & 0x10; } - void set_16_bit_mode() { - SetAccumulatorSize(true); - SetIndexSize(true); - } - void set_8_bit_mode() { - SetAccumulatorSize(false); - SetIndexSize(false); - } void SetAccumulatorSize(bool set) { SetFlag(0x20, set); } void SetIndexSize(bool set) { SetFlag(0x10, set); } @@ -152,9 +141,120 @@ class Cpu : public memory::Memory, enum class AccessType { Control, Data }; + // Memory access routines + uint8_t ReadByte(uint32_t address) const { + return callbacks_.read_byte(address); + } + uint16_t ReadWord(uint32_t address) const { + uint8_t value = ReadByte(address); + uint8_t value2 = ReadByte(address + 1); + return value | (value2 << 8); + } + uint32_t ReadWordLong(uint32_t address) const { + uint8_t value = ReadByte(address); + uint8_t value2 = ReadByte(address + 1); + uint8_t value3 = ReadByte(address + 2); + return value | (value2 << 8) | (value3 << 16); + } + + void WriteByte(uint32_t address, uint8_t value) { + callbacks_.write_byte(address, value); + } + + void WriteWord(uint32_t address, uint16_t value) { + WriteByte(address, value & 0xFF); + WriteByte(address + 1, value >> 8); + } + void WriteLong(uint32_t address, uint32_t value) { + WriteByte(address, value & 0xFF); + WriteByte(address + 1, (value >> 8) & 0xFF); + WriteByte(address + 2, value >> 16); + } + + uint8_t FetchByte() { + uint32_t address = (PB << 16) | PC + 1; + uint8_t byte = ReadByte(address); + return byte; + } + + uint16_t FetchWord() { + uint32_t address = (PB << 16) | PC + 1; + uint16_t value = ReadWord(address); + return value; + } + + uint32_t FetchLong() { + uint32_t value = ReadWordLong((PB << 16) | PC + 1); + return value; + } + + int8_t FetchSignedByte() { return static_cast(FetchByte()); } + + int16_t FetchSignedWord() { + auto offset = static_cast(FetchWord()); + return offset; + } + + uint8_t FetchByteDirectPage(uint8_t operand) { + uint16_t distance = D * 0x100; + + // Calculate the effective address in the Direct Page + uint16_t effectiveAddress = operand + distance; + + // Fetch the byte from memory + uint8_t fetchedByte = ReadByte(effectiveAddress); + + next_pc_ = PC + 1; + + return fetchedByte; + } + + uint16_t ReadByteOrWord(uint32_t address) { + if (GetAccumulatorSize()) { + // 8-bit mode + return ReadByte(address) & 0xFF; + } else { + // 16-bit mode + return ReadWord(address); + } + } + + void PushByte(uint8_t value) { + WriteByte(SP(), value); + SetSP(SP() - 1); + if (E) SetSP((SP() & 0xff) | 0x100); + } + void PushWord(uint16_t value, bool int_check = false) { + PushByte(value >> 8); + if (int_check) CheckInt(); + PushByte(value & 0xFF); + } + void PushLong(uint32_t value) { // Push 24-bit value + PushByte(value >> 16); + PushWord(value & 0xFFFF); + } + + uint8_t PopByte() { + SetSP(SP() + 1); + if (E) SetSP((SP() & 0xff) | 0x100); + return ReadByte(SP()); + } + uint16_t PopWord(bool int_check = false) { + uint8_t low = PopByte(); + if (int_check) CheckInt(); + return low | (PopByte() << 8); + } + uint32_t PopLong() { // Pop 24-bit value + uint32_t low = PopWord(); + uint32_t high = PopByte(); + return (high << 16) | low; + } + // ========================================================================== // Addressing Modes + void AdrImp(); + // Effective Address: // Bank: Data Bank Register if locating data // Program Bank Register if transferring control @@ -321,88 +421,6 @@ class Cpu : public memory::Memory, // LDA (sr, S), Y uint32_t StackRelativeIndirectIndexedY(); - // Memory access routines - uint8_t ReadByte(uint32_t address) const override { - return memory.ReadByte(address); - } - uint16_t ReadWord(uint32_t address) const override { - return memory.ReadWord(address); - } - uint32_t ReadWordLong(uint32_t address) const override { - return memory.ReadWordLong(address); - } - - std::vector ReadByteVector(uint32_t address, - uint16_t size) const override { - return memory.ReadByteVector(address, size); - } - - void WriteByte(uint32_t address, uint8_t value) override { - memory.WriteByte(address, value); - } - - void WriteWord(uint32_t address, uint16_t value) override { - memory.WriteWord(address, value); - } - void WriteLong(uint32_t address, uint32_t value) override { - memory.WriteLong(address, value); - } - - uint8_t FetchByte() { - uint32_t address = (PB << 16) | PC + 1; - uint8_t byte = memory.ReadByte(address); - return byte; - } - - uint16_t FetchWord() { - uint32_t address = (PB << 16) | PC + 1; - uint16_t value = memory.ReadWord(address); - return value; - } - - uint32_t FetchLong() { - uint32_t value = memory.ReadWordLong((PB << 16) | PC + 1); - return value; - } - - int8_t FetchSignedByte() { return static_cast(FetchByte()); } - - int16_t FetchSignedWord() { - auto offset = static_cast(FetchWord()); - return offset; - } - - uint8_t FetchByteDirectPage(uint8_t operand) { - uint16_t distance = D * 0x100; - - // Calculate the effective address in the Direct Page - uint16_t effectiveAddress = operand + distance; - - // Fetch the byte from memory - uint8_t fetchedByte = memory.ReadByte(effectiveAddress); - - next_pc_ = PC + 1; - - return fetchedByte; - } - - uint16_t ReadByteOrWord(uint32_t address) { - if (GetAccumulatorSize()) { - // 8-bit mode - return memory.ReadByte(address) & 0xFF; - } else { - // 16-bit mode - return memory.ReadWord(address); - } - } - - void PushByte(uint8_t value) override { memory.PushByte(value); } - void PushWord(uint16_t value) override { memory.PushWord(value); } - uint8_t PopByte() override { return memory.PopByte(); } - uint16_t PopWord() override { return memory.PopWord(); } - void PushLong(uint32_t value) override { memory.PushLong(value); } - uint32_t PopLong() override { return memory.PopLong(); } - // ====================================================== // Instructions @@ -684,6 +702,33 @@ class Cpu : public memory::Memory, // XCE: Exchange carry and emulation bits void XCE(); + // ========================================================================== + uint16_t SP() const { return memory.SP(); } + void SetSP(uint16_t value) { memory.SetSP(value); } + void set_next_pc(uint16_t value) { next_pc_ = value; } + + 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_; } + + void CheckInt() { + int_wanted_ = nmi_wanted_ || (irq_wanted_ && !GetInterruptFlag()); + } + + auto mutable_log_instructions() -> bool* { return &log_instructions_; } + private: void compare(uint16_t register_value, uint16_t memory_value) { uint16_t result; @@ -711,14 +756,22 @@ class Cpu : public memory::Memory, } bool GetFlag(uint8_t mask) const { return (status & mask) != 0; } - void ClearMemory() override { memory.ClearMemory(); } - uint8_t operator[](int i) const override { return 0; } - uint8_t at(int i) const override { return 0; } + + bool log_instructions_ = false; + + bool waiting_ = false; + bool stopped_ = false; + + bool irq_wanted_ = false; + bool nmi_wanted_ = false; + bool reset_wanted_ = false; + bool int_wanted_ = false; uint16_t last_call_frame_; uint16_t next_pc_; - Memory& memory; + memory::CpuCallbacks callbacks_; + memory::Memory& memory; Clock& clock; }; diff --git a/src/app/emu/cpu/internal/addressing.cc b/src/app/emu/cpu/internal/addressing.cc index cbd76493..9985316a 100644 --- a/src/app/emu/cpu/internal/addressing.cc +++ b/src/app/emu/cpu/internal/addressing.cc @@ -12,30 +12,31 @@ uint32_t Cpu::Absolute(Cpu::AccessType access_type) { } uint32_t Cpu::AbsoluteIndexedX() { - uint16_t address = memory.ReadWord((PB << 16) | (PC + 1)); + uint16_t address = ReadWord((PB << 16) | (PC + 1)); uint32_t effective_address = (DB << 16) | ((address + X) & 0xFFFF); return effective_address; } uint32_t Cpu::AbsoluteIndexedY() { - uint16_t address = memory.ReadWord((PB << 16) | (PC + 1)); + uint16_t address = ReadWord((PB << 16) | (PC + 1)); uint32_t effective_address = (DB << 16) | address + Y; return effective_address; } uint16_t Cpu::AbsoluteIndexedIndirect() { uint16_t address = FetchWord() + X; - return memory.ReadWord((DB << 16) | address & 0xFFFF); + callbacks_.idle(false); + return ReadWord((DB << 16) | address & 0xFFFF); } uint16_t Cpu::AbsoluteIndirect() { uint16_t address = FetchWord(); - return memory.ReadWord((PB << 16) | address); + return ReadWord((PB << 16) | address); } uint32_t Cpu::AbsoluteIndirectLong() { uint16_t address = FetchWord(); - return memory.ReadWordLong((PB << 16) | address); + return ReadWordLong((PB << 16) | address); } uint32_t Cpu::AbsoluteLong() { return FetchLong(); } @@ -44,7 +45,7 @@ uint32_t Cpu::AbsoluteLongIndexedX() { return FetchLong() + X; } void Cpu::BlockMove(uint16_t source, uint16_t dest, uint16_t length) { for (int i = 0; i < length; i++) { - memory.WriteByte(dest + i, memory.ReadByte(source + i)); + WriteByte(dest + i, ReadByte(source + i)); } } @@ -66,44 +67,47 @@ uint16_t Cpu::DirectPageIndexedY() { uint16_t Cpu::DirectPageIndexedIndirectX() { uint8_t operand = FetchByte(); + if (D & 0xFF) { + callbacks_.idle(false); // dpr not 0: 1 extra cycle + } + callbacks_.idle(false); uint16_t indirect_address = D + operand + X; - uint16_t effective_address = memory.ReadWord(indirect_address & 0xFFFF); + uint16_t effective_address = ReadWord(indirect_address & 0xFFFF); return effective_address; } uint16_t Cpu::DirectPageIndirect() { uint8_t dp = FetchByte(); uint16_t effective_address = D + dp; - return memory.ReadWord(effective_address); + return ReadWord(effective_address); } uint32_t Cpu::DirectPageIndirectLong() { uint8_t dp = FetchByte(); uint16_t effective_address = D + dp; - return memory.ReadWordLong((0x00 << 0x10) | effective_address); + return ReadWordLong((0x00 << 0x10) | effective_address); } uint16_t Cpu::DirectPageIndirectIndexedY() { uint8_t operand = FetchByte(); uint16_t indirect_address = D + operand; - return memory.ReadWord(indirect_address) + Y; + return ReadWord(indirect_address) + Y; } uint32_t Cpu::DirectPageIndirectLongIndexedY() { uint8_t operand = FetchByte(); uint16_t indirect_address = D + operand; uint16_t y_by_mode = GetAccumulatorSize() ? Y : Y & 0xFF; - uint32_t effective_address = - memory.ReadWordLong(indirect_address) + y_by_mode; + uint32_t effective_address = ReadWordLong(indirect_address) + y_by_mode; return effective_address; } uint16_t Cpu::Immediate(bool index_size) { bool bit_mode = index_size ? GetIndexSize() : GetAccumulatorSize(); if (bit_mode) { - return memory.ReadByte((PB << 16) | PC + 1); + return ReadByte((PB << 16) | PC + 1); } else { - return memory.ReadWord((PB << 16) | PC + 1); + return ReadWord((PB << 16) | PC + 1); } } @@ -115,7 +119,7 @@ uint16_t Cpu::StackRelative() { uint32_t Cpu::StackRelativeIndirectIndexedY() { uint8_t sr = FetchByte(); - return (DB << 0x10) | (memory.ReadWord(SP() + sr) + Y); + return (DB << 0x10) | (ReadWord(SP() + sr) + Y); } } // namespace emu diff --git a/src/app/emu/cpu/internal/instructions.cc b/src/app/emu/cpu/internal/instructions.cc index 2eafcf63..ccd116ed 100644 --- a/src/app/emu/cpu/internal/instructions.cc +++ b/src/app/emu/cpu/internal/instructions.cc @@ -50,12 +50,12 @@ void Cpu::ADC(uint16_t operand) { void Cpu::AND(uint32_t value, bool isImmediate) { uint16_t operand; if (GetAccumulatorSize()) { // 8-bit mode - operand = isImmediate ? value : memory.ReadByte(value); + operand = isImmediate ? value : ReadByte(value); A &= operand; SetZeroFlag(A == 0); SetNegativeFlag(A & 0x80); } else { // 16-bit mode - operand = isImmediate ? value : memory.ReadWord(value); + operand = isImmediate ? value : ReadWord(value); A &= operand; SetZeroFlag(A == 0); SetNegativeFlag(A & 0x8000); @@ -64,18 +64,18 @@ void Cpu::AND(uint32_t value, bool isImmediate) { // New function for absolute long addressing mode void Cpu::ANDAbsoluteLong(uint32_t address) { - uint32_t operand32 = memory.ReadWordLong(address); + uint32_t operand32 = ReadWordLong(address); A &= operand32; SetZeroFlag(A == 0); SetNegativeFlag(A & 0x8000); } void Cpu::ASL(uint16_t address) { - uint8_t value = memory.ReadByte(address); + uint8_t value = ReadByte(address); SetCarryFlag(!(value & 0x80)); // Set carry flag if bit 7 is set value <<= 1; // Shift left value &= 0xFE; // Clear bit 0 - memory.WriteByte(address, value); + WriteByte(address, value); SetNegativeFlag(!value); SetZeroFlag(value); } @@ -99,7 +99,7 @@ void Cpu::BEQ(int8_t offset) { } void Cpu::BIT(uint16_t address) { - uint8_t value = memory.ReadByte(address); + uint8_t value = ReadByte(address); SetNegativeFlag(value & 0x80); SetOverflowFlag(value & 0x40); SetZeroFlag((A & value) == 0); @@ -127,15 +127,16 @@ void Cpu::BPL(int8_t offset) { void Cpu::BRA(int8_t offset) { next_pc_ = offset; } void Cpu::BRK() { - next_pc_ = PC + 2; // Increment the program counter by 2 - memory.PushWord(next_pc_); - memory.PushByte(status); + // ReadOpcode(); + next_pc_ += 2; // Increment the program counter by 2 + ReadByte(PC); // Read the next byte + PushByte(PB); + PushByte(PC); // ,false + PushByte(status); SetInterruptFlag(true); - try { - next_pc_ = memory.ReadWord(0xFFFE); - } catch (const std::exception& e) { - std::cout << "BRK: " << e.what() << std::endl; - } + SetDecimalFlag(false); + PB = 0; + PC = ReadWord(0xFFE6); // ,true } void Cpu::BRL(int16_t offset) { next_pc_ = offset; } @@ -169,7 +170,7 @@ void Cpu::CMP(uint32_t value, bool isImmediate) { if (isImmediate) { result = A - (value & 0xFF); } else { - uint8_t memory_value = memory.ReadByte(value); + uint8_t memory_value = ReadByte(value); result = A - memory_value; } SetZeroFlag(result == 0); @@ -180,7 +181,7 @@ void Cpu::CMP(uint32_t value, bool isImmediate) { if (isImmediate) { result = A - (value & 0xFFFF); } else { - uint16_t memory_value = memory.ReadWord(value); + uint16_t memory_value = ReadWord(value); result = A - memory_value; } SetZeroFlag(result == 0); @@ -191,33 +192,33 @@ void Cpu::CMP(uint32_t value, bool isImmediate) { void Cpu::COP() { next_pc_ += 2; // Increment the program counter by 2 - memory.PushWord(next_pc_); - memory.PushByte(status); + PushWord(next_pc_); + PushByte(status); SetInterruptFlag(true); if (E) { - next_pc_ = memory.ReadWord(0xFFF4); + next_pc_ = ReadWord(0xFFF4); } else { - next_pc_ = memory.ReadWord(0xFFE4); + next_pc_ = ReadWord(0xFFE4); } SetDecimalFlag(false); } void Cpu::CPX(uint32_t value, bool isImmediate) { if (GetIndexSize()) { // 8-bit - uint8_t memory_value = isImmediate ? value : memory.ReadByte(value); + uint8_t memory_value = isImmediate ? value : ReadByte(value); compare(X, memory_value); } else { // 16-bit - uint16_t memory_value = isImmediate ? value : memory.ReadWord(value); + uint16_t memory_value = isImmediate ? value : ReadWord(value); compare(X, memory_value); } } void Cpu::CPY(uint32_t value, bool isImmediate) { if (GetIndexSize()) { // 8-bit - uint8_t memory_value = isImmediate ? value : memory.ReadByte(value); + uint8_t memory_value = isImmediate ? value : ReadByte(value); compare(Y, memory_value); } else { // 16-bit - uint16_t memory_value = isImmediate ? value : memory.ReadWord(value); + uint16_t memory_value = isImmediate ? value : ReadWord(value); compare(Y, memory_value); } } @@ -237,15 +238,15 @@ void Cpu::DEC(uint32_t address, bool accumulator) { } if (GetAccumulatorSize()) { - uint8_t value = memory.ReadByte(address); + uint8_t value = ReadByte(address); value--; - memory.WriteByte(address, value); + WriteByte(address, value); SetZeroFlag(value == 0); SetNegativeFlag(value & 0x80); } else { - uint16_t value = memory.ReadWord(address); + uint16_t value = ReadWord(address); value--; - memory.WriteWord(address, value); + WriteWord(address, value); SetZeroFlag(value == 0); SetNegativeFlag(value & 0x8000); } @@ -277,11 +278,11 @@ void Cpu::DEY() { void Cpu::EOR(uint32_t address, bool isImmediate) { if (GetAccumulatorSize()) { - A ^= isImmediate ? address : memory.ReadByte(address); + A ^= isImmediate ? address : ReadByte(address); SetZeroFlag(A == 0); SetNegativeFlag(A & 0x80); } else { - A ^= isImmediate ? address : memory.ReadWord(address); + A ^= isImmediate ? address : ReadWord(address); SetZeroFlag(A == 0); SetNegativeFlag(A & 0x8000); } @@ -302,15 +303,15 @@ void Cpu::INC(uint32_t address, bool accumulator) { } if (GetAccumulatorSize()) { - uint8_t value = memory.ReadByte(address); + uint8_t value = ReadByte(address); value++; - memory.WriteByte(address, value); + WriteByte(address, value); SetNegativeFlag(value & 0x80); SetZeroFlag(value == 0); } else { - uint16_t value = memory.ReadWord(address); + uint16_t value = ReadWord(address); value++; - memory.WriteWord(address, value); + WriteWord(address, value); SetNegativeFlag(value & 0x8000); SetZeroFlag(value == 0); } @@ -351,27 +352,28 @@ void Cpu::JML(uint32_t address) { } void Cpu::JSR(uint16_t address) { - memory.PushWord(PC); // Push the program counter onto the stack - next_pc_ = address; // Set program counter to the new address + PushWord(PC); // Push the program counter onto the stack + next_pc_ = address; // Set program counter to the new address } void Cpu::JSL(uint32_t address) { - memory.PushLong(PC); // Push the program counter onto the stack as a long - // value (24 bits) - next_pc_ = address; // Set program counter to the new address + PushLong(PC); // Push the program counter onto the stack as a long + // value (24 bits) + next_pc_ = address; // Set program counter to the new address } -void Cpu::LDA(uint16_t address, bool isImmediate, bool direct_page, bool data_bank) { +void Cpu::LDA(uint16_t address, bool isImmediate, bool direct_page, + bool data_bank) { uint8_t bank = PB; if (direct_page) { bank = 0; } if (GetAccumulatorSize()) { - A = isImmediate ? address : memory.ReadByte((bank << 16) | address); + A = isImmediate ? address : ReadByte((bank << 16) | address); SetZeroFlag(A == 0); SetNegativeFlag(A & 0x80); } else { - A = isImmediate ? address : memory.ReadWord((bank << 16) | address); + A = isImmediate ? address : ReadWord((bank << 16) | address); SetZeroFlag(A == 0); SetNegativeFlag(A & 0x8000); } @@ -379,11 +381,11 @@ void Cpu::LDA(uint16_t address, bool isImmediate, bool direct_page, bool data_ba void Cpu::LDX(uint16_t address, bool isImmediate) { if (GetIndexSize()) { - X = isImmediate ? address : memory.ReadByte(address); + X = isImmediate ? address : ReadByte(address); SetZeroFlag(X == 0); SetNegativeFlag(X & 0x80); } else { - X = isImmediate ? address : memory.ReadWord(address); + X = isImmediate ? address : ReadWord(address); SetZeroFlag(X == 0); SetNegativeFlag(X & 0x8000); } @@ -391,11 +393,11 @@ void Cpu::LDX(uint16_t address, bool isImmediate) { void Cpu::LDY(uint16_t address, bool isImmediate) { if (GetIndexSize()) { - Y = isImmediate ? address : memory.ReadByte(address); + Y = isImmediate ? address : ReadByte(address); SetZeroFlag(Y == 0); SetNegativeFlag(Y & 0x80); } else { - Y = isImmediate ? address : memory.ReadWord(address); + Y = isImmediate ? address : ReadWord(address); SetZeroFlag(Y == 0); SetNegativeFlag(Y & 0x8000); } @@ -416,17 +418,17 @@ void Cpu::LSR(uint16_t address, bool accumulator) { } return; } - uint8_t value = memory.ReadByte(address); + uint8_t value = ReadByte(address); SetCarryFlag(value & 0x01); value >>= 1; - memory.WriteByte(address, value); + WriteByte(address, value); SetNegativeFlag(false); SetZeroFlag(value == 0); } void Cpu::MVN(uint16_t source, uint16_t dest, uint16_t length) { for (uint16_t i = 0; i < length; i++) { - memory.WriteByte(dest, memory.ReadByte(source)); + WriteByte(dest, ReadByte(source)); source++; dest++; } @@ -434,23 +436,33 @@ void Cpu::MVN(uint16_t source, uint16_t dest, uint16_t length) { void Cpu::MVP(uint16_t source, uint16_t dest, uint16_t length) { for (uint16_t i = 0; i < length; i++) { - memory.WriteByte(dest, memory.ReadByte(source)); + WriteByte(dest, ReadByte(source)); source--; dest--; } } -void Cpu::NOP() { - // Do nothing -} +void Cpu::NOP() { AdrImp(); } + +// void cpu_ora(uint32_t low, uint32_t high) { +// if (cpu->mf) { +// CheckInt(); +// uint8_t value = cpu_read(cpu, low); +// cpu->a = (cpu->a & 0xff00) | ((cpu->a | value) & 0xff); +// } else { +// uint16_t value = cpu_readWord(cpu, low, high, true); +// cpu->a |= value; +// } +// cpu_setZN(cpu, cpu->a, cpu->mf); +// } void Cpu::ORA(uint16_t address, bool isImmediate) { if (GetAccumulatorSize()) { - A |= isImmediate ? address : memory.ReadByte(address); + A |= isImmediate ? address : ReadByte(address); SetZeroFlag(A == 0); SetNegativeFlag(A & 0x80); } else { - A |= isImmediate ? address : memory.ReadWord(address); + A |= isImmediate ? address : ReadWord(address); SetZeroFlag(A == 0); SetNegativeFlag(A & 0x8000); } @@ -458,84 +470,122 @@ void Cpu::ORA(uint16_t address, bool isImmediate) { void Cpu::PEA() { uint16_t address = FetchWord(); - memory.PushWord(address); + PushWord(address); } void Cpu::PEI() { uint16_t address = FetchWord(); - memory.PushWord(memory.ReadWord(address)); + PushWord(ReadWord(address)); } void Cpu::PER() { uint16_t address = FetchWord(); - memory.PushWord(PC + address); + callbacks_.idle(false); + PushWord(PC + address); } void Cpu::PHA() { + callbacks_.idle(false); if (GetAccumulatorSize()) { - memory.PushByte(static_cast(A)); + CheckInt(); + PushByte(static_cast(A)); } else { - memory.PushWord(A); + PushWord(A); } } -void Cpu::PHB() { memory.PushByte(DB); } +void Cpu::PHB() { + callbacks_.idle(false); + CheckInt(); + PushByte(DB); +} -void Cpu::PHD() { memory.PushWord(D); } +void Cpu::PHD() { + callbacks_.idle(false); + PushWord(D); +} -void Cpu::PHK() { memory.PushByte(PB); } +void Cpu::PHK() { + callbacks_.idle(false); + CheckInt(); + PushByte(PB); +} -void Cpu::PHP() { memory.PushByte(status); } +void Cpu::PHP() { + callbacks_.idle(false); + CheckInt(); + PushByte(status); +} void Cpu::PHX() { + callbacks_.idle(false); if (GetIndexSize()) { - memory.PushByte(static_cast(X)); + CheckInt(); + PushByte(static_cast(X)); } else { - memory.PushWord(X); + PushWord(X); } } void Cpu::PHY() { + callbacks_.idle(false); if (GetIndexSize()) { - memory.PushByte(static_cast(Y)); + CheckInt(); + PushByte(static_cast(Y)); } else { - memory.PushWord(Y); + PushWord(Y); } } void Cpu::PLA() { + callbacks_.idle(false); + callbacks_.idle(false); if (GetAccumulatorSize()) { - A = memory.PopByte(); + CheckInt(); + A = PopByte(); SetNegativeFlag((A & 0x80) != 0); } else { - A = memory.PopWord(); + A = PopWord(); SetNegativeFlag((A & 0x8000) != 0); } SetZeroFlag(A == 0); } void Cpu::PLB() { - DB = memory.PopByte(); + callbacks_.idle(false); + callbacks_.idle(false); + CheckInt(); + DB = PopByte(); SetNegativeFlag((DB & 0x80) != 0); SetZeroFlag(DB == 0); } // Pull Direct Page Register from Stack void Cpu::PLD() { - D = memory.PopWord(); + callbacks_.idle(false); + callbacks_.idle(false); + D = PopWord(); SetNegativeFlag((D & 0x8000) != 0); SetZeroFlag(D == 0); } // Pull Processor Status Register from Stack -void Cpu::PLP() { status = memory.PopByte(); } +void Cpu::PLP() { + callbacks_.idle(false); + callbacks_.idle(false); + CheckInt(); + status = PopByte(); +} void Cpu::PLX() { + callbacks_.idle(false); + callbacks_.idle(false); if (GetIndexSize()) { - X = memory.PopByte(); + CheckInt(); + X = PopByte(); SetNegativeFlag((A & 0x80) != 0); } else { - X = memory.PopWord(); + X = PopWord(); SetNegativeFlag((A & 0x8000) != 0); } @@ -543,11 +593,14 @@ void Cpu::PLX() { } void Cpu::PLY() { + callbacks_.idle(false); + callbacks_.idle(false); if (GetIndexSize()) { - Y = memory.PopByte(); + CheckInt(); + Y = PopByte(); SetNegativeFlag((A & 0x80) != 0); } else { - Y = memory.PopWord(); + Y = PopWord(); SetNegativeFlag((A & 0x8000) != 0); } SetZeroFlag(Y == 0); @@ -555,7 +608,9 @@ void Cpu::PLY() { void Cpu::REP() { auto byte = FetchByte(); + CheckInt(); status &= ~byte; + callbacks_.idle(false); } void Cpu::ROL(uint32_t address, bool accumulator) { @@ -578,12 +633,12 @@ void Cpu::ROL(uint32_t address, bool accumulator) { return; } - uint8_t value = memory.ReadByte(address); + uint8_t value = ReadByte(address); uint8_t carry = GetCarryFlag() ? 0x01 : 0x00; SetCarryFlag(value & 0x80); value <<= 1; value |= carry; - memory.WriteByte(address, value); + WriteByte(address, value); SetNegativeFlag(value & 0x80); SetZeroFlag(value == 0); } @@ -608,34 +663,32 @@ void Cpu::ROR(uint32_t address, bool accumulator) { return; } - uint8_t value = memory.ReadByte(address); + uint8_t value = ReadByte(address); uint8_t carry = GetCarryFlag() ? 0x80 : 0x00; SetCarryFlag(value & 0x01); value >>= 1; value |= carry; - memory.WriteByte(address, value); + WriteByte(address, value); SetNegativeFlag(value & 0x80); SetZeroFlag(value == 0); } void Cpu::RTI() { - status = memory.PopByte(); - PC = memory.PopWord(); + status = PopByte(); + PC = PopWord(); } void Cpu::RTL() { - next_pc_ = memory.PopWord(); - PB = memory.PopByte(); + next_pc_ = PopWord(); + PB = PopByte(); } -void Cpu::RTS() { - last_call_frame_ = memory.PopWord(); -} +void Cpu::RTS() { last_call_frame_ = PopWord(); } void Cpu::SBC(uint32_t value, bool isImmediate) { uint16_t operand; if (!GetAccumulatorSize()) { // 16-bit mode - operand = isImmediate ? value : memory.ReadWord(value); + operand = isImmediate ? value : ReadWord(value); uint16_t result = A - operand - (GetCarryFlag() ? 0 : 1); SetCarryFlag(!(result > 0xFFFF)); // Update the carry flag @@ -649,7 +702,7 @@ void Cpu::SBC(uint32_t value, bool isImmediate) { SetZeroFlag(A == 0); SetNegativeFlag(A & 0x8000); } else { // 8-bit mode - operand = isImmediate ? value : memory.ReadByte(value); + operand = isImmediate ? value : ReadByte(value); uint8_t result = A - operand - (GetCarryFlag() ? 0 : 1); SetCarryFlag(!(result > 0xFF)); // Update the carry flag @@ -673,45 +726,46 @@ void Cpu::SEI() { status |= 0x04; } void Cpu::SEP() { auto byte = FetchByte(); + CheckInt(); status |= byte; + callbacks_.idle(false); } void Cpu::STA(uint32_t address) { if (GetAccumulatorSize()) { - memory.WriteByte(address, static_cast(A)); + WriteByte(address, static_cast(A)); } else { - memory.WriteWord(address, A); + WriteWord(address, A); } } -// TODO: Make this work with the Clock class of the CPU - void Cpu::STP() { - // During the next phase 2 clock cycle, stop the processors oscillator input - // The processor is effectively shut down until a reset occurs (RES` pin). + stopped_ = true; + callbacks_.idle(false); + callbacks_.idle(false); } void Cpu::STX(uint16_t address) { if (GetIndexSize()) { - memory.WriteByte(address, static_cast(X)); + WriteByte(address, static_cast(X)); } else { - memory.WriteWord(address, X); + WriteWord(address, X); } } void Cpu::STY(uint16_t address) { if (GetIndexSize()) { - memory.WriteByte(address, static_cast(Y)); + WriteByte(address, static_cast(Y)); } else { - memory.WriteWord(address, Y); + WriteWord(address, Y); } } void Cpu::STZ(uint16_t address) { if (GetAccumulatorSize()) { - memory.WriteByte(address, 0x00); + WriteByte(address, 0x00); } else { - memory.WriteWord(address, 0x0000); + WriteWord(address, 0x0000); } } @@ -733,7 +787,7 @@ void Cpu::TCD() { SetNegativeFlag(D & 0x80); } -void Cpu::TCS() { memory.SetSP(A); } +void Cpu::TCS() { SetSP(A); } void Cpu::TDC() { A = D; @@ -742,17 +796,17 @@ void Cpu::TDC() { } void Cpu::TRB(uint16_t address) { - uint8_t value = memory.ReadByte(address); + uint8_t value = ReadByte(address); SetZeroFlag((A & value) == 0); value &= ~A; - memory.WriteByte(address, value); + WriteByte(address, value); } void Cpu::TSB(uint16_t address) { - uint8_t value = memory.ReadByte(address); + uint8_t value = ReadByte(address); SetZeroFlag((A & value) == 0); value |= A; - memory.WriteByte(address, value); + WriteByte(address, value); } void Cpu::TSC() { @@ -762,53 +816,68 @@ void Cpu::TSC() { } void Cpu::TSX() { + AdrImp(); X = SP(); SetZeroFlag(X == 0); SetNegativeFlag(X & 0x80); } void Cpu::TXA() { + AdrImp(); A = X; SetZeroFlag(A == 0); SetNegativeFlag(A & 0x80); } -void Cpu::TXS() { memory.SetSP(X); } +void Cpu::TXS() { + AdrImp(); + SetSP(X); +} void Cpu::TXY() { + AdrImp(); Y = X; SetZeroFlag(X == 0); SetNegativeFlag(X & 0x80); } void Cpu::TYA() { + AdrImp(); A = Y; SetZeroFlag(A == 0); SetNegativeFlag(A & 0x80); } void Cpu::TYX() { - X = Y; + AdrImp(); + if (GetIndexSize()) { + X = Y & 0xFF; + } else { + X = Y; + } SetZeroFlag(Y == 0); SetNegativeFlag(Y & 0x80); } -// TODO: Make this communicate with the SNES class - void Cpu::WAI() { - // Pull the RDY pin low - // Power consumption is reduced(?) - // RDY remains low until an external hardware interupt - // (NMI, IRQ, ABORT, or RESET) is received from the SNES class + waiting_ = true; + callbacks_.idle(false); + callbacks_.idle(false); } void Cpu::XBA() { uint8_t lowByte = A & 0xFF; uint8_t highByte = (A >> 8) & 0xFF; A = (lowByte << 8) | highByte; + SetZeroFlag(A == 0); + SetNegativeFlag(A & 0x80); + callbacks_.idle(false); + CheckInt(); + callbacks_.idle(false); } void Cpu::XCE() { + AdrImp(); uint8_t carry = status & 0x01; status &= ~0x01; status |= E;