diff --git a/Core/Console.cpp b/Core/Console.cpp index 11a74ba..e127fcd 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -32,6 +32,7 @@ void Console::Stop() _runLock.WaitForRelease(); _cpu.reset(); + _ppu.reset(); _memoryManager.reset(); _debugger.reset(); } @@ -42,12 +43,18 @@ void Console::LoadRom(VirtualFile romFile, VirtualFile patchFile) shared_ptr cart = BaseCartridge::CreateCartridge(romFile, patchFile); if(cart) { + _ppu.reset(new Ppu()); _memoryManager.reset(new MemoryManager(cart, shared_from_this())); _cpu.reset(new Cpu(_memoryManager)); - _debugger.reset(new Debugger(_cpu, _memoryManager)); + _debugger.reset(new Debugger(_cpu, _ppu, _memoryManager)); } } +shared_ptr Console::GetPpu() +{ + return _ppu; +} + shared_ptr Console::GetDebugger(bool allowStart) { return _debugger; diff --git a/Core/Console.h b/Core/Console.h index 297b65b..2650943 100644 --- a/Core/Console.h +++ b/Core/Console.h @@ -4,6 +4,7 @@ #include "../Utilities/SimpleLock.h" class Cpu; +class Ppu; class MemoryManager; class Debugger; enum class MemoryOperationType; @@ -12,6 +13,7 @@ class Console : public std::enable_shared_from_this { private: shared_ptr _cpu; + shared_ptr _ppu; shared_ptr _memoryManager; shared_ptr _debugger; @@ -24,6 +26,7 @@ public: void LoadRom(VirtualFile romFile, VirtualFile patchFile); + shared_ptr GetPpu(); shared_ptr GetDebugger(bool allowStart = true); void ProcessCpuRead(uint32_t addr, uint8_t value, MemoryOperationType type); diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index e78f311..7d28bb2 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -60,6 +60,8 @@ + + @@ -68,6 +70,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index deb6e31..2275cae 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -56,6 +56,12 @@ Misc + + SNES + + + SNES + @@ -86,6 +92,9 @@ Misc + + SNES + diff --git a/Core/Cpu.Instructions.cpp b/Core/Cpu.Instructions.cpp new file mode 100644 index 0000000..0b29d7c --- /dev/null +++ b/Core/Cpu.Instructions.cpp @@ -0,0 +1,921 @@ +#include "stdafx.h" +#include "Cpu.h" + +/************************ +Add/substract operations +*************************/ +void Cpu::Add8(uint8_t value) +{ + uint32_t result; + if(CheckFlag(ProcFlags::Decimal)) { + result = (_state.A & 0x0F) + (value & 0x0F) + (_state.PS & ProcFlags::Carry); + if(result > 0x09) result += 0x06; + result = (_state.A & 0xF0) + (value & 0xF0) + (result > 0x0F ? 0x10 : 0) + (result & 0x0F); + } else { + result = (uint32_t)_state.A + value + (_state.PS & ProcFlags::Carry); + } + + if(~(_state.A ^ value) & (_state.A ^ result) & 0x80) { + SetFlags(ProcFlags::Overflow); + } else { + ClearFlags(ProcFlags::Overflow); + } + + if(CheckFlag(ProcFlags::Decimal) && result > 0x9F) { + result += 0x60; + } + + ClearFlags(ProcFlags::Carry | ProcFlags::Negative | ProcFlags::Zero); + SetZeroNegativeFlags((uint8_t)result); + + if(result & 0x100) { + SetFlags(ProcFlags::Carry); + } + + _state.A = (uint8_t)result; +} + +void Cpu::Add16(uint16_t value) +{ + uint32_t result; + if(CheckFlag(ProcFlags::Decimal)) { + result = (_state.A & 0x0F) + (value & 0x0F) + (_state.PS & ProcFlags::Carry); + + if(result > 0x09) result += 0x06; + result = (_state.A & 0xF0) + (value & 0xF0) + (result > 0x0F ? 0x10 : 0) + (result & 0x0F); + + if(result > 0x9F) result += 0x60; + result = (_state.A & 0xF00) + (value & 0xF00) + (result > 0xFF ? 0x100 : 0) + (result & 0xFF); + + if(result > 0x9FF) result += 0x600; + result = (_state.A & 0xF000) + (value & 0xF000) + (result > 0xFFF ? 0x1000 : 0) + (result & 0xFFF); + } else { + result = (uint32_t)_state.A + value + (_state.PS & ProcFlags::Carry); + } + + if(~(_state.A ^ value) & (_state.A ^ result) & 0x8000) { + SetFlags(ProcFlags::Overflow); + } else { + ClearFlags(ProcFlags::Overflow); + } + + if(CheckFlag(ProcFlags::Decimal) && result > 0x9FFF) { + result += 0x6000; + } + + ClearFlags(ProcFlags::Carry | ProcFlags::Negative | ProcFlags::Zero); + SetZeroNegativeFlags((uint16_t)result); + + if(result & 0x10000) { + SetFlags(ProcFlags::Carry); + } + + _state.A = (uint16_t)result; +} + +void Cpu::ADC() +{ + if(CheckFlag(ProcFlags::MemoryMode8)) { + Add8(GetByteValue()); + } else { + Add16(GetWordValue()); + } +} + +void Cpu::Sub8(uint8_t value) +{ + uint32_t result; + if(CheckFlag(ProcFlags::Decimal)) { + result = (_state.A & 0x0F) + (value & 0x0F) + (_state.PS & ProcFlags::Carry); + if(result <= 0x0F) result -= 0x06; + result = (_state.A & 0xF0) + (value & 0xF0) + (result > 0x0F ? 0x10 : 0) + (result & 0x0F); + } else { + result = (uint32_t)_state.A + value + (_state.PS & ProcFlags::Carry); + } + + if(~(_state.A ^ value) & (_state.A ^ result) & 0x80) { + SetFlags(ProcFlags::Overflow); + } else { + ClearFlags(ProcFlags::Overflow); + } + + if(CheckFlag(ProcFlags::Decimal) && result <= 0xFF) { + result -= 0x60; + } + + ClearFlags(ProcFlags::Carry | ProcFlags::Negative | ProcFlags::Zero); + SetZeroNegativeFlags((uint8_t)result); + + if(result & 0x100) { + SetFlags(ProcFlags::Carry); + } + + _state.A = (uint8_t)result; +} + +void Cpu::Sub16(uint16_t value) +{ + uint32_t result; + if(CheckFlag(ProcFlags::Decimal)) { + result = (_state.A & 0x0F) + (value & 0x0F) + (_state.PS & ProcFlags::Carry); + + if(result <= 0x0F) result -= 0x06; + result = (_state.A & 0xF0) + (value & 0xF0) + (result > 0x0F ? 0x10 : 0) + (result & 0x0F); + + if(result <= 0xFF) result -= 0x60; + result = (_state.A & 0xF00) + (value & 0xF00) + (result > 0xFF ? 0x100 : 0) + (result & 0xFF); + + if(result <= 0xFFF) result -= 0x600; + result = (_state.A & 0xF000) + (value & 0xF000) + (result > 0xFFF ? 0x1000 : 0) + (result & 0xFFF); + } else { + result = (uint32_t)_state.A + value + (_state.PS & ProcFlags::Carry); + } + + if(~(_state.A ^ value) & (_state.A ^ result) & 0x8000) { + SetFlags(ProcFlags::Overflow); + } else { + ClearFlags(ProcFlags::Overflow); + } + + if(CheckFlag(ProcFlags::Decimal) && result <= 0xFFFF) { + result -= 0x6000; + } + + ClearFlags(ProcFlags::Carry | ProcFlags::Negative | ProcFlags::Zero); + SetZeroNegativeFlags((uint16_t)result); + + if(result & 0x10000) { + SetFlags(ProcFlags::Carry); + } + + _state.A = (uint16_t)result; +} + +void Cpu::SBC() +{ + if(CheckFlag(ProcFlags::MemoryMode8)) { + Sub8(~GetByteValue()); + } else { + Sub16(~GetWordValue()); + } +} + +/**************** +Branch operations +****************/ +void Cpu::BCC() +{ + BranchRelative(!CheckFlag(ProcFlags::Carry)); +} + +void Cpu::BCS() +{ + BranchRelative(CheckFlag(ProcFlags::Carry)); +} + +void Cpu::BEQ() +{ + BranchRelative(CheckFlag(ProcFlags::Zero)); +} + +void Cpu::BMI() +{ + BranchRelative(CheckFlag(ProcFlags::Negative)); +} + +void Cpu::BNE() +{ + BranchRelative(!CheckFlag(ProcFlags::Zero)); +} + +void Cpu::BPL() +{ + BranchRelative(!CheckFlag(ProcFlags::Negative)); +} + +void Cpu::BRA() +{ + BranchRelative(true); +} + +void Cpu::BRL() +{ + _state.PC = (uint16_t)(_state.PC + (int16_t)_operand); +} + +void Cpu::BVC() +{ + BranchRelative(!CheckFlag(ProcFlags::Overflow)); +} + +void Cpu::BVS() +{ + BranchRelative(CheckFlag(ProcFlags::Overflow)); +} + +void Cpu::BranchRelative(bool branch) +{ + int8_t offset = _operand; + if(branch) { + _state.PC = (uint16_t)(_state.PC + offset); + } +} + +/*************************** +Set/clear flag instructions +****************************/ +void Cpu::CLC() +{ + ClearFlags(ProcFlags::Carry); +} + +void Cpu::CLD() +{ + ClearFlags(ProcFlags::Decimal); +} + +void Cpu::CLI() +{ + ClearFlags(ProcFlags::IrqDisable); +} + +void Cpu::CLV() +{ + ClearFlags(ProcFlags::Overflow); +} + +void Cpu::SEC() +{ + SetFlags(ProcFlags::Carry); +} + +void Cpu::SED() +{ + SetFlags(ProcFlags::Decimal); +} + +void Cpu::SEI() +{ + SetFlags(ProcFlags::IrqDisable); +} + +void Cpu::REP() +{ + ClearFlags((uint8_t)_operand); +} + +void Cpu::SEP() +{ + SetFlags((uint8_t)_operand); +} + +/****************************** +Increment/decrement operations +*******************************/ +void Cpu::DEX() +{ + IncDecReg(_state.X, -1); +} + +void Cpu::DEY() +{ + IncDecReg(_state.Y, -1); +} + +void Cpu::INX() +{ + IncDecReg(_state.X, 1); +} + +void Cpu::INY() +{ + IncDecReg(_state.Y, 1); +} + +void Cpu::DEC() +{ + IncDec(-1); +} + +void Cpu::INC() +{ + IncDec(1); +} + +void Cpu::IncDecReg(uint16_t ®, int8_t offset) +{ + SetRegister(reg, reg + offset, CheckFlag(ProcFlags::IndexMode8)); +} + +void Cpu::IncDec(int8_t offset) +{ + if(_instAddrMode == AddrMode::Acc) { + SetRegister(_state.A, _state.A + offset, CheckFlag(ProcFlags::MemoryMode8)); + } else { + if(CheckFlag(ProcFlags::MemoryMode8)) { + uint8_t value = GetByteValue() + offset; + SetZeroNegativeFlags(value); + Write(_operand, value); + } else { + uint16_t value = GetWordValue() + offset; + SetZeroNegativeFlags(value); + WriteWord(_operand, value); + } + } +} + +/******************** +Compare instructions +*********************/ +void Cpu::Compare(uint16_t reg, bool eightBitMode) +{ + if(eightBitMode) { + uint8_t value = GetByteValue(); + if((uint8_t)reg >= value) { + SetFlags(ProcFlags::Carry); + } else { + ClearFlags(ProcFlags::Carry); + } + uint8_t result = (uint8_t)reg - value; + SetZeroNegativeFlags(result); + } else { + uint16_t value = GetWordValue(); + if(reg >= value) { + SetFlags(ProcFlags::Carry); + } else { + ClearFlags(ProcFlags::Carry); + } + + uint16_t result = reg - value; + SetZeroNegativeFlags(result); + } +} + +void Cpu::CMP() +{ + Compare(_state.A, CheckFlag(ProcFlags::MemoryMode8)); +} + +void Cpu::CPX() +{ + Compare(_state.X, CheckFlag(ProcFlags::IndexMode8)); +} + +void Cpu::CPY() +{ + Compare(_state.Y, CheckFlag(ProcFlags::IndexMode8)); +} + +/***************** +Jump instructions +******************/ +void Cpu::JML() +{ + _state.K = (_operand >> 16) & 0xFF; + _state.PC = (uint16_t)_operand; +} + +void Cpu::JMP() +{ + _state.PC = (uint16_t)_operand; +} + +void Cpu::JSL() +{ + PushByte(_state.K); + PushWord(_state.PC - 1); + _state.K = (_operand >> 16) & 0xFF; + _state.PC = (uint16_t)_operand; +} + +void Cpu::JSR() +{ + PushWord(_state.PC - 1); + _state.PC = (uint16_t)_operand; +} + +void Cpu::RTI() +{ + if(_state.EmulationMode) { + _state.PS = PopByte(); //TODO: incorrect + _state.PC = PopWord(); + } else { + _state.PS = PopByte(); //TODO: incorrect + _state.PC = PopWord(); + _state.K = PopByte(); + } +} + +void Cpu::RTL() +{ + _state.PC = PopWord(); + _state.PC++; + _state.K = PopByte(); +} + +void Cpu::RTS() +{ + _state.PC = PopWord(); + _state.PC++; +} + +/********** +Interrupts +***********/ +void Cpu::ProcessInterrupt(uint16_t vector) +{ + if(_state.EmulationMode) { + PushWord(_state.PC + 1); + PushByte(_state.PS | 0x20); + + SetFlags(ProcFlags::IrqDisable); + ClearFlags(ProcFlags::Decimal); + + _state.PC = ReadDataWord(vector); + } else { + PushByte(_state.K); + PushWord(_state.PC + 1); + PushByte(_state.PS); + + SetFlags(ProcFlags::IrqDisable); + ClearFlags(ProcFlags::Decimal); + + _state.PC = ReadDataWord(vector); + } +} + +void Cpu::BRK() +{ + ProcessInterrupt(_state.EmulationMode ? Cpu::LegacyBreakVector : Cpu::BreakVector); +} + +void Cpu::COP() +{ + ProcessInterrupt(_state.EmulationMode ? Cpu::LegacyCoprocessorVector : Cpu::CoprocessorVector); +} + +/****************** +Bitwise operations +*******************/ +void Cpu::AND() +{ + SetRegister(_state.A, _state.A & GetByteValue(), CheckFlag(ProcFlags::MemoryMode8)); +} + +void Cpu::EOR() +{ + SetRegister(_state.A, _state.A ^ GetByteValue(), CheckFlag(ProcFlags::MemoryMode8)); +} + +void Cpu::ORA() +{ + SetRegister(_state.A, _state.A | GetByteValue(), CheckFlag(ProcFlags::MemoryMode8)); +} + +/**************** +Shift operations +*****************/ +template T Cpu::ShiftLeft(T value) +{ + T result = value << 1; + if(value & (1 << (sizeof(T) * 8 - 1))) { + SetFlags(ProcFlags::Carry); + } else { + ClearFlags(ProcFlags::Carry); + } + SetZeroNegativeFlags(result); + return result; +} + +template T Cpu::RollLeft(T value) +{ + T result = value << 1 | (_state.PS & ProcFlags::Carry); + if(value & (1 << (sizeof(T) * 8 - 1))) { + SetFlags(ProcFlags::Carry); + } else { + ClearFlags(ProcFlags::Carry); + } + SetZeroNegativeFlags(result); + return result; +} + +template T Cpu::ShiftRight(T value) +{ + T result = value >> 1; + if(value & 0x01) { + SetFlags(ProcFlags::Carry); + } else { + ClearFlags(ProcFlags::Carry); + } + SetZeroNegativeFlags(result); + return result; +} + +template T Cpu::RollRight(T value) +{ + T result = value >> 1 | ((_state.PS & 0x01) << (sizeof(T) * 8 - 1)); + if(value & 0x01) { + SetFlags(ProcFlags::Carry); + } else { + ClearFlags(ProcFlags::Carry); + } + SetZeroNegativeFlags(result); + return result; +} + +void Cpu::ASL() +{ + if(_instAddrMode == AddrMode::Acc) { + if(CheckFlag(ProcFlags::MemoryMode8)) { + _state.A = ShiftLeft((uint8_t)_state.A); + } else { + _state.A = ShiftLeft(_state.A); + } + } else { + if(CheckFlag(ProcFlags::MemoryMode8)) { + Write(_operand, ShiftLeft(GetByteValue())); + } else { + WriteWord(_operand, ShiftLeft(GetWordValue())); + } + } +} + +void Cpu::LSR() +{ + if(_instAddrMode == AddrMode::Acc) { + if(CheckFlag(ProcFlags::MemoryMode8)) { + _state.A = ShiftRight((uint8_t)_state.A); + } else { + _state.A = ShiftRight(_state.A); + } + } else { + if(CheckFlag(ProcFlags::MemoryMode8)) { + Write(_operand, ShiftRight(GetByteValue())); + } else { + WriteWord(_operand, ShiftRight(GetWordValue())); + } + } +} + +void Cpu::ROL() +{ + if(_instAddrMode == AddrMode::Acc) { + if(CheckFlag(ProcFlags::MemoryMode8)) { + _state.A = RollLeft((uint8_t)_state.A); + } else { + _state.A = RollLeft(_state.A); + } + } else { + if(CheckFlag(ProcFlags::MemoryMode8)) { + Write(_operand, RollLeft(GetByteValue())); + } else { + WriteWord(_operand, RollLeft(GetWordValue())); + } + } +} + +void Cpu::ROR() +{ + if(_instAddrMode == AddrMode::Acc) { + if(CheckFlag(ProcFlags::MemoryMode8)) { + _state.A = RollRight((uint8_t)_state.A); + } else { + _state.A = RollRight(_state.A); + } + } else { + if(CheckFlag(ProcFlags::MemoryMode8)) { + Write(_operand, RollRight(GetByteValue())); + } else { + WriteWord(_operand, RollRight(GetWordValue())); + } + } +} + +/*************** +Move operations +****************/ +void Cpu::MVN() +{ + //TODO +} + +void Cpu::MVP() +{ + //TODO +} + +/******************** +Push/pull operations +*********************/ +void Cpu::PEA() +{ + //Push Effective Address + PushWord((uint16_t)_operand); +} + +void Cpu::PEI() +{ + //Push Effective Indirect address + PushWord((uint16_t)_operand); +} + +void Cpu::PER() +{ + //Push Effective Relative address + PushWord((uint16_t)_operand); +} + +void Cpu::PHB() +{ + PushWord(_state.DBR); +} + +void Cpu::PHD() +{ + PushWord(_state.D); +} + +void Cpu::PHK() +{ + //"PHP, PHK, PHP, PLB, and PLP push and pull one byte from the stack" + PushByte(_state.K); +} + +void Cpu::PHP() +{ + //"PHP, PHK, PHP, PLB, and PLP push and pull one byte from the stack" + PushByte(_state.PS); +} + +void Cpu::PLB() +{ + //"PHP, PHK, PHP, PLB, and PLP push and pull one byte from the stack" + SetRegister(_state.DBR, PopByte()); +} + +void Cpu::PLD() +{ + //"PHD and PLD push and pull two bytes from the stack." + SetRegister(_state.D, PopWord(), false); +} + +void Cpu::PLP() +{ + //"For PLP, (all of) the flags are pulled from the stack. Note that when the e flag is 1, the m and x flag are forced to 1, so after the PLP, both flags will still be 1 no matter what value is pulled from the stack." + if(_state.EmulationMode) { + _state.PS = PopByte() | ProcFlags::MemoryMode8 | ProcFlags::IndexMode8; + } else { + _state.PS = PopByte(); + } +} + +void Cpu::PHA() +{ + //"When the m flag is 0, PHA and PLA push and pull a 16-bit value, and when the m flag is 1, PHA and PLA push and pull an 8-bit value. " + PushRegister(_state.A, CheckFlag(ProcFlags::MemoryMode8)); +} + +void Cpu::PHX() +{ + PushRegister(_state.X, CheckFlag(ProcFlags::IndexMode8)); +} + +void Cpu::PHY() +{ + PushRegister(_state.Y, CheckFlag(ProcFlags::IndexMode8)); +} + +void Cpu::PLA() +{ + //"When the m flag is 0, PHA and PLA push and pull a 16-bit value, and when the m flag is 1, PHA and PLA push and pull an 8-bit value." + PullRegister(_state.A, CheckFlag(ProcFlags::MemoryMode8)); +} + +void Cpu::PLX() +{ + PullRegister(_state.X, CheckFlag(ProcFlags::IndexMode8)); +} + +void Cpu::PLY() +{ + PullRegister(_state.Y, CheckFlag(ProcFlags::IndexMode8)); +} + +void Cpu::PushRegister(uint16_t reg, bool eightBitMode) +{ + //"When the x flag is 0, PHX, PHY, PLX, and PLY push and pull a 16-bit value, and when the x flag is 1, PHX, PHY, PLX, and PLY push and pull an 8-bit value." + if(eightBitMode) { + PushByte((uint8_t)reg); + } else { + PushWord(reg); + } +} + +void Cpu::PullRegister(uint16_t ®, bool eightBitMode) +{ + //"When the x flag is 0, PHX, PHY, PLX, and PLY push and pull a 16-bit value, and when the x flag is 1, PHX, PHY, PLX, and PLY push and pull an 8-bit value." + if(eightBitMode) { + SetRegister(reg, PopByte(), true); + } else { + SetRegister(reg, PopWord(), false); + } +} + +/********************* +Store/load operations +**********************/ +void Cpu::LoadRegister(uint16_t ®, bool eightBitMode) +{ + if(eightBitMode) { + SetRegister(reg, GetByteValue(), true); + } else { + SetRegister(reg, GetWordValue(), false); + } +} + +void Cpu::StoreRegister(uint16_t val, bool eightBitMode) +{ + if(eightBitMode) { + Write(_operand, (uint8_t)val); + } else { + WriteWord(_operand, val); + } +} + +void Cpu::LDA() +{ + //"When the m flag is 0, LDA, STA, and STZ are 16-bit operations" + LoadRegister(_state.A, CheckFlag(ProcFlags::MemoryMode8)); +} + +void Cpu::LDX() +{ + //"When the x flag is 0, LDX, LDY, STX, and STY are 16-bit operations" + LoadRegister(_state.X, CheckFlag(ProcFlags::IndexMode8)); +} + +void Cpu::LDY() +{ + //"When the x flag is 0, LDX, LDY, STX, and STY are 16-bit operations" + LoadRegister(_state.Y, CheckFlag(ProcFlags::IndexMode8)); +} + +void Cpu::STA() +{ + //"When the m flag is 0, LDA, STA, and STZ are 16-bit operations" + StoreRegister(_state.A, CheckFlag(ProcFlags::MemoryMode8)); +} + +void Cpu::STX() +{ + //"When the x flag is 0, LDX, LDY, STX, and STY are 16-bit operations" + StoreRegister(_state.X, CheckFlag(ProcFlags::IndexMode8)); +} + +void Cpu::STY() +{ + //"When the x flag is 0, LDX, LDY, STX, and STY are 16-bit operations" + StoreRegister(_state.Y, CheckFlag(ProcFlags::IndexMode8)); +} + +void Cpu::STZ() +{ + //"When the m flag is 0, LDA, STA, and STZ are 16-bit operations" + StoreRegister(0, CheckFlag(ProcFlags::MemoryMode8)); +} + +/******************* +Bit test operations +********************/ +template void Cpu::TestBits(T value) +{ + ClearFlags(ProcFlags::Zero | ProcFlags::Overflow | ProcFlags::Negative); + if(((T)_state.A & value) == 0) { + SetFlags(ProcFlags::Zero); + } + if(value & (1 << (sizeof(T) * 8 - 2))) { + SetFlags(ProcFlags::Overflow); + } + if(value & (1 << (sizeof(T) * 8 - 1))) { + SetFlags(ProcFlags::Negative); + } +} + +void Cpu::BIT() +{ + if(CheckFlag(ProcFlags::MemoryMode8)) { + TestBits(GetByteValue()); + } else { + TestBits(GetWordValue()); + } +} + +void Cpu::TRB() +{ + //TODO +} + +void Cpu::TSB() +{ + //TODO +} + +/****************** +Transfer operations +*******************/ +void Cpu::TAX() +{ + SetRegister(_state.X, _state.A, CheckFlag(ProcFlags::IndexMode8)); +} + +void Cpu::TAY() +{ + SetRegister(_state.Y, _state.A, CheckFlag(ProcFlags::IndexMode8)); +} + +void Cpu::TCD() +{ + SetRegister(_state.DBR, (uint8_t)_state.A); +} + +void Cpu::TCS() +{ + _state.SP = _state.A; +} + +void Cpu::TDC() +{ + SetRegister(_state.A, _state.DBR, CheckFlag(ProcFlags::MemoryMode8)); +} + +void Cpu::TSC() +{ + SetRegister(_state.A, _state.SP, CheckFlag(ProcFlags::MemoryMode8)); +} + +void Cpu::TSX() +{ + SetRegister(_state.X, _state.SP, CheckFlag(ProcFlags::IndexMode8)); +} + +void Cpu::TXA() +{ + SetRegister(_state.A, _state.X, CheckFlag(ProcFlags::MemoryMode8)); +} + +void Cpu::TXS() +{ + _state.SP = _state.X; +} + +void Cpu::TXY() +{ + SetRegister(_state.Y, _state.X, CheckFlag(ProcFlags::IndexMode8)); +} + +void Cpu::TYA() +{ + SetRegister(_state.A, _state.Y, CheckFlag(ProcFlags::MemoryMode8)); +} + +void Cpu::TYX() +{ + SetRegister(_state.X, _state.Y, CheckFlag(ProcFlags::IndexMode8)); +} + +void Cpu::XBA() +{ + _state.A = ((_state.A & 0xFF) << 8) | ((_state.A >> 8) & 0xFF); + SetZeroNegativeFlags((uint8_t)_state.A); +} + +void Cpu::XCE() +{ + _state.EmulationMode = (_state.PS & ProcFlags::Carry) ? true : false; +} + +/***************** +No operation (NOP) +******************/ +void Cpu::NOP() +{ + //1-byte NOP +} + +void Cpu::WDM() +{ + //2-byte NOP +} + +/**************** +Misc. operations +*****************/ +void Cpu::STP() +{ + //Stop the CPU +} + +void Cpu::WAI() +{ + //Wait for interrupt +} \ No newline at end of file diff --git a/Core/Cpu.cpp b/Core/Cpu.cpp index a7e3d18..486d2cb 100644 --- a/Core/Cpu.cpp +++ b/Core/Cpu.cpp @@ -5,925 +5,6 @@ //TODO: PER doesn't load the right number of bytes? -/************************ -Add/substract operations -*************************/ -void Cpu::Add8(uint8_t value) -{ - uint32_t result; - if(CheckFlag(ProcFlags::Decimal)) { - result = (_state.A & 0x0F) + (value & 0x0F) + (_state.PS & ProcFlags::Carry); - if(result > 0x09) result += 0x06; - result = (_state.A & 0xF0) + (value & 0xF0) + (result > 0x0F ? 0x10 : 0) + (result & 0x0F); - } else { - result = (uint32_t)_state.A + value + (_state.PS & ProcFlags::Carry); - } - - if(~(_state.A ^ value) & (_state.A ^ result) & 0x80) { - SetFlags(ProcFlags::Overflow); - } else { - ClearFlags(ProcFlags::Overflow); - } - - if(CheckFlag(ProcFlags::Decimal) && result > 0x9F) { - result += 0x60; - } - - ClearFlags(ProcFlags::Carry | ProcFlags::Negative | ProcFlags::Zero); - SetZeroNegativeFlags((uint8_t)result); - - if(result & 0x100) { - SetFlags(ProcFlags::Carry); - } - - _state.A = (uint8_t)result; -} - -void Cpu::Add16(uint16_t value) -{ - uint32_t result; - if(CheckFlag(ProcFlags::Decimal)) { - result = (_state.A & 0x0F) + (value & 0x0F) + (_state.PS & ProcFlags::Carry); - - if(result > 0x09) result += 0x06; - result = (_state.A & 0xF0) + (value & 0xF0) + (result > 0x0F ? 0x10 : 0) + (result & 0x0F); - - if(result > 0x9F) result += 0x60; - result = (_state.A & 0xF00) + (value & 0xF00) + (result > 0xFF ? 0x100 : 0) + (result & 0xFF); - - if(result > 0x9FF) result += 0x600; - result = (_state.A & 0xF000) + (value & 0xF000) + (result > 0xFFF ? 0x1000 : 0) + (result & 0xFFF); - } else { - result = (uint32_t)_state.A + value + (_state.PS & ProcFlags::Carry); - } - - if(~(_state.A ^ value) & (_state.A ^ result) & 0x8000) { - SetFlags(ProcFlags::Overflow); - } else { - ClearFlags(ProcFlags::Overflow); - } - - if(CheckFlag(ProcFlags::Decimal) && result > 0x9FFF) { - result += 0x6000; - } - - ClearFlags(ProcFlags::Carry | ProcFlags::Negative | ProcFlags::Zero); - SetZeroNegativeFlags((uint16_t)result); - - if(result & 0x10000) { - SetFlags(ProcFlags::Carry); - } - - _state.A = (uint16_t)result; -} - -void Cpu::ADC() -{ - if(CheckFlag(ProcFlags::MemoryMode8)) { - Add8(GetByteValue()); - } else { - Add16(GetWordValue()); - } -} - -void Cpu::Sub8(uint8_t value) -{ - uint32_t result; - if(CheckFlag(ProcFlags::Decimal)) { - result = (_state.A & 0x0F) + (value & 0x0F) + (_state.PS & ProcFlags::Carry); - if(result <= 0x0F) result -= 0x06; - result = (_state.A & 0xF0) + (value & 0xF0) + (result > 0x0F ? 0x10 : 0) + (result & 0x0F); - } else { - result = (uint32_t)_state.A + value + (_state.PS & ProcFlags::Carry); - } - - if(~(_state.A ^ value) & (_state.A ^ result) & 0x80) { - SetFlags(ProcFlags::Overflow); - } else { - ClearFlags(ProcFlags::Overflow); - } - - if(CheckFlag(ProcFlags::Decimal) && result <= 0xFF) { - result -= 0x60; - } - - ClearFlags(ProcFlags::Carry | ProcFlags::Negative | ProcFlags::Zero); - SetZeroNegativeFlags((uint8_t)result); - - if(result & 0x100) { - SetFlags(ProcFlags::Carry); - } - - _state.A = (uint8_t)result; -} - -void Cpu::Sub16(uint16_t value) -{ - uint32_t result; - if(CheckFlag(ProcFlags::Decimal)) { - result = (_state.A & 0x0F) + (value & 0x0F) + (_state.PS & ProcFlags::Carry); - - if(result <= 0x0F) result -= 0x06; - result = (_state.A & 0xF0) + (value & 0xF0) + (result > 0x0F ? 0x10 : 0) + (result & 0x0F); - - if(result <= 0xFF) result -= 0x60; - result = (_state.A & 0xF00) + (value & 0xF00) + (result > 0xFF ? 0x100 : 0) + (result & 0xFF); - - if(result <= 0xFFF) result -= 0x600; - result = (_state.A & 0xF000) + (value & 0xF000) + (result > 0xFFF ? 0x1000 : 0) + (result & 0xFFF); - } else { - result = (uint32_t)_state.A + value + (_state.PS & ProcFlags::Carry); - } - - if(~(_state.A ^ value) & (_state.A ^ result) & 0x8000) { - SetFlags(ProcFlags::Overflow); - } else { - ClearFlags(ProcFlags::Overflow); - } - - if(CheckFlag(ProcFlags::Decimal) && result <= 0xFFFF) { - result -= 0x6000; - } - - ClearFlags(ProcFlags::Carry | ProcFlags::Negative | ProcFlags::Zero); - SetZeroNegativeFlags((uint16_t)result); - - if(result & 0x10000) { - SetFlags(ProcFlags::Carry); - } - - _state.A = (uint16_t)result; -} - -void Cpu::SBC() -{ - if(CheckFlag(ProcFlags::MemoryMode8)) { - Sub8(~GetByteValue()); - } else { - Sub16(~GetWordValue()); - } -} - -/**************** -Branch operations -****************/ -void Cpu::BCC() -{ - BranchRelative(!CheckFlag(ProcFlags::Carry)); -} - -void Cpu::BCS() -{ - BranchRelative(CheckFlag(ProcFlags::Carry)); -} - -void Cpu::BEQ() -{ - BranchRelative(CheckFlag(ProcFlags::Zero)); -} - -void Cpu::BMI() -{ - BranchRelative(CheckFlag(ProcFlags::Negative)); -} - -void Cpu::BNE() -{ - BranchRelative(!CheckFlag(ProcFlags::Zero)); -} - -void Cpu::BPL() -{ - BranchRelative(!CheckFlag(ProcFlags::Negative)); -} - -void Cpu::BRA() -{ - BranchRelative(true); -} - -void Cpu::BRL() -{ - _state.PC = (uint16_t)(_state.PC + (int16_t)_operand); -} - -void Cpu::BVC() -{ - BranchRelative(!CheckFlag(ProcFlags::Overflow)); -} - -void Cpu::BVS() -{ - BranchRelative(CheckFlag(ProcFlags::Overflow)); -} - -void Cpu::BranchRelative(bool branch) -{ - int8_t offset = _operand; - if(branch) { - _state.PC = (uint16_t)(_state.PC + offset); - } -} - -/*************************** -Set/clear flag instructions -****************************/ -void Cpu::CLC() -{ - ClearFlags(ProcFlags::Carry); -} - -void Cpu::CLD() -{ - ClearFlags(ProcFlags::Decimal); -} - -void Cpu::CLI() -{ - ClearFlags(ProcFlags::IrqDisable); -} - -void Cpu::CLV() -{ - ClearFlags(ProcFlags::Overflow); -} - -void Cpu::SEC() -{ - SetFlags(ProcFlags::Carry); -} - -void Cpu::SED() -{ - SetFlags(ProcFlags::Decimal); -} - -void Cpu::SEI() -{ - SetFlags(ProcFlags::IrqDisable); -} - -void Cpu::REP() -{ - ClearFlags((uint8_t)_operand); -} - -void Cpu::SEP() -{ - SetFlags((uint8_t)_operand); -} - -/****************************** -Increment/decrement operations -*******************************/ -void Cpu::DEX() -{ - IncDecReg(_state.X, -1); -} - -void Cpu::DEY() -{ - IncDecReg(_state.Y, -1); -} - -void Cpu::INX() -{ - IncDecReg(_state.X, 1); -} - -void Cpu::INY() -{ - IncDecReg(_state.Y, 1); -} - -void Cpu::DEC() -{ - IncDec(-1); -} - -void Cpu::INC() -{ - IncDec(1); -} - -void Cpu::IncDecReg(uint16_t ®, int8_t offset) -{ - SetRegister(reg, reg + offset, CheckFlag(ProcFlags::IndexMode8)); -} - -void Cpu::IncDec(int8_t offset) -{ - if(_instAddrMode == AddrMode::Acc) { - SetRegister(_state.A, _state.A + offset, CheckFlag(ProcFlags::MemoryMode8)); - } else { - if(CheckFlag(ProcFlags::MemoryMode8)) { - uint8_t value = GetByteValue() + offset; - SetZeroNegativeFlags(value); - Write(_operand, value); - } else { - uint16_t value = GetWordValue() + offset; - SetZeroNegativeFlags(value); - WriteWord(_operand, value); - } - } -} - -/******************** -Compare instructions -*********************/ -void Cpu::Compare(uint16_t reg, bool eightBitMode) -{ - if(eightBitMode) { - uint8_t value = GetByteValue(); - if((uint8_t)reg >= value) { - SetFlags(ProcFlags::Carry); - } else { - ClearFlags(ProcFlags::Carry); - } - uint8_t result = (uint8_t)reg - value; - SetZeroNegativeFlags(result); - } else { - uint16_t value = GetWordValue(); - if(reg >= value) { - SetFlags(ProcFlags::Carry); - } else { - ClearFlags(ProcFlags::Carry); - } - - uint16_t result = reg - value; - SetZeroNegativeFlags(result); - } -} - -void Cpu::CMP() -{ - Compare(_state.A, CheckFlag(ProcFlags::MemoryMode8)); -} - -void Cpu::CPX() -{ - Compare(_state.X, CheckFlag(ProcFlags::IndexMode8)); -} - -void Cpu::CPY() -{ - Compare(_state.Y, CheckFlag(ProcFlags::IndexMode8)); -} - -/***************** -Jump instructions -******************/ -void Cpu::JML() -{ - _state.K = (_operand >> 16) & 0xFF; - _state.PC = (uint16_t)_operand; -} - -void Cpu::JMP() -{ - _state.PC = (uint16_t)_operand; -} - -void Cpu::JSL() -{ - PushByte(_state.K); - PushWord(_state.PC - 1); - _state.K = (_operand >> 16) & 0xFF; - _state.PC = (uint16_t)_operand; -} - -void Cpu::JSR() -{ - PushWord(_state.PC - 1); - _state.PC = (uint16_t)_operand; -} - -void Cpu::RTI() -{ - if(_state.EmulationMode) { - _state.PS = PopByte(); //TODO: incorrect - _state.PC = PopWord(); - } else { - _state.PS = PopByte(); //TODO: incorrect - _state.PC = PopWord(); - _state.K = PopByte(); - } -} - -void Cpu::RTL() -{ - _state.PC = PopWord(); - _state.PC++; - _state.K = PopByte(); -} - -void Cpu::RTS() -{ - _state.PC = PopWord(); - _state.PC++; -} - -/********** -Interrupts -***********/ -void Cpu::ProcessInterrupt(uint16_t vector) -{ - if(_state.EmulationMode) { - PushWord(_state.PC + 1); - PushByte(_state.PS | 0x20); - - SetFlags(ProcFlags::IrqDisable); - ClearFlags(ProcFlags::Decimal); - - _state.PC = ReadDataWord(vector); - } else { - PushByte(_state.K); - PushWord(_state.PC + 1); - PushByte(_state.PS); - - SetFlags(ProcFlags::IrqDisable); - ClearFlags(ProcFlags::Decimal); - - _state.PC = ReadDataWord(vector); - } -} - -void Cpu::BRK() -{ - ProcessInterrupt(_state.EmulationMode ? Cpu::LegacyBreakVector : Cpu::BreakVector); -} - -void Cpu::COP() -{ - ProcessInterrupt(_state.EmulationMode ? Cpu::LegacyCoprocessorVector : Cpu::CoprocessorVector); -} - -/****************** -Bitwise operations -*******************/ -void Cpu::AND() -{ - SetRegister(_state.A, _state.A & GetByteValue(), CheckFlag(ProcFlags::MemoryMode8)); -} - -void Cpu::EOR() -{ - SetRegister(_state.A, _state.A ^ GetByteValue(), CheckFlag(ProcFlags::MemoryMode8)); -} - -void Cpu::ORA() -{ - SetRegister(_state.A, _state.A | GetByteValue(), CheckFlag(ProcFlags::MemoryMode8)); -} - -/**************** -Shift operations -*****************/ -template T Cpu::ShiftLeft(T value) -{ - T result = value << 1; - if(value & (1 << (sizeof(T)*8-1))) { - SetFlags(ProcFlags::Carry); - } else { - ClearFlags(ProcFlags::Carry); - } - SetZeroNegativeFlags(result); - return result; -} - -template T Cpu::RollLeft(T value) -{ - T result = value << 1 | (_state.PS & ProcFlags::Carry); - if(value & (1 << (sizeof(T) * 8 - 1))) { - SetFlags(ProcFlags::Carry); - } else { - ClearFlags(ProcFlags::Carry); - } - SetZeroNegativeFlags(result); - return result; -} - -template T Cpu::ShiftRight(T value) -{ - T result = value >> 1; - if(value & 0x01) { - SetFlags(ProcFlags::Carry); - } else { - ClearFlags(ProcFlags::Carry); - } - SetZeroNegativeFlags(result); - return result; -} - -template T Cpu::RollRight(T value) -{ - T result = value >> 1 | ((_state.PS & 0x01) << (sizeof(T) * 8 - 1)); - if(value & 0x01) { - SetFlags(ProcFlags::Carry); - } else { - ClearFlags(ProcFlags::Carry); - } - SetZeroNegativeFlags(result); - return result; -} - -void Cpu::ASL() -{ - if(_instAddrMode == AddrMode::Acc) { - if(CheckFlag(ProcFlags::MemoryMode8)) { - _state.A = ShiftLeft((uint8_t)_state.A); - } else { - _state.A = ShiftLeft(_state.A); - } - } else { - if(CheckFlag(ProcFlags::MemoryMode8)) { - Write(_operand, ShiftLeft(GetByteValue())); - } else { - WriteWord(_operand, ShiftLeft(GetWordValue())); - } - } -} - -void Cpu::LSR() -{ - if(_instAddrMode == AddrMode::Acc) { - if(CheckFlag(ProcFlags::MemoryMode8)) { - _state.A = ShiftRight((uint8_t)_state.A); - } else { - _state.A = ShiftRight(_state.A); - } - } else { - if(CheckFlag(ProcFlags::MemoryMode8)) { - Write(_operand, ShiftRight(GetByteValue())); - } else { - WriteWord(_operand, ShiftRight(GetWordValue())); - } - } -} - -void Cpu::ROL() -{ - if(_instAddrMode == AddrMode::Acc) { - if(CheckFlag(ProcFlags::MemoryMode8)) { - _state.A = RollLeft((uint8_t)_state.A); - } else { - _state.A = RollLeft(_state.A); - } - } else { - if(CheckFlag(ProcFlags::MemoryMode8)) { - Write(_operand, RollLeft(GetByteValue())); - } else { - WriteWord(_operand, RollLeft(GetWordValue())); - } - } -} - -void Cpu::ROR() -{ - if(_instAddrMode == AddrMode::Acc) { - if(CheckFlag(ProcFlags::MemoryMode8)) { - _state.A = RollRight((uint8_t)_state.A); - } else { - _state.A = RollRight(_state.A); - } - } else { - if(CheckFlag(ProcFlags::MemoryMode8)) { - Write(_operand, RollRight(GetByteValue())); - } else { - WriteWord(_operand, RollRight(GetWordValue())); - } - } -} - -/*************** -Move operations -****************/ -void Cpu::MVN() -{ - //TODO -} - -void Cpu::MVP() -{ - //TODO -} - -/******************** - Push/pull operations -*********************/ -void Cpu::PEA() -{ - //Push Effective Address - PushWord((uint16_t)_operand); -} - -void Cpu::PEI() -{ - //Push Effective Indirect address - PushWord((uint16_t)_operand); -} - -void Cpu::PER() -{ - //Push Effective Relative address - PushWord((uint16_t)_operand); -} - -void Cpu::PHB() -{ - PushWord(_state.DBR); -} - -void Cpu::PHD() -{ - PushWord(_state.D); -} - -void Cpu::PHK() -{ - //"PHP, PHK, PHP, PLB, and PLP push and pull one byte from the stack" - PushByte(_state.K); -} - -void Cpu::PHP() -{ - //"PHP, PHK, PHP, PLB, and PLP push and pull one byte from the stack" - PushByte(_state.PS); -} - -void Cpu::PLB() -{ - //"PHP, PHK, PHP, PLB, and PLP push and pull one byte from the stack" - SetRegister(_state.DBR, PopByte()); -} - -void Cpu::PLD() -{ - //"PHD and PLD push and pull two bytes from the stack." - SetRegister(_state.D, PopWord(), false); -} - -void Cpu::PLP() -{ - //"For PLP, (all of) the flags are pulled from the stack. Note that when the e flag is 1, the m and x flag are forced to 1, so after the PLP, both flags will still be 1 no matter what value is pulled from the stack." - if(_state.EmulationMode) { - _state.PS = PopByte() | ProcFlags::MemoryMode8 | ProcFlags::IndexMode8; - } else { - _state.PS = PopByte(); - } -} - -void Cpu::PHA() -{ - //"When the m flag is 0, PHA and PLA push and pull a 16-bit value, and when the m flag is 1, PHA and PLA push and pull an 8-bit value. " - PushRegister(_state.A, CheckFlag(ProcFlags::MemoryMode8)); -} - -void Cpu::PHX() -{ - PushRegister(_state.X, CheckFlag(ProcFlags::IndexMode8)); -} - -void Cpu::PHY() -{ - PushRegister(_state.Y, CheckFlag(ProcFlags::IndexMode8)); -} - -void Cpu::PLA() -{ - //"When the m flag is 0, PHA and PLA push and pull a 16-bit value, and when the m flag is 1, PHA and PLA push and pull an 8-bit value." - PullRegister(_state.A, CheckFlag(ProcFlags::MemoryMode8)); -} - -void Cpu::PLX() -{ - PullRegister(_state.X, CheckFlag(ProcFlags::IndexMode8)); -} - -void Cpu::PLY() -{ - PullRegister(_state.Y, CheckFlag(ProcFlags::IndexMode8)); -} - -void Cpu::PushRegister(uint16_t reg, bool eightBitMode) -{ - //"When the x flag is 0, PHX, PHY, PLX, and PLY push and pull a 16-bit value, and when the x flag is 1, PHX, PHY, PLX, and PLY push and pull an 8-bit value." - if(eightBitMode) { - PushByte((uint8_t)reg); - } else { - PushWord(reg); - } -} - -void Cpu::PullRegister(uint16_t ®, bool eightBitMode) -{ - //"When the x flag is 0, PHX, PHY, PLX, and PLY push and pull a 16-bit value, and when the x flag is 1, PHX, PHY, PLX, and PLY push and pull an 8-bit value." - if(eightBitMode) { - SetRegister(reg, PopByte(), true); - } else { - SetRegister(reg, PopWord(), false); - } -} - -/********************* -Store/load operations -**********************/ -void Cpu::LoadRegister(uint16_t ®, bool eightBitMode) -{ - if(eightBitMode) { - SetRegister(reg, GetByteValue(), true); - } else { - SetRegister(reg, GetWordValue(), false); - } -} - -void Cpu::StoreRegister(uint16_t val, bool eightBitMode) -{ - if(eightBitMode) { - Write(_operand, (uint8_t)val); - } else { - WriteWord(_operand, val); - } -} - -void Cpu::LDA() -{ - //"When the m flag is 0, LDA, STA, and STZ are 16-bit operations" - LoadRegister(_state.A, CheckFlag(ProcFlags::MemoryMode8)); -} - -void Cpu::LDX() -{ - //"When the x flag is 0, LDX, LDY, STX, and STY are 16-bit operations" - LoadRegister(_state.X, CheckFlag(ProcFlags::IndexMode8)); -} - -void Cpu::LDY() -{ - //"When the x flag is 0, LDX, LDY, STX, and STY are 16-bit operations" - LoadRegister(_state.Y, CheckFlag(ProcFlags::IndexMode8)); -} - -void Cpu::STA() -{ - //"When the m flag is 0, LDA, STA, and STZ are 16-bit operations" - StoreRegister(_state.A, CheckFlag(ProcFlags::MemoryMode8)); -} - -void Cpu::STX() -{ - //"When the x flag is 0, LDX, LDY, STX, and STY are 16-bit operations" - StoreRegister(_state.X, CheckFlag(ProcFlags::IndexMode8)); -} - -void Cpu::STY() -{ - //"When the x flag is 0, LDX, LDY, STX, and STY are 16-bit operations" - StoreRegister(_state.Y, CheckFlag(ProcFlags::IndexMode8)); -} - -void Cpu::STZ() -{ - //"When the m flag is 0, LDA, STA, and STZ are 16-bit operations" - StoreRegister(0, CheckFlag(ProcFlags::MemoryMode8)); -} - -/******************* -Bit test operations -********************/ -template void Cpu::TestBits(T value) -{ - ClearFlags(ProcFlags::Zero | ProcFlags::Overflow | ProcFlags::Negative); - if(((T)_state.A & value) == 0) { - SetFlags(ProcFlags::Zero); - } - if(value & (1 << (sizeof(T) * 8 - 2))) { - SetFlags(ProcFlags::Overflow); - } - if(value & (1 << (sizeof(T) * 8 - 1))) { - SetFlags(ProcFlags::Negative); - } -} - -void Cpu::BIT() -{ - if(CheckFlag(ProcFlags::MemoryMode8)) { - TestBits(GetByteValue()); - } else { - TestBits(GetWordValue()); - } -} - -void Cpu::TRB() -{ - //TODO -} - -void Cpu::TSB() -{ - //TODO -} - -/****************** -Transfer operations -*******************/ -void Cpu::TAX() -{ - SetRegister(_state.X, _state.A, CheckFlag(ProcFlags::IndexMode8)); -} - -void Cpu::TAY() -{ - SetRegister(_state.Y, _state.A, CheckFlag(ProcFlags::IndexMode8)); -} - -void Cpu::TCD() -{ - SetRegister(_state.DBR, (uint8_t)_state.A); -} - -void Cpu::TCS() -{ - _state.SP = _state.A; -} - -void Cpu::TDC() -{ - SetRegister(_state.A, _state.DBR, CheckFlag(ProcFlags::MemoryMode8)); -} - -void Cpu::TSC() -{ - SetRegister(_state.A, _state.SP, CheckFlag(ProcFlags::MemoryMode8)); -} - -void Cpu::TSX() -{ - SetRegister(_state.X, _state.SP, CheckFlag(ProcFlags::IndexMode8)); -} - -void Cpu::TXA() -{ - SetRegister(_state.A, _state.X, CheckFlag(ProcFlags::MemoryMode8)); -} - -void Cpu::TXS() -{ - _state.SP = _state.X; -} - -void Cpu::TXY() -{ - SetRegister(_state.Y, _state.X, CheckFlag(ProcFlags::IndexMode8)); -} - -void Cpu::TYA() -{ - SetRegister(_state.A, _state.Y, CheckFlag(ProcFlags::MemoryMode8)); -} - -void Cpu::TYX() -{ - SetRegister(_state.X, _state.Y, CheckFlag(ProcFlags::IndexMode8)); -} - -void Cpu::XBA() -{ - _state.A = ((_state.A & 0xFF) << 8) | ((_state.A >> 8) & 0xFF); - SetZeroNegativeFlags((uint8_t)_state.A); -} - -void Cpu::XCE() -{ - _state.EmulationMode = (_state.PS & ProcFlags::Carry) ? true : false; -} - -/***************** -No operation (NOP) -******************/ -void Cpu::NOP() -{ - //1-byte NOP -} - -void Cpu::WDM() -{ - //2-byte NOP -} - -/**************** -Misc. operations -*****************/ -void Cpu::STP() -{ - //Stop the CPU -} - -void Cpu::WAI() -{ - //Wait for interrupt -} - Cpu::Cpu(shared_ptr memoryManager) { typedef Cpu C; @@ -952,10 +33,10 @@ Cpu::Cpu(shared_ptr memoryManager) //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::Abs, M::DirIdxIndX, M::AbsLng, 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::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::Abs, 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::AbsLng, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // 5 + 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::Stk, 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 @@ -998,14 +79,9 @@ void Cpu::Exec() opCount++; } -uint32_t Cpu::GetBank() +uint32_t Cpu::GetProgramAddress(uint16_t addr) { - return _state.DBR << 16; -} - -uint32_t Cpu::GetProgramAddress() -{ - return (_state.K << 16) | _state.PC; + return (_state.K << 16) | addr; } uint32_t Cpu::GetDataAddress(uint16_t addr) @@ -1047,11 +123,20 @@ uint32_t Cpu::ReadOperandLong() 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); } @@ -1072,6 +157,7 @@ uint32_t Cpu::ReadDataLong(uint32_t addr, MemoryOperationType type) void Cpu::Write(uint32_t addr, uint8_t value, MemoryOperationType type) { + _state.CycleCount++; _memoryManager->Write(addr, value, type); } @@ -1147,15 +233,17 @@ uint16_t Cpu::GetDirectAddress(uint8_t baseAddress, uint16_t offset, bool allowE uint32_t Cpu::FetchEffectiveAddress() { switch(_instAddrMode) { - case AddrMode::Abs: return GetBank() | ReadOperandWord(); - case AddrMode::AbsIdxXInd: return ReadDataWord((_state.K << 16) | ReadOperandWord()); //JMP/JSR - case AddrMode::AbsIdxX: return (GetBank() | ReadOperandWord()) + _state.X; - case AddrMode::AbsIdxY: return (GetBank() | ReadOperandWord()) + _state.Y; - case AddrMode::AbsInd: return ReadDataWord(ReadOperandWord()); //JMP only - case AddrMode::AbsIndLng: return ReadDataLong(ReadOperandLong()); //JML only - - case AddrMode::AbsLngIdxX: return ReadOperandLong() + _state.X; + case AddrMode::Abs: return GetDataAddress(ReadOperandWord()); + case AddrMode::AbsIdxX: return GetDataAddress(ReadOperandWord()) + _state.X; + case AddrMode::AbsIdxY: return GetDataAddress(ReadOperandWord()) + _state.Y; case AddrMode::AbsLng: return ReadOperandLong(); + case AddrMode::AbsLngIdxX: return ReadOperandLong() + _state.X; + + 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 GetProgramAddress(ReadDataWord(ReadOperandWord())); //JMP only + case AddrMode::AbsIndLng: return ReadDataLong(ReadOperandWord()); //JML only case AddrMode::Acc: DummyRead(); return 0; @@ -1165,7 +253,7 @@ uint32_t Cpu::FetchEffectiveAddress() uint8_t operand = ReadOperandByte(); uint8_t lsb = ReadData(GetDirectAddress(operand, _state.X)); uint8_t msb = ReadData(GetDirectAddress(operand, _state.X + 1)); - return GetBank() | (msb << 8) | lsb; + return GetDataAddress((msb << 8) | lsb); } case AddrMode::DirIdxX: return GetDirectAddress(ReadOperandByte(), _state.X); @@ -1175,7 +263,7 @@ uint32_t Cpu::FetchEffectiveAddress() uint8_t operand = ReadOperandByte(); uint8_t lsb = ReadData(GetDirectAddress(operand)); uint8_t msb = ReadData(GetDirectAddress(operand, 1)); - return (GetBank() | (msb << 8) | lsb) + _state.Y; + return GetDataAddress((msb << 8) | lsb) + _state.Y; } case AddrMode::DirIndLngIdxY: { @@ -1198,7 +286,7 @@ uint32_t Cpu::FetchEffectiveAddress() uint8_t operand = ReadOperandByte(); uint8_t lsb = ReadData(GetDirectAddress(operand)); uint8_t msb = ReadData(GetDirectAddress(operand, 1)); - return GetBank() | (msb << 8) | lsb; + return GetDataAddress((msb << 8) | lsb); } case AddrMode::Dir: return GetDirectAddress(ReadOperandByte()); @@ -1217,7 +305,7 @@ uint32_t Cpu::FetchEffectiveAddress() case AddrMode::StkRelIndIdxY: { uint16_t addr = (uint16_t)(ReadOperandByte() + _state.SP); - return (GetBank() | addr) + _state.Y; + return GetDataAddress(addr) + _state.Y; } } diff --git a/Core/Cpu.h b/Core/Cpu.h index 5ccf4e7..c476202 100644 --- a/Core/Cpu.h +++ b/Core/Cpu.h @@ -33,10 +33,10 @@ private: Func _opTable[256]; AddrMode _addrMode[256]; - uint32_t GetBank(); - uint32_t GetProgramAddress(); - uint16_t GetDirectAddress(uint8_t baseAddress, uint16_t offset = 0, bool allowEmulationMode = true); + uint32_t GetProgramAddress(uint16_t addr); uint32_t GetDataAddress(uint16_t addr); + + uint16_t GetDirectAddress(uint8_t baseAddress, uint16_t offset = 0, bool allowEmulationMode = true); uint8_t GetOpCode(); void DummyRead(); @@ -57,6 +57,8 @@ private: bool CheckFlag(uint8_t flag); uint8_t ReadCode(uint16_t addr, MemoryOperationType type = MemoryOperationType::Read); + uint16_t ReadCodeWord(uint16_t addr, MemoryOperationType type = MemoryOperationType::Read); + uint8_t ReadData(uint32_t addr, MemoryOperationType type = MemoryOperationType::Read); uint16_t ReadDataWord(uint32_t addr, MemoryOperationType type = MemoryOperationType::Read); uint32_t ReadDataLong(uint32_t addr, MemoryOperationType type = MemoryOperationType::Read); diff --git a/Core/CpuTypes.h b/Core/CpuTypes.h index 1507500..ef37a92 100644 --- a/Core/CpuTypes.h +++ b/Core/CpuTypes.h @@ -62,9 +62,11 @@ enum class AddrMode : uint8_t AbsIdxX, AbsIdxY, AbsInd, //JMP only - AbsIndLng, //JMP only (unofficial) + AbsIndLng, //JML only AbsLngIdxX, AbsLng, + AbsJmp, //JSR/JMP only + AbsLngJmp, //JSL/JMP only Acc, BlkMov, DirIdxIndX, diff --git a/Core/Debugger.cpp b/Core/Debugger.cpp index 5a2de77..d8de80f 100644 --- a/Core/Debugger.cpp +++ b/Core/Debugger.cpp @@ -1,14 +1,16 @@ #include "stdafx.h" #include "Debugger.h" #include "Cpu.h" +#include "Ppu.h" #include "CpuTypes.h" #include "DisassemblyInfo.h" #include "TraceLogger.h" #include "../Utilities/HexUtilities.h" -Debugger::Debugger(shared_ptr cpu, shared_ptr memoryManager) +Debugger::Debugger(shared_ptr cpu, shared_ptr ppu, shared_ptr memoryManager) { _cpu = cpu; + _ppu = ppu; _memoryManager = memoryManager; _traceLogger.reset(new TraceLogger(this, memoryManager)); @@ -24,10 +26,11 @@ void Debugger::ProcessCpuRead(uint32_t addr, uint8_t value, MemoryOperationType if(type == MemoryOperationType::ExecOpCode) { CpuState state = _cpu->GetState(); DisassemblyInfo disassemblyInfo(state, _memoryManager.get()); - DebugState debugState = { state }; + DebugState debugState; + GetState(&debugState); _traceLogger->Log(debugState, disassemblyInfo); - string out; + /*string out; out += HexUtilities::ToHex(state.K) + HexUtilities::ToHex(state.PC); out += " "; disassemblyInfo.GetDisassembly(out, _memoryManager.get()); @@ -43,7 +46,7 @@ void Debugger::ProcessCpuRead(uint32_t addr, uint8_t value, MemoryOperationType " DB:$" << HexUtilities::ToHex(state.DBR) << " P:$" << HexUtilities::ToHex(state.PS) << std::endl; - //_traceLogger->Trace + //_traceLogger->Trace*/ if(_cpuStepCount > 0) { _cpuStepCount--; @@ -77,6 +80,12 @@ bool Debugger::IsExecutionStopped() return false; } +void Debugger::GetState(DebugState *state) +{ + state->Cpu = _cpu->GetState(); + state->Ppu = _ppu->GetState(); +} + shared_ptr Debugger::GetTraceLogger() { return _traceLogger; diff --git a/Core/Debugger.h b/Core/Debugger.h index 3d06055..e087c03 100644 --- a/Core/Debugger.h +++ b/Core/Debugger.h @@ -1,8 +1,10 @@ #pragma once #include "stdafx.h" #include "CpuTypes.h" +#include "PpuTypes.h" class Cpu; +class Ppu; class MemoryManager; enum class MemoryOperationType; @@ -12,7 +14,7 @@ class TraceLogger; struct DebugState { CpuState Cpu; - //PpuState ppuState; + PpuState Ppu; //ApuState apuState; }; @@ -20,6 +22,7 @@ class Debugger { private: shared_ptr _cpu; + shared_ptr _ppu; shared_ptr _memoryManager; shared_ptr _traceLogger; @@ -28,7 +31,7 @@ private: atomic _cpuStepCount; public: - Debugger(shared_ptr cpu, shared_ptr memoryManager); + Debugger(shared_ptr cpu, shared_ptr ppu, shared_ptr memoryManager); ~Debugger(); void ProcessCpuRead(uint32_t addr, uint8_t value, MemoryOperationType type); @@ -38,5 +41,7 @@ public: void Step(int32_t stepCount); bool IsExecutionStopped(); + void GetState(DebugState *state); + shared_ptr GetTraceLogger(); }; \ No newline at end of file diff --git a/Core/DisassemblyInfo.cpp b/Core/DisassemblyInfo.cpp index bda75f5..16f3928 100644 --- a/Core/DisassemblyInfo.cpp +++ b/Core/DisassemblyInfo.cpp @@ -1,8 +1,10 @@ #include "stdafx.h" +#include #include "DisassemblyInfo.h" #include "CpuTypes.h" #include "MemoryManager.h" #include "../Utilities/HexUtilities.h" +#include "../Utilities/FastString.h" DisassemblyInfo::DisassemblyInfo() { @@ -10,6 +12,8 @@ DisassemblyInfo::DisassemblyInfo() DisassemblyInfo::DisassemblyInfo(CpuState &state, MemoryManager *memoryManager) { + _flags = state.PS; + uint32_t addr = (state.K << 16) | state.PC; _byteCode[0] = memoryManager->Peek(addr); _addrMode = DisassemblyInfo::OpMode[_byteCode[0]]; @@ -19,17 +23,79 @@ DisassemblyInfo::DisassemblyInfo(CpuState &state, MemoryManager *memoryManager) _byteCode[i] = memoryManager->Peek(addr+i); } - _flags = state.PS; _emulationMode = state.EmulationMode; } -void DisassemblyInfo::GetDisassembly(string &out, MemoryManager *memoryManager) +void DisassemblyInfo::GetDisassembly(string &out, uint32_t memoryAddr) { - out += DisassemblyInfo::OpName[_byteCode[0]]; - out += _opSize > 1 ? " $" : " "; - for(int i = _opSize - 1; i > 0; i--) { - out += HexUtilities::ToHex(_byteCode[i]); + FastString str; + str.Write(DisassemblyInfo::OpName[_byteCode[0]]); + str.Write(' '); + + uint32_t opAddr = GetOperandAddress(memoryAddr); + + FastString operand; + if(_opSize > 1) { + operand.Write('$'); + operand.Write(HexUtilities::ToHex(opAddr)); } + + + switch(_addrMode) { + case AddrMode::Abs: str.Write(operand); break; + case AddrMode::AbsIdxXInd: str.Write('(', operand, ",X)"); break; + case AddrMode::AbsIdxX: str.Write(operand, ",X"); break; + case AddrMode::AbsIdxY: str.Write(operand, ",Y"); break; + case AddrMode::AbsInd: str.Write('(', operand, ')'); break; + case AddrMode::AbsIndLng: str.Write('[', operand, ']'); break; + case AddrMode::AbsLngIdxX: str.Write(operand, ",X"); break; + case AddrMode::AbsLng: str.Write(operand); break; + case AddrMode::Acc: break; + case AddrMode::BlkMov: str.Write(operand[0], operand[1], " <- ", operand[2], operand[3]); break; //TODO + case AddrMode::DirIdxIndX: str.Write('(', operand, ",X)"); break; + case AddrMode::DirIdxX: str.Write(operand, ",X"); break; + case AddrMode::DirIdxY: str.Write(operand, ",Y"); break; + case AddrMode::DirIndIdxY: str.Write("(", operand, "),Y"); break; + case AddrMode::DirIndLngIdxY: str.Write("[", operand, "],Y"); break; + case AddrMode::DirIndLng: str.Write("[", operand, "]"); break; + case AddrMode::DirInd: str.Write("(", operand, ")"); break; + case AddrMode::Dir: str.Write(operand); break; + + case AddrMode::Imm8: case AddrMode::ImmX: case AddrMode::ImmM: + str.Write('#', operand); + break; + + case AddrMode::Imp: break;; + case AddrMode::RelLng: str.Write(operand); + case AddrMode::Rel: str.Write(operand); + case AddrMode::Stk: break; + case AddrMode::StkRel: str.Write(operand, ",S"); break; + case AddrMode::StkRelIndIdxY: str.Write('(', operand, ",S),Y"); break; + } + + out += str.ToString(); +} + +uint32_t DisassemblyInfo::GetOperandAddress(uint32_t memoryAddr) +{ + uint32_t opAddr = 0; + if(_opSize == 2) { + opAddr = _byteCode[1]; + } else if(_opSize == 3) { + opAddr = _byteCode[1] | (_byteCode[2] << 8); + } else if(_opSize == 4) { + opAddr = _byteCode[1] | (_byteCode[2] << 8) | (_byteCode[3] << 16); + } + + if(_addrMode == AddrMode::Rel) { + if(_opSize == 2) { + opAddr = (int8_t)opAddr + memoryAddr + 2; + } else { + opAddr = (int16_t)opAddr + memoryAddr + 2; + } + } + + return opAddr; } uint8_t DisassemblyInfo::GetOperandSize() @@ -39,12 +105,16 @@ uint8_t DisassemblyInfo::GetOperandSize() case AddrMode::AbsIdxXInd: return 2; case AddrMode::AbsIdxX: return 2; case AddrMode::AbsIdxY: return 2; + case AddrMode::AbsInd: return 2; - case AddrMode::AbsIndLng: return 3; + case AddrMode::AbsIndLng: return 2; + case AddrMode::AbsLngIdxX: return 3; case AddrMode::AbsLng: return 3; + case AddrMode::Acc: return 0; case AddrMode::BlkMov: return 2; + case AddrMode::DirIdxIndX: return 1; case AddrMode::DirIdxX: return 1; case AddrMode::DirIdxY: return 1; @@ -53,12 +123,15 @@ uint8_t DisassemblyInfo::GetOperandSize() case AddrMode::DirIndLng: return 1; case AddrMode::DirInd: return 1; case AddrMode::Dir: return 1; + case AddrMode::Imm8: return 1; - case AddrMode::ImmX: return _flags & ProcFlags::IndexMode8 ? 1 : 2; - case AddrMode::ImmM: return _flags & ProcFlags::MemoryMode8 ? 1 : 2; + case AddrMode::ImmX: return (_flags & ProcFlags::IndexMode8) ? 1 : 2; + case AddrMode::ImmM: return (_flags & ProcFlags::MemoryMode8) ? 1 : 2; + case AddrMode::Imp: return 0; case AddrMode::RelLng: return 2; case AddrMode::Rel: return 1; + case AddrMode::Stk: return 0; //TODO case AddrMode::StkRel: return 1; case AddrMode::StkRelIndIdxY: return 1; @@ -69,8 +142,22 @@ uint8_t DisassemblyInfo::GetOperandSize() void DisassemblyInfo::GetByteCode(string &out) { + FastString str; for(int i = 0; i < _opSize; i++) { - out += "$" + HexUtilities::ToHex(_byteCode[i]) + ((i < _opSize - 1) ? " " : ""); + str.Write('$', HexUtilities::ToHex(_byteCode[i])); + if(i < _opSize - 1) { + str.Write(' '); + } + } + out += str.ToString(); +} + +int32_t DisassemblyInfo::GetEffectiveAddress(CpuState &cpuState, MemoryManager *memoryManager) +{ + uint32_t bank = (cpuState.K << 16); + //uint32_t opAddr = GetOperandAddress(); + switch(_addrMode) { + } } diff --git a/Core/DisassemblyInfo.h b/Core/DisassemblyInfo.h index 9b8e1d1..538b9e3 100644 --- a/Core/DisassemblyInfo.h +++ b/Core/DisassemblyInfo.h @@ -25,19 +25,17 @@ public: DisassemblyInfo(); DisassemblyInfo(CpuState &state, MemoryManager *memoryManager); - void GetDisassembly(string &out, MemoryManager* memoryManager); + void GetDisassembly(string &out, uint32_t memoryAddr); + uint32_t GetOperandAddress(uint32_t memoryAddr); uint8_t GetOperandSize(); void GetByteCode(string &out); - /* - int32_t GetEffectiveAddress(CpuState& cpuState, MemoryManager* memoryManager); - void GetEffectiveAddressString(string &out, CpuState& cpuState, MemoryManager* memoryManager); + + int32_t GetEffectiveAddress(CpuState &cpuState, MemoryManager *memoryManager); + void GetEffectiveAddressString(string &out, CpuState& cpuState, MemoryManager *memoryManager); /*int32_t GetMemoryValue(CpuState& cpuState, MemoryManager* memoryManager); uint16_t GetJumpDestination(uint16_t pc, MemoryManager* memoryManager); uint16_t GetIndirectJumpDestination(MemoryManager* memoryManager); - void ToString(string &out, uint32_t memoryAddr, MemoryManager* memoryManager, bool extendZeroPage); - void GetByteCode(string &out); - uint32_t GetSize(); - uint16_t GetOpAddr(uint16_t memoryAddr);*/ + */ }; diff --git a/Core/MemoryManager.h b/Core/MemoryManager.h index ada9b76..9f8f860 100644 --- a/Core/MemoryManager.h +++ b/Core/MemoryManager.h @@ -1,6 +1,7 @@ #pragma once #include "stdafx.h" #include "Console.h" +#include "Ppu.h" #include "../Utilities/HexUtilities.h" #include "../Utilities/VirtualFile.h" @@ -51,6 +52,28 @@ public: } }; +class CpuRegisterHandler : public IMemoryHandler +{ +private: + shared_ptr _ppu; + +public: + CpuRegisterHandler(shared_ptr ppu) + { + _ppu = ppu; + } + + uint8_t Read(uint32_t addr) override + { + return _ppu->Read(addr); + } + + void Write(uint32_t addr, uint8_t value) override + { + _ppu->Write(addr, value); + } +}; + class WorkRamHandler : public IMemoryHandler { private: @@ -82,12 +105,22 @@ private: IMemoryHandler* _handlers[0x100 * 0x10]; vector> _workRamHandlers; shared_ptr _cart; + shared_ptr _cpuRegisterHandler; + shared_ptr _ppu; + + uint64_t _masterClock; + uint64_t _lastMasterClock; public: MemoryManager(shared_ptr cart, shared_ptr console) { + _lastMasterClock = 0; + _masterClock = 0; _console = console; _cart = cart; + _ppu = console->GetPpu(); + + _cpuRegisterHandler.reset(new CpuRegisterHandler(_ppu)); memset(_handlers, 0, sizeof(_handlers)); _workRam = new uint8_t[128 * 1024]; @@ -96,6 +129,11 @@ public: RegisterHandler(0x7E0000 | i, 0x7E0000 | (i + 0xFFF), _workRamHandlers[_workRamHandlers.size() - 1].get()); } + for(int i = 0; i <= 0x3F; i++) { + RegisterHandler((i << 16) | 0x4000, (i << 16) | 0x5FFF, _cpuRegisterHandler.get()); + RegisterHandler(((i | 0x80) << 16) | 0x4000, ((i | 0x80) << 16) | 0x5FFF, _cpuRegisterHandler.get()); + } + RegisterHandler(0x0000, 0x0FFF, _workRamHandlers[0].get()); RegisterHandler(0x1000, 0x1FFF, _workRamHandlers[1].get()); @@ -120,8 +158,51 @@ public: } } + void IncrementMasterClock(uint32_t addr) + { + //This is incredibly inaccurate + uint8_t bank = (addr & 0xFF0000) >> 8; + if(bank >= 0x40 && bank <= 0x7F) { + //Slow + _masterClock += 8; + } else if(bank >= 0xCF) { + //Slow or fast (depending on register) + //Use slow + _masterClock += 8; + } else { + uint8_t page = (addr & 0xFF00) >> 8; + if(page <= 0x1F) { + //Slow + _masterClock += 6; + } else if(page >= 0x20 && page <= 0x3F) { + //Fast + _masterClock += 6; + } else if(page == 0x40 || page == 0x41) { + //extra slow + _masterClock += 12; + } else if(page >= 0x42 && page <= 0x5F) { + //Fast + _masterClock += 6; + } else if(page >= 0x60 && page <= 0x7F) { + //Slow + _masterClock += 8; + } else { + //Slow or fast (depending on register) + //Use slow + _masterClock += 8; + } + } + + while(_lastMasterClock < _masterClock - 3) { + _ppu->Exec(); + _lastMasterClock += 4; + } + } + uint8_t Read(uint32_t addr, MemoryOperationType type) { + IncrementMasterClock(addr); + uint8_t value = 0; if(_handlers[addr >> 12]) { value = _handlers[addr >> 12]->Read(addr); @@ -146,6 +227,8 @@ public: void Write(uint32_t addr, uint8_t value, MemoryOperationType type) { + IncrementMasterClock(addr); + _console->ProcessCpuWrite(addr, value, type); if(_handlers[addr >> 12]) { return _handlers[addr >> 12]->Write(addr, value); diff --git a/Core/Ppu.h b/Core/Ppu.h new file mode 100644 index 0000000..cc16bb3 --- /dev/null +++ b/Core/Ppu.h @@ -0,0 +1,65 @@ +#pragma once +#include "stdafx.h" +#include "PpuTypes.h" + +class Ppu +{ +private: + uint16_t _cycle = 0; + uint16_t _scanline = 0; + uint32_t _frameCount = 0; + bool _nmiFlag = false; + +public: + PpuState GetState() + { + return { + _cycle, + _scanline, + _frameCount + }; + } + + void Exec() + { + if(_cycle == 340) { + _cycle = 0; + _scanline++; + + if(_scanline == 225) { + _nmiFlag = true; + } + + if(_scanline == 260) { + _nmiFlag = false; + _scanline = 0; + _frameCount++; + } + } + + _cycle++; + } + + uint8_t Read(uint16_t addr) + { + switch(addr) { + case 0x4210: + return _nmiFlag ? 0x80 : 0; + break; + + case 0x4212: + return ( + (_scanline >= 225 ? 0x80 : 0) || + ((_cycle >= 0x121 || _cycle <= 0x15) ? 0x40 : 0) + ); + break; + } + + return 0; + } + + void Write(uint32_t addr, uint8_t value) + { + + } +}; \ No newline at end of file diff --git a/Core/PpuTypes.h b/Core/PpuTypes.h new file mode 100644 index 0000000..5305c51 --- /dev/null +++ b/Core/PpuTypes.h @@ -0,0 +1,9 @@ +#pragma once +#include "stdafx.h" + +struct PpuState +{ + uint16_t Cycle; + uint16_t Scanline; + uint32_t FrameCount; +}; diff --git a/Core/TraceLogger.cpp b/Core/TraceLogger.cpp index ffffb63..ed2e4da 100644 --- a/Core/TraceLogger.cpp +++ b/Core/TraceLogger.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include +#include #include "TraceLogger.h" #include "DisassemblyInfo.h" #include "Console.h" @@ -26,7 +27,7 @@ TraceLogger::~TraceLogger() template void TraceLogger::WriteValue(string &output, T value, RowPart& rowPart) { - string str = rowPart.DisplayInHex ? HexUtilities::ToHex((uint32_t)value) : std::to_string(value); + string str = rowPart.DisplayInHex ? HexUtilities::ToHex(value) : std::to_string(value); output += str; if(rowPart.MinWidth > (int)str.size()) { output += std::string(rowPart.MinWidth - str.size(), ' '); @@ -94,6 +95,10 @@ void TraceLogger::SetOptions(TraceLoggerOptions options) part.DataType = RowDataType::X; } else if(dataType == "Y") { part.DataType = RowDataType::Y; + } else if(dataType == "D") { + part.DataType = RowDataType::D; + } else if(dataType == "DB") { + part.DataType = RowDataType::DB; } else if(dataType == "P") { part.DataType = RowDataType::PS; } else if(dataType == "SP") { @@ -159,8 +164,8 @@ void TraceLogger::GetStatusFlag(string &output, uint8_t ps, RowPart& part) if(part.DisplayInHex) { WriteValue(output, ps, part); } else { - constexpr char activeStatusLetters[8] = { 'N', 'V', 'X', 'M', 'D', 'I', 'Z', 'C' }; - constexpr char inactiveStatusLetters[8] = { 'n', 'v', 'x', 'm', 'd', 'i', 'z', 'c' }; + constexpr char activeStatusLetters[8] = { 'N', 'V', 'M', 'X', 'D', 'I', 'Z', 'C' }; + constexpr char inactiveStatusLetters[8] = { 'n', 'v', 'm', 'x', 'd', 'i', 'z', 'c' }; string flags; for(int i = 0; i < 8; i++) { if(ps & 0x80) { @@ -174,9 +179,10 @@ void TraceLogger::GetStatusFlag(string &output, uint8_t ps, RowPart& part) } } -void TraceLogger::GetTraceRow(string &output, CpuState &cpuState, DisassemblyInfo &disassemblyInfo) +void TraceLogger::GetTraceRow(string &output, CpuState &cpuState, PpuState &ppuState, DisassemblyInfo &disassemblyInfo) { int originalSize = (int)output.size(); + uint32_t pcAddress = (cpuState.K << 16) | cpuState.PC; for(RowPart& rowPart : _rowParts) { switch(rowPart.DataType) { case RowDataType::Text: output += rowPart.Text; break; @@ -204,7 +210,7 @@ void TraceLogger::GetTraceRow(string &output, CpuState &cpuState, DisassemblyInf } //LabelManager* labelManager = _options.UseLabels ? _labelManager.get() : nullptr; - disassemblyInfo.GetDisassembly(code, _memoryManager.get()); + disassemblyInfo.GetDisassembly(code, pcAddress); WriteValue(output, code, rowPart); break; } @@ -233,16 +239,18 @@ void TraceLogger::GetTraceRow(string &output, CpuState &cpuState, DisassemblyInf } break; - case RowDataType::PC: WriteValue(output, (cpuState.K << 16) | cpuState.PC, rowPart); break; + case RowDataType::PC: WriteValue(output, pcAddress, rowPart); break; case RowDataType::A: WriteValue(output, cpuState.A, rowPart); break; case RowDataType::X: WriteValue(output, cpuState.X, rowPart); break; case RowDataType::Y: WriteValue(output, cpuState.Y, rowPart); break; + case RowDataType::D: WriteValue(output, cpuState.D, rowPart); break; + case RowDataType::DB: WriteValue(output, cpuState.DBR, rowPart); break; case RowDataType::SP: WriteValue(output, cpuState.SP, rowPart); break; case RowDataType::PS: GetStatusFlag(output, cpuState.PS, rowPart); break; - //case RowDataType::Cycle: WriteValue(output, ppuState.Cycle, rowPart); break; - //case RowDataType::Scanline: WriteValue(output, ppuState.Scanline, rowPart); break; - //case RowDataType::FrameCount: WriteValue(output, ppuState.FrameCount, rowPart); break; - case RowDataType::CycleCount: WriteValue(output, cpuState.CycleCount, rowPart); break; + case RowDataType::Cycle: WriteValue(output, ppuState.Cycle, rowPart); break; + case RowDataType::Scanline: WriteValue(output, ppuState.Scanline, rowPart); break; + case RowDataType::FrameCount: WriteValue(output, ppuState.FrameCount, rowPart); break; + case RowDataType::CycleCount: WriteValue(output, (uint32_t)cpuState.CycleCount, rowPart); break; } } output += _options.UseWindowsEol ? "\r\n" : "\n"; @@ -269,7 +277,7 @@ void TraceLogger::AddRow(DisassemblyInfo &disassemblyInfo, DebugState &state) { _disassemblyCache[_currentPos] = disassemblyInfo; _cpuStateCache[_currentPos] = state.Cpu; - //_ppuStateCache[_currentPos] = state.PPU; + _ppuStateCache[_currentPos] = state.Ppu; _pendingLog = false; if(_logCount < ExecutionLogSize) { @@ -277,7 +285,7 @@ void TraceLogger::AddRow(DisassemblyInfo &disassemblyInfo, DebugState &state) } if(_logToFile) { - GetTraceRow(_outputBuffer, state.Cpu, disassemblyInfo); + GetTraceRow(_outputBuffer, state.Cpu, state.Ppu, disassemblyInfo); if(_outputBuffer.size() > 32768) { _outputFile << _outputBuffer; _outputBuffer.clear(); @@ -314,7 +322,7 @@ const char* TraceLogger::GetExecutionTrace(uint32_t lineCount) auto lock = _lock.AcquireSafe(); lineCount = std::min(lineCount, _logCount); memcpy(_cpuStateCacheCopy, _cpuStateCache, sizeof(_cpuStateCache)); - //memcpy(_ppuStateCacheCopy, _ppuStateCache, sizeof(_ppuStateCache)); + memcpy(_ppuStateCacheCopy, _ppuStateCache, sizeof(_ppuStateCache)); memcpy(_disassemblyCacheCopy, _disassemblyCache, sizeof(_disassemblyCache)); startPos = _currentPos + ExecutionLogSize - lineCount; } @@ -325,7 +333,7 @@ const char* TraceLogger::GetExecutionTrace(uint32_t lineCount) string byteCode; _disassemblyCacheCopy[index].GetByteCode(byteCode); _executionTrace += byteCode + "\x1"; - GetTraceRow(_executionTrace, _cpuStateCacheCopy[index], _disassemblyCacheCopy[index]); + GetTraceRow(_executionTrace, _cpuStateCacheCopy[index], _ppuStateCacheCopy[index], _disassemblyCacheCopy[index]); } return _executionTrace.c_str(); diff --git a/Core/TraceLogger.h b/Core/TraceLogger.h index 39d476e..53f5d09 100644 --- a/Core/TraceLogger.h +++ b/Core/TraceLogger.h @@ -1,6 +1,7 @@ #pragma once #include "stdafx.h" #include "CpuTypes.h" +#include "PpuTypes.h" #include "DisassemblyInfo.h" #include "../Utilities/SimpleLock.h" @@ -32,6 +33,8 @@ enum class RowDataType A, X, Y, + D, + DB, SP, PS, Cycle, @@ -73,11 +76,11 @@ private: uint16_t _currentPos; uint32_t _logCount; CpuState _cpuStateCache[ExecutionLogSize] = {}; - //PPUDebugState _ppuStateCache[ExecutionLogSize] = {}; + PpuState _ppuStateCache[ExecutionLogSize] = {}; DisassemblyInfo _disassemblyCache[ExecutionLogSize]; CpuState _cpuStateCacheCopy[ExecutionLogSize] = {}; - //PPUDebugState _ppuStateCacheCopy[ExecutionLogSize] = {}; + PpuState _ppuStateCacheCopy[ExecutionLogSize] = {}; DisassemblyInfo _disassemblyCacheCopy[ExecutionLogSize]; SimpleLock _lock; @@ -86,7 +89,7 @@ private: void AddRow(DisassemblyInfo &disassemblyInfo, DebugState &state); //bool ConditionMatches(DebugState &state, DisassemblyInfo &disassemblyInfo, OperationInfo &operationInfo); - void GetTraceRow(string &output, CpuState &cpuState, DisassemblyInfo &disassemblyInfo); + void GetTraceRow(string &output, CpuState &cpuState, PpuState &ppuState, DisassemblyInfo &disassemblyInfo); template void WriteValue(string &output, T value, RowPart& rowPart); diff --git a/UI/Debugger/frmTraceLogger.cs b/UI/Debugger/frmTraceLogger.cs index 5112312..d6e05ff 100644 --- a/UI/Debugger/frmTraceLogger.cs +++ b/UI/Debugger/frmTraceLogger.cs @@ -21,6 +21,7 @@ namespace Mesen.GUI.Debugger private string _lastFilename; private EntityBinder _entityBinder = new EntityBinder(); private string _previousTrace; + private UInt64 _previousCycleCount; private volatile bool _refreshRunning; private bool _initialized; @@ -161,13 +162,12 @@ namespace Mesen.GUI.Debugger format += "[Align," + alignValue.ToString() + "] "; if(chkShowRegisters.Checked) { - format += "A:[A,h] X:[X,h] Y:[Y,h] "; + format += "A:[A,4h] X:[X,4h] Y:[Y,4h] S:[SP,4h] D:[D,4h] DB:[DB,2h] "; switch(cboStatusFlagFormat.GetEnumValue()) { - case StatusFlagFormat.Hexadecimal: format += "P:[P,h]"; break; - case StatusFlagFormat.CompactText: format += "P:[P]"; break; - case StatusFlagFormat.Text: format += "P:[P,8]"; break; + case StatusFlagFormat.Hexadecimal: format += "P:[P,h] "; break; + case StatusFlagFormat.CompactText: format += "P:[P] "; break; + case StatusFlagFormat.Text: format += "P:[P,8] "; break; } - format += " SP:[SP,h] "; } if(chkShowPpuCycles.Checked) { format += "CYC:[Cycle,3] "; @@ -271,12 +271,10 @@ namespace Mesen.GUI.Debugger Task.Run(() => { //Update trace log in another thread for performance //DebugState state = new DebugState(); - //InteropEmu.DebugGetState(ref state); - //if(_previousCycleCount != state.CPU.CycleCount || forceUpdate) { - //TODO - if(true) { + DebugState state = DebugApi.GetState(); + if(_previousCycleCount != state.Cpu.CycleCount || forceUpdate) { string newTrace = DebugApi.GetExecutionTrace((UInt32)_lineCount); - //_previousCycleCount = state.CPU.CycleCount; + _previousCycleCount = state.Cpu.CycleCount; _previousTrace = newTrace; int index = 0; @@ -367,7 +365,7 @@ namespace Mesen.GUI.Debugger } if(mnuAutoRefresh.Checked) { - RefreshLog(false, false); + RefreshLog(true, false); } } diff --git a/UI/Debugger/frmTraceLogger.resx b/UI/Debugger/frmTraceLogger.resx index 4068e7b..3b755a9 100644 --- a/UI/Debugger/frmTraceLogger.resx +++ b/UI/Debugger/frmTraceLogger.resx @@ -121,12 +121,12 @@ 17, 17 - 353, 17 + 17, 134 - 107, 17 + 17, 56 - 238, 17 + 17, 95 \ No newline at end of file diff --git a/UI/Forms/frmMain.Designer.cs b/UI/Forms/frmMain.Designer.cs index 695cda2..2cf7c97 100644 --- a/UI/Forms/frmMain.Designer.cs +++ b/UI/Forms/frmMain.Designer.cs @@ -32,11 +32,12 @@ this.mnuFile = new System.Windows.Forms.ToolStripMenuItem(); this.mnuOpen = new System.Windows.Forms.ToolStripMenuItem(); this.debugToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuRun = new System.Windows.Forms.ToolStripMenuItem(); this.mnuStep = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); this.mnuDebugger = new System.Windows.Forms.ToolStripMenuItem(); this.mnuTraceLogger = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuRun = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuRun100Instructions = new System.Windows.Forms.ToolStripMenuItem(); this.mnuMain.SuspendLayout(); this.SuspendLayout(); // @@ -70,7 +71,7 @@ // this.mnuOpen.Image = global::Mesen.GUI.Properties.Resources.Folder; this.mnuOpen.Name = "mnuOpen"; - this.mnuOpen.Size = new System.Drawing.Size(152, 22); + this.mnuOpen.Size = new System.Drawing.Size(103, 22); this.mnuOpen.Text = "Open"; this.mnuOpen.Click += new System.EventHandler(this.mnuOpen_Click); // @@ -79,6 +80,7 @@ this.debugToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuRun, this.mnuStep, + this.mnuRun100Instructions, this.toolStripMenuItem1, this.mnuDebugger, this.mnuTraceLogger}); @@ -86,38 +88,47 @@ this.debugToolStripMenuItem.Size = new System.Drawing.Size(54, 20); this.debugToolStripMenuItem.Text = "Debug"; // + // mnuRun + // + this.mnuRun.Name = "mnuRun"; + this.mnuRun.ShortcutKeys = System.Windows.Forms.Keys.F5; + this.mnuRun.Size = new System.Drawing.Size(157, 22); + this.mnuRun.Text = "Run"; + this.mnuRun.Click += new System.EventHandler(this.mnuRun_Click); + // // mnuStep // this.mnuStep.Name = "mnuStep"; this.mnuStep.ShortcutKeys = System.Windows.Forms.Keys.F11; - this.mnuStep.Size = new System.Drawing.Size(152, 22); + this.mnuStep.Size = new System.Drawing.Size(157, 22); this.mnuStep.Text = "Step"; this.mnuStep.Click += new System.EventHandler(this.mnuStep_Click); // + // toolStripMenuItem1 + // + this.toolStripMenuItem1.Name = "toolStripMenuItem1"; + this.toolStripMenuItem1.Size = new System.Drawing.Size(154, 6); + // // mnuDebugger // this.mnuDebugger.Name = "mnuDebugger"; - this.mnuDebugger.Size = new System.Drawing.Size(152, 22); + this.mnuDebugger.Size = new System.Drawing.Size(157, 22); this.mnuDebugger.Text = "Debugger"; // // mnuTraceLogger // this.mnuTraceLogger.Name = "mnuTraceLogger"; - this.mnuTraceLogger.Size = new System.Drawing.Size(152, 22); + this.mnuTraceLogger.Size = new System.Drawing.Size(157, 22); this.mnuTraceLogger.Text = "Trace Logger"; this.mnuTraceLogger.Click += new System.EventHandler(this.mnuTraceLogger_Click); // - // toolStripMenuItem1 + // mnuRun100Instructions // - this.toolStripMenuItem1.Name = "toolStripMenuItem1"; - this.toolStripMenuItem1.Size = new System.Drawing.Size(149, 6); - // - // mnuRun - // - this.mnuRun.Name = "mnuRun"; - this.mnuRun.Size = new System.Drawing.Size(152, 22); - this.mnuRun.Text = "Run"; - this.mnuRun.Click += new System.EventHandler(this.mnuRun_Click); + this.mnuRun100Instructions.Name = "mnuRun100Instructions"; + this.mnuRun100Instructions.ShortcutKeys = System.Windows.Forms.Keys.F6; + this.mnuRun100Instructions.Size = new System.Drawing.Size(157, 22); + this.mnuRun100Instructions.Text = "Run 100 ops"; + this.mnuRun100Instructions.Click += new System.EventHandler(this.mnuRun100Instructions_Click); // // frmMain // @@ -148,5 +159,6 @@ private System.Windows.Forms.ToolStripMenuItem mnuOpen; private System.Windows.Forms.ToolStripMenuItem mnuRun; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem mnuRun100Instructions; } } \ No newline at end of file diff --git a/UI/Forms/frmMain.cs b/UI/Forms/frmMain.cs index 3fa7da7..0a35597 100644 --- a/UI/Forms/frmMain.cs +++ b/UI/Forms/frmMain.cs @@ -58,5 +58,10 @@ namespace Mesen.GUI.Forms { DebugApi.ResumeExecution(); } + + private void mnuRun100Instructions_Click(object sender, EventArgs e) + { + DebugApi.Step(100); + } } } diff --git a/UI/Interop/DebugApi.cs b/UI/Interop/DebugApi.cs index 0233db1..6926793 100644 --- a/UI/Interop/DebugApi.cs +++ b/UI/Interop/DebugApi.cs @@ -19,7 +19,7 @@ namespace Mesen.GUI [DllImport(DllPath)] public static extern void ReleaseDebugger(); [DllImport(DllPath)] public static extern void ResumeExecution(); - [DllImport(DllPath)] public static extern void Step(Int32 scanCode); + [DllImport(DllPath)] public static extern void Step(Int32 instructionCount); [DllImport(DllPath)] public static extern void StartTraceLogger([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string filename); [DllImport(DllPath)] public static extern void StopTraceLogger(); @@ -27,6 +27,45 @@ namespace Mesen.GUI [DllImport(DllPath, EntryPoint = "GetExecutionTrace")] private static extern IntPtr GetExecutionTraceWrapper(UInt32 lineCount); public static string GetExecutionTrace(UInt32 lineCount) { return Utf8Marshaler.PtrToStringUtf8(DebugApi.GetExecutionTraceWrapper(lineCount)); } + + [DllImport(DllPath, EntryPoint = "GetState")] private static extern void GetStateWrapper(ref DebugState state); + public static DebugState GetState() + { + DebugState state = new DebugState(); + DebugApi.GetStateWrapper(ref state); + return state; + } + } + + public struct CpuState + { + public UInt64 CycleCount; + + public UInt16 A; + public UInt16 X; + public UInt16 Y; + + public UInt16 SP; + public UInt16 D; + public UInt16 PC; + + public byte K; + public byte DBR; + public byte PS; + [MarshalAs(UnmanagedType.I1)] public bool EmulationMode; + }; + + public struct PpuState + { + public UInt16 Cycle; + public UInt16 Scanline; + public UInt32 FrameCount; + }; + + public struct DebugState + { + public CpuState Cpu; + public PpuState Ppu; } [Serializable]