diff --git a/Intel8080/inc/Intel8080.h b/Intel8080/inc/Intel8080.h index ba976b3..989e418 100644 --- a/Intel8080/inc/Intel8080.h +++ b/Intel8080/inc/Intel8080.h @@ -79,8 +79,7 @@ namespace EightBit { } void adjustReservedFlags() { - F() &= ~(Bit5 | Bit3); - F() |= Bit1; + F() = (F() | Bit1) & ~(Bit5 | Bit3); } static void adjustAuxiliaryCarryAdd(uint8_t& f, uint8_t before, uint8_t value, int calculation) { @@ -150,9 +149,9 @@ namespace EightBit { void dad(uint16_t value) { auto& f = F(); - uint32_t sum = HL().word + value; - setFlag(f, CF, sum > 0xffff); - HL().word = (uint16_t)sum; + auto sum = HL().word + value; + setFlag(f, CF, sum & Bit16); + HL().word = sum; } void sub(uint8_t& operand, uint8_t value, int carry = 0) { @@ -275,9 +274,9 @@ namespace EightBit { m_memory.reference() = data; } - void lxi_b() { Processor::fetchWord(BC()); } - void lxi_d() { Processor::fetchWord(DE()); } - void lxi_h() { Processor::fetchWord(HL()); } + void lxi_b() { fetchWord(BC()); } + void lxi_d() { fetchWord(DE()); } + void lxi_h() { fetchWord(HL()); } void stax_r(register16_t& destination) { m_memory.ADDRESS() = destination; @@ -351,7 +350,7 @@ namespace EightBit { } void lxi_sp() { - Processor::fetchWord(SP()); + fetchWord(SP()); } void inx_sp() { ++SP().word; } @@ -527,13 +526,11 @@ namespace EightBit { } void sbi() { - auto value = fetchByte(); - sbb(value); + sbb(fetchByte()); } void sui() { - auto value = fetchByte(); - sub(value); + sub(fetchByte()); } // logical @@ -603,35 +600,31 @@ namespace EightBit { void rlc() { auto& a = A(); auto carry = a & Bit7; - a <<= 1; - carry ? a |= Bit0 : a &= ~Bit0; + a = (a << 1) | (carry >> 7); setFlag(F(), CF, carry); } void rrc() { auto& a = A(); auto carry = a & Bit0; - a >>= 1; - carry ? a |= Bit7 : a &= ~Bit7; + a = (a >> 1) | (carry << 7); setFlag(F(), CF, carry); } void ral() { auto& a = A(); auto& f = F(); - auto carry = a & Bit7; - a <<= 1; - a |= (f & CF); - setFlag(f, CF, carry); + const auto carry = f & CF; + setFlag(f, CF, a & Bit7); + a = (a << 1) | carry; } void rar() { auto& a = A(); auto& f = F(); - auto carry = a & 1; - a >>= 1; - a |= (f & CF) << 7; - setFlag(f, CF, carry); + const auto carry = f & CF; + setFlag(f, CF, a & Bit0); + a = (a >> 1) | (carry << 7); } // specials diff --git a/Intel8080/test/Board.h b/Intel8080/test/Board.h index fa96247..85aea27 100644 --- a/Intel8080/test/Board.h +++ b/Intel8080/test/Board.h @@ -14,9 +14,8 @@ class Board { public: Board(const Configuration& configuration); - EightBit::Memory& getMemory() { return m_memory; } - const EightBit::Intel8080& getCPU() const { return m_cpu; } - EightBit::Intel8080& getCPUMutable() { return m_cpu; } + EightBit::Memory& Memory() { return m_memory; } + EightBit::Intel8080& CPU() { return m_cpu; } void initialise(); diff --git a/LR35902/src/LR35902.cpp b/LR35902/src/LR35902.cpp index 4e02ee5..248a533 100644 --- a/LR35902/src/LR35902.cpp +++ b/LR35902/src/LR35902.cpp @@ -524,7 +524,7 @@ void EightBit::LR35902::executeOther(int x, int y, int z, int p, int q) { case 1: // 16-bit load immediate/add switch (q) { case 0: // LD rp,nn - Processor::fetchWord(RP(p)); + fetchWord(RP(p)); cycles += 3; break; case 1: // ADD HL,rp diff --git a/LR35902/src/LR35902.vcxproj b/LR35902/src/LR35902.vcxproj index ac9825d..d97bb84 100644 --- a/LR35902/src/LR35902.vcxproj +++ b/LR35902/src/LR35902.vcxproj @@ -22,7 +22,6 @@ {7730ECF5-8206-4ECB-BA6B-6B5CFE50CFB2} Win32Proj LR35902 - 8.1 diff --git a/M6502/inc/AddressEventArgs.h b/M6502/inc/AddressEventArgs.h deleted file mode 100644 index b13d738..0000000 --- a/M6502/inc/AddressEventArgs.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include - -class AddressEventArgs -{ -private: - uint16_t m_address; - uint8_t m_cell; - -public: - AddressEventArgs(uint16_t address, uint8_t cell) - : m_address(address), m_cell(cell) {} - - uint16_t getAddress() const { return m_address; } - uint8_t getCell() const { return m_cell; } -}; diff --git a/M6502/inc/AddressingMode.h b/M6502/inc/AddressingMode.h deleted file mode 100644 index 1f4db11..0000000 --- a/M6502/inc/AddressingMode.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -enum AddressingMode { - Illegal, - Implied, - Accumulator, - Immediate, - Relative, - XIndexed, - IndexedY, - ZeroPage, - ZeroPageX, - ZeroPageY, - Absolute, - AbsoluteX, - AbsoluteY, - AbsoluteXIndirect, - Indirect, - ZeroPageIndirect, - ZeroPageRelative -}; diff --git a/M6502/inc/AddressingModeDumper.h b/M6502/inc/AddressingModeDumper.h deleted file mode 100644 index 57da6c7..0000000 --- a/M6502/inc/AddressingModeDumper.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include -#include - -typedef std::function dumper_t; - -struct AddressingModeDumper { - dumper_t byteDumper; - dumper_t disassemblyDumper; -}; diff --git a/M6502/inc/Disassembly.h b/M6502/inc/Disassembly.h index 7239730..02b4722 100644 --- a/M6502/inc/Disassembly.h +++ b/M6502/inc/Disassembly.h @@ -3,62 +3,326 @@ #include #include -#include -#include - +#include "mos6502.h" #include "Symbols.h" -#include "AddressingModeDumper.h" -class Disassembly -{ -public: - MOS6502& processor; - const Symbols& symbols; +namespace EightBit { + class Disassembly { + public: + Disassembly(MOS6502& processor, const Symbols& symbols); - std::map dumpers; + std::string dump_Flags(uint8_t value) const; + std::string dump_ByteValue(uint8_t value) const; + std::string dump_WordValue(uint16_t value) const; - Disassembly(MOS6502& processor, const Symbols& symbols); + std::string disassemble(uint16_t current) const; - std::string Dump_ByteValue(uint8_t value) const; - std::string Dump_WordValue(uint16_t value) const; + private: + MOS6502& processor; + const Symbols& symbols; - std::string DumpBytes(AddressingMode mode, uint16_t current) const; - std::string Disassemble(uint16_t current) const; - std::string DumpOperand(AddressingMode mode, uint16_t current) const; + mutable uint16_t m_address; -private: - uint8_t GetByte(uint16_t address) const; - uint16_t GetWord(uint16_t address) const; + std::string disassemble_Implied(const std::string& instruction) const { + return "\t" + instruction; + } - std::string Dump_Nothing(uint16_t unused) const; - std::string Dump_Byte(uint16_t address) const; - std::string Dump_DByte(uint16_t address) const; + std::string disassemble_Absolute(const std::string& instruction) const { + return AM_Absolute_dump() + "\t" + instruction + " " + AM_Absolute(); + } - std::string ConvertAddress(uint16_t address) const; - std::string ConvertAddress(uint8_t address) const; - std::string ConvertConstant(uint16_t constant) const; - std::string ConvertConstant(uint8_t constant) const; + std::string disassemble_Indirect(const std::string& instruction) const { + return AM_Absolute_dump() + "\t" + instruction + " (" + AM_Absolute() + ")"; + } - std::string Dump_A(uint16_t unused) const; - std::string Dump_imm(uint16_t current) const; - std::string Dump_abs(uint16_t current) const; - std::string Dump_zp(uint16_t current) const; - std::string Dump_zpx(uint16_t current) const; - std::string Dump_zpy(uint16_t current) const; - std::string Dump_absx(uint16_t current) const; - std::string Dump_absy(uint16_t current) const; - std::string Dump_absxind(uint16_t current) const; - std::string Dump_xind(uint16_t current) const; - std::string Dump_indy(uint16_t current) const; - std::string Dump_ind(uint16_t current) const; - std::string Dump_zpind(uint16_t current) const; - std::string Dump_rel(uint16_t current) const; - std::string Dump_zprel(uint16_t current) const; + std::string disassemble_Relative(const std::string& instruction, uint16_t address) const { + return AM_Immediate_dump() + "\t" + instruction + " $" + dump_WordValue(address); + } - const AddressingModeDumper& getDumper(AddressingMode mode) const { - auto dumper = dumpers.find(mode); - if (dumper == dumpers.end()) - throw std::invalid_argument("Illegal addressing mode"); - return dumper->second; - } -}; \ No newline at end of file + std::string disassemble_AM_00(int bbb, const std::string& instruction) const { + return AM_00_dump(bbb) + "\t" + instruction + " " + AM_00(bbb); + } + + std::string disassemble_AM_01(int bbb, const std::string& instruction) const { + return AM_01_dump(bbb) + "\t" + instruction + " " + AM_01(bbb); + } + + std::string disassemble_AM_10(int bbb, const std::string& instruction) const { + return AM_10_dump(bbb) + "\t" + instruction + " " + AM_10(bbb); + } + + std::string disassemble_AM_10_x(int bbb, const std::string& instruction) const { + return AM_10_x_dump(bbb) + "\t" + instruction + " " + AM_10_x(bbb); + } + +#pragma region 6502 addressing modes + +#pragma region References + + std::string AM_Immediate_dump() const { + return dump_Byte(m_address + 1); + } + + std::string AM_Immediate() const { + return "#" + AM_Immediate_dump(); + } + + std::string AM_Absolute_dump() const { + return dump_DByte(m_address + 1); + } + + std::string AM_Absolute() const { + return "$" + dump_WordValue(processor.getMemory().peekWord(m_address + 1)); + } + + std::string AM_ZeroPage_dump() const { + return dump_Byte(m_address + 1); + } + + std::string AM_ZeroPage() const { + return "$" + dump_Byte(m_address + 1); + } + + std::string AM_ZeroPageX_dump() const { + return AM_ZeroPage_dump(); + } + + std::string AM_ZeroPageX() const { + return AM_ZeroPage() + ",X"; + } + + std::string AM_ZeroPageY_dump() const { + return AM_ZeroPage_dump(); + } + + std::string AM_ZeroPageY() const { + return AM_ZeroPage() + ",Y"; + } + + std::string AM_AbsoluteX_dump() const { + return AM_Absolute_dump(); + } + + std::string AM_AbsoluteX() const { + return AM_Absolute() + ",X"; + } + + std::string AM_AbsoluteY_dump() const { + return AM_Absolute_dump(); + } + + std::string AM_AbsoluteY() const { + return AM_Absolute() + ",Y"; + } + + std::string AM_IndexedIndirectX_dump() const { + return AM_ZeroPage_dump(); + } + + std::string AM_IndexedIndirectX() const { + return "($" + dump_Byte(m_address + 1) + ",X)"; + } + + std::string AM_IndirectIndexedY_dump() const { + return AM_ZeroPage_dump(); + } + + std::string AM_IndirectIndexedY() const { + return "($" + dump_Byte(m_address + 1) + "),Y"; + } + +#pragma endregion References + +#pragma region 6502 addressing mode switching + + std::string AM_00_dump(int bbb) const { + switch (bbb) { + case 0b000: + return AM_Immediate_dump(); + case 0b001: + return AM_ZeroPage_dump(); + case 0b011: + return AM_Absolute_dump(); + case 0b101: + return AM_ZeroPageX_dump(); + case 0b111: + return AM_AbsoluteX_dump(); + case 0b010: + case 0b100: + case 0b110: + throw std::domain_error("Illegal addressing mode"); + default: + __assume(0); + } + } + + std::string AM_00(int bbb) const { + switch (bbb) { + case 0b000: + return AM_Immediate(); + case 0b001: + return AM_ZeroPage(); + case 0b011: + return AM_Absolute(); + case 0b101: + return AM_ZeroPageX(); + case 0b111: + return AM_AbsoluteX(); + case 0b010: + case 0b100: + case 0b110: + throw std::domain_error("Illegal addressing mode"); + default: + __assume(0); + } + } + + std::string AM_01_dump(int bbb) const { + switch (bbb) { + case 0b000: + return AM_IndexedIndirectX_dump(); + case 0b001: + return AM_ZeroPage_dump(); + case 0b010: + return AM_Immediate_dump(); + case 0b011: + return AM_Absolute_dump(); + case 0b100: + return AM_IndirectIndexedY_dump(); + case 0b101: + return AM_ZeroPageX_dump(); + case 0b110: + return AM_AbsoluteY_dump(); + case 0b111: + return AM_AbsoluteX_dump(); + default: + __assume(0); + } + } + + std::string AM_01(int bbb) const { + switch (bbb) { + case 0b000: + return AM_IndexedIndirectX(); + case 0b001: + return AM_ZeroPage(); + case 0b010: + return AM_Immediate(); + case 0b011: + return AM_Absolute(); + case 0b100: + return AM_IndirectIndexedY(); + case 0b101: + return AM_ZeroPageX(); + case 0b110: + return AM_AbsoluteY(); + case 0b111: + return AM_AbsoluteX(); + default: + __assume(0); + } + } + + std::string AM_10_dump(int bbb) const { + switch (bbb) { + case 0b000: + return AM_Immediate_dump(); + case 0b001: + return AM_ZeroPage_dump(); + case 0b010: + return ""; + case 0b011: + return AM_Absolute_dump(); + case 0b101: + return AM_ZeroPageX_dump(); + case 0b111: + return AM_AbsoluteX_dump(); + case 0b100: + case 0b110: + throw std::domain_error("Illegal addressing mode"); + default: + __assume(0); + } + } + + std::string AM_10(int bbb) const { + switch (bbb) { + case 0b000: + return AM_Immediate(); + case 0b001: + return AM_ZeroPage(); + case 0b010: + return "A"; + case 0b011: + return AM_Absolute(); + case 0b101: + return AM_ZeroPageX(); + case 0b111: + return AM_AbsoluteX(); + case 0b100: + case 0b110: + throw std::domain_error("Illegal addressing mode"); + default: + __assume(0); + } + } + + std::string AM_10_x_dump(int bbb) const { + switch (bbb) { + case 0b000: + return AM_Immediate_dump(); + case 0b001: + return AM_ZeroPage_dump(); + case 0b010: + return ""; + case 0b011: + return AM_Absolute_dump(); + case 0b101: + return AM_ZeroPageY_dump(); + case 0b111: + return AM_AbsoluteY_dump(); + case 0b100: + case 0b110: + throw std::domain_error("Illegal addressing mode"); + default: + __assume(0); + } + } + + std::string AM_10_x(int bbb) const { + switch (bbb) { + case 0b000: + return AM_Immediate(); + case 0b001: + return AM_ZeroPage(); + case 0b010: + return "A"; + case 0b011: + return AM_Absolute(); + case 0b101: + return AM_ZeroPageY(); + case 0b111: + return AM_AbsoluteY(); + case 0b100: + case 0b110: + throw std::domain_error("Illegal addressing mode"); + default: + __assume(0); + } + } + +#pragma endregion 6502 addressing mode switching + +#pragma endregion 6502 addressing modes + + uint8_t getByte(uint16_t address) const; + + std::string dump_Byte(uint16_t address) const; + std::string dump_DByte(uint16_t address) const; + + std::string convertAddress(uint16_t address) const; + std::string convertAddress(uint8_t address) const; + std::string convertConstant(uint16_t constant) const; + std::string convertConstant(uint8_t constant) const; + }; +} \ No newline at end of file diff --git a/M6502/inc/DisassemblyEventArgs.h b/M6502/inc/DisassemblyEventArgs.h index a99642e..2d8015a 100644 --- a/M6502/inc/DisassemblyEventArgs.h +++ b/M6502/inc/DisassemblyEventArgs.h @@ -2,14 +2,15 @@ #include -class DisassemblyEventArgs -{ -private: - std::string m_output; +namespace EightBit { + class DisassemblyEventArgs { + private: + std::string m_output; -public: - DisassemblyEventArgs(std::string output) - : m_output(output) {} + public: + DisassemblyEventArgs(std::string output) + : m_output(output) {} - const std::string& getOutput() const { return m_output; } -}; \ No newline at end of file + const std::string& getOutput() const { return m_output; } + }; +} \ No newline at end of file diff --git a/M6502/inc/Memory.h b/M6502/inc/Memory.h deleted file mode 100644 index 5166b3e..0000000 --- a/M6502/inc/Memory.h +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "Signal.h" -#include "AddressEventArgs.h" - -class Memory -{ -private: - std::vector memory; - std::vector locked; - -public: - Memory(unsigned memorySize) - : memory(memorySize), - locked(memorySize) {} - - Signal InvalidWriteAttempt; - Signal WritingByte; - Signal ReadingByte; - - void ClearMemory() { - std::fill(memory.begin(), memory.end(), 0); - } - - void ClearLocking() { - std::fill(locked.begin(), locked.end(), false); - } - - uint8_t GetByte(uint16_t offset) const { - auto content = memory[offset]; - ReadingByte.fire(AddressEventArgs(offset, content)); - return content; - } - - void SetByte(uint16_t offset, uint8_t value) { - AddressEventArgs e(offset, value); - if (locked[offset]) { - InvalidWriteAttempt.fire(e); - } else { - memory[offset] = value; - WritingByte.fire(e); - } - } - - void LoadRom(std::string path, uint16_t offset) { - auto length = LoadMemory(path, offset); - LockMemory(offset, length); - } - - void LoadRam(std::string path, uint16_t offset) { - LoadMemory(path, offset); - } - - void LockMemory(uint16_t offset, uint16_t length) { - for (auto i = 0; i < length; ++i) - locked[offset + i] = true; - } - - uint16_t LoadMemory(std::string path, uint16_t offset) { - std::ifstream file(path, std::ios::binary | std::ios::ate); - auto size = (int)file.tellg(); - file.seekg(0, std::ios::beg); - std::vector buffer(size); - file.read(&buffer[0], size); - file.close(); - - std::copy(buffer.begin(), buffer.end(), memory.begin() + offset); - - return (uint16_t)size; - } -}; diff --git a/M6502/inc/ProcessorType.h b/M6502/inc/ProcessorType.h deleted file mode 100644 index 3471cd6..0000000 --- a/M6502/inc/ProcessorType.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -enum ProcessorType { - Cpu6502, - Cpu65SC02, - Cpu65C02 -}; \ No newline at end of file diff --git a/M6502/inc/ProfileEventArgs.h b/M6502/inc/ProfileEventArgs.h index cb9b8b8..d3b01a8 100644 --- a/M6502/inc/ProfileEventArgs.h +++ b/M6502/inc/ProfileEventArgs.h @@ -2,14 +2,15 @@ #include -class ProfileEventArgs -{ -private: - std::string m_output; +namespace EightBit { + class ProfileEventArgs { + private: + std::string m_output; -public: - ProfileEventArgs(std::string output) - : m_output(output) {} + public: + ProfileEventArgs(std::string output) + : m_output(output) {} - const std::string& getOutput() const { return m_output; } -}; \ No newline at end of file + const std::string& getOutput() const { return m_output; } + }; +} \ No newline at end of file diff --git a/M6502/inc/ProfileLineEventArgs.h b/M6502/inc/ProfileLineEventArgs.h index 31e1682..81b5f20 100644 --- a/M6502/inc/ProfileLineEventArgs.h +++ b/M6502/inc/ProfileLineEventArgs.h @@ -3,16 +3,18 @@ #include #include -class ProfileLineEventArgs -{ -private: - std::string m_source; - uint64_t m_cycles; +namespace EightBit { + class ProfileLineEventArgs + { + private: + std::string m_source; + uint64_t m_cycles; -public: - ProfileLineEventArgs(std::string source, uint64_t cycles) - : m_source(source), m_cycles(cycles) {} + public: + ProfileLineEventArgs(std::string source, uint64_t cycles) + : m_source(source), m_cycles(cycles) {} - const std::string& getSource() const { return m_source; } - uint64_t getCycles() const { return m_cycles; } -}; \ No newline at end of file + const std::string& getSource() const { return m_source; } + uint64_t getCycles() const { return m_cycles; } + }; +} \ No newline at end of file diff --git a/M6502/inc/ProfileScopeEventArgs.h b/M6502/inc/ProfileScopeEventArgs.h index 2f787f1..2f3c3e6 100644 --- a/M6502/inc/ProfileScopeEventArgs.h +++ b/M6502/inc/ProfileScopeEventArgs.h @@ -3,19 +3,19 @@ #include #include -class ProfileScopeEventArgs -{ -private: - std::string m_scope; - uint64_t m_cycles; - uint64_t m_count; +namespace EightBit { + class ProfileScopeEventArgs { + private: + std::string m_scope; + uint64_t m_cycles; + uint64_t m_count; -public: - ProfileScopeEventArgs(std::string scope, uint64_t cycles, uint64_t count) - : m_scope(scope), m_cycles(cycles), m_count(count) {} - - const std::string& getScope() const { return m_scope; } - uint64_t getCycles() const { return m_cycles; } - uint64_t getCount() const { return m_count; } -}; + public: + ProfileScopeEventArgs(std::string scope, uint64_t cycles, uint64_t count) + : m_scope(scope), m_cycles(cycles), m_count(count) {} + const std::string& getScope() const { return m_scope; } + uint64_t getCycles() const { return m_cycles; } + uint64_t getCount() const { return m_count; } + }; +} \ No newline at end of file diff --git a/M6502/inc/Profiler.h b/M6502/inc/Profiler.h index c59cf26..c689b4f 100644 --- a/M6502/inc/Profiler.h +++ b/M6502/inc/Profiler.h @@ -5,58 +5,55 @@ #include #include -#include -#include -#include +#include "EventArgs.h" +#include "Signal.h" #include "Disassembly.h" #include "Symbols.h" +#include "AddressEventArgs.h" #include "ProfileLineEventArgs.h" #include "ProfileScopeEventArgs.h" -class Profiler -{ -public: - std::array instructionCounts; - std::array addressProfiles; - std::array addressCounts; +#include "mos6502.h" - std::array addressScopes; - std::map scopeCycles; +namespace EightBit { + class Profiler { + public: + std::array instructionCounts; + std::array addressProfiles; + std::array addressCounts; - System6502& processor; - const Disassembly& disassembler; - const Symbols& symbols; + std::array addressScopes; + std::map scopeCycles; - bool countInstructions; - bool profileAddresses; + MOS6502& processor; + const Disassembly& disassembler; + const Symbols& symbols; - uint64_t priorCycleCount = 0; + Profiler(MOS6502& processor, Disassembly& disassembler, Symbols& symbols); - Profiler(System6502& processor, Disassembly& disassembler, Symbols& symbols, bool countInstructions, bool profileAddresses); + Signal StartingOutput; + Signal FinishedOutput; - Signal StartingOutput; - Signal FinishedOutput; + Signal StartingLineOutput; + Signal FinishedLineOutput; - Signal StartingLineOutput; - Signal FinishedLineOutput; + Signal EmitLine; - Signal EmitLine; + Signal StartingScopeOutput; + Signal FinishedScopeOutput; - Signal StartingScopeOutput; - Signal FinishedScopeOutput; + Signal EmitScope; - Signal EmitScope; + void addInstruction(uint8_t instruction); + void addAddress(uint16_t address, int cycles); - void Generate(); + void Generate(); -private: - void EmitProfileInformation(); + private: + void EmitProfileInformation(); - void Processor_ExecutingInstruction_ProfileAddresses(const AddressEventArgs& addressEvent); - void Processor_ExecutingInstruction_CountInstructions(const AddressEventArgs& addressEvent); - void Processor_ExecutedInstruction_ProfileAddresses(const AddressEventArgs& addressEvent); - - void BuildAddressScopes(); -}; + void BuildAddressScopes(); + }; +} \ No newline at end of file diff --git a/M6502/inc/StatusFlags.h b/M6502/inc/StatusFlags.h deleted file mode 100644 index 7526e4d..0000000 --- a/M6502/inc/StatusFlags.h +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#include -#include - -struct StatusFlags { - - bool negative; - bool overflow; - bool reserved; - bool brk; - bool decimal; - bool interrupt; - bool zero; - bool carry; - - enum StatusBits { - Negative = 0x80, // N - Overflow = 0x40, // V - Reserved = 0x20, // ignored - Break = 0x10, // B - Decimal = 0x08, // D (use BCD for arithmetic) - Interrupt = 0x04, // I (IRQ disable) - Zero = 0x02, // Z - Carry = 0x01, // C - }; - - StatusFlags(uint8_t value) { - negative = (value & StatusBits::Negative) != 0; - overflow = (value & StatusBits::Overflow) != 0; - reserved = (value & StatusBits::Reserved) != 0; - brk = (value & StatusBits::Break) != 0; - decimal = (value & StatusBits::Decimal) != 0; - interrupt = (value & StatusBits::Interrupt) != 0; - zero = (value & StatusBits::Zero) != 0; - carry = (value & StatusBits::Carry) != 0; - } - - operator uint8_t() const { - - uint8_t flags = 0; - - if (negative) - flags |= StatusBits::Negative; - - if (overflow) - flags |= StatusBits::Overflow; - - if (reserved) - flags |= StatusBits::Reserved; - - if (brk) - flags |= StatusBits::Break; - - if (decimal) - flags |= StatusBits::Decimal; - - if (interrupt) - flags |= StatusBits::Interrupt; - - if (zero) - flags |= StatusBits::Zero; - - if (carry) - flags |= StatusBits::Carry; - - return flags; - } - - operator std::string() const { - std::string returned; - returned += negative ? "N" : "-"; - returned += overflow ? "O" : "-"; - returned += reserved ? "R" : "-"; - returned += brk ? "B" : "-"; - returned += decimal ? "D" : "-"; - returned += interrupt ? "I" : "-"; - returned += zero ? "Z" : "-"; - returned += carry ? "C" : "-"; - return returned; - } -}; diff --git a/M6502/inc/Symbols.h b/M6502/inc/Symbols.h index 8ab5083..fbb8f28 100644 --- a/M6502/inc/Symbols.h +++ b/M6502/inc/Symbols.h @@ -5,32 +5,33 @@ #include #include -class Symbols -{ -public: - Symbols(std::string path); +namespace EightBit { + class Symbols { + public: + Symbols(std::string path); - const std::map& getLabels() const { return labels; } - const std::map& getConstants() const { return constants; } - const std::map& getScopes() const { return scopes; } - const std::map& getAddresses() const { return addresses; } + const std::map& getLabels() const { return labels; } + const std::map& getConstants() const { return constants; } + const std::map& getScopes() const { return scopes; } + const std::map& getAddresses() const { return addresses; } -private: - void AssignScopes(); - void AssignSymbols(); + private: + void AssignScopes(); + void AssignSymbols(); - void Parse(std::string path); + void Parse(std::string path); - std::map labels; - std::map constants; - std::map scopes; - std::map addresses; + std::map labels; + std::map constants; + std::map scopes; + std::map addresses; - struct kv_pair_t { - std::map element; + struct kv_pair_t { + std::map element; + }; + + std::vector split(const std::string& input, const std::vector& delimiters); + + std::map> parsed; }; - - std::vector split(const std::string& input, const std::vector& delimiters); - - std::map> parsed; -}; +} \ No newline at end of file diff --git a/M6502/inc/mos6502.h b/M6502/inc/mos6502.h index 62a0a94..bfa056a 100644 --- a/M6502/inc/mos6502.h +++ b/M6502/inc/mos6502.h @@ -5,460 +5,434 @@ #include #include -#include "ProcessorType.h" -#include "StatusFlags.h" -#include "AddressingMode.h" +#include "Memory.h" +#include "Processor.h" +#include "Signal.h" -class MOS6502 { -public: - typedef std::function instruction_t; +namespace EightBit { + class MOS6502 : public Processor { + public: + struct opcode_decoded_t { - struct Instruction { - instruction_t vector = nullptr; - uint64_t count = 0; - AddressingMode mode = AddressingMode::Illegal; - std::string display = ""; + int aaa; + int bbb; + int cc; + + opcode_decoded_t() { + aaa = bbb = cc = 0; + } + + opcode_decoded_t(uint8_t opcode) { + aaa = (opcode & 0b11100000) >> 5; // 0 - 7 + bbb = (opcode & 0b00011100) >> 2; // 0 - 7 + cc = (opcode & 0b00000011); // 0 - 3 + } + }; + + enum StatusBits { + NF = Bit7, // Negative + VF = Bit6, // Overflow + RF = Bit5, // reserved + BF = Bit4, // Brk + DF = Bit3, // D (use BCD for arithmetic) + IF = Bit2, // I (IRQ disable) + ZF = Bit1, // Zero + CF = Bit0, // Carry + }; + + enum BusDirection { + Read, + Write + }; + + MOS6502(Memory& memory); + virtual ~MOS6502(); + + Signal ExecutingInstruction; + Signal ExecutedInstruction; + + uint8_t& X() { return x; } + uint8_t& Y() { return y; } + uint8_t& A() { return a; } + uint8_t& S() { return s; } + uint8_t& P() { return p; } + + virtual void initialise(); + + virtual int step(); + + virtual void reset(); + + virtual void triggerIRQ(); + virtual void triggerNMI(); + + void getWord(register16_t& output); + void getWord(uint16_t offset, register16_t& output); + + uint8_t getByte() { return m_memory.read(); } + uint8_t getByte(uint16_t offset) { return m_memory.read(offset); } + + void setByte(uint8_t value) { m_memory.write(value); } + void setByte(uint16_t offset, uint8_t value) { m_memory.write(offset, value); } + + protected: + virtual void interrupt(uint16_t vector); + + virtual int execute(uint8_t cell); + + private: + register16_t& MEMPTR() { return m_memptr; } + + void adjustZero(uint8_t datum) { clearFlag(P(), ZF, datum); } + void adjustNegative(uint8_t datum) { setFlag(P(), NF, datum & NF); } + + void adjustNZ(uint8_t datum) { + adjustZero(datum); + adjustNegative(datum); + } + + void pushByte(uint8_t value); + uint8_t popByte(); + void pushWord(register16_t value); + void popWord(register16_t& output); + + uint8_t fetchByte(); + void fetchWord(register16_t& output); + +#pragma region 6502 addressing modes + +#pragma region Addresses + + void Address_Absolute() { + fetchWord(MEMPTR()); + } + + void Address_ZeroPage() { + MEMPTR().low = fetchByte(); + MEMPTR().high = 0; + } + + void Address_ZeroPageIndirect() { + Address_ZeroPage(); + m_memory.ADDRESS() = MEMPTR(); + getWord(MEMPTR()); + } + + void Address_Indirect() { + Address_Absolute(); + m_memory.ADDRESS() = MEMPTR(); + getWord(MEMPTR()); + } + + void Address_ZeroPageX() { + Address_ZeroPage(); + MEMPTR().low += X(); + } + + void Address_ZeroPageY() { + Address_ZeroPage(); + MEMPTR().low += Y(); + } + + void Address_AbsoluteX() { + Address_Absolute(); + MEMPTR().word += X(); + } + + void Address_AbsoluteY() { + Address_Absolute(); + MEMPTR().word += Y(); + } + + void Address_IndexedIndirectX() { + Address_ZeroPageX(); + m_memory.ADDRESS() = MEMPTR(); + getWord(MEMPTR()); + } + + void Address_IndirectIndexedY() { + Address_ZeroPageIndirect(); + MEMPTR().word += Y(); + } + +#pragma endregion Addresses + +#pragma region References + + uint8_t& AM_A() { + m_busRW = false; + return A(); + } + + uint8_t& AM_Immediate() { + m_busRW = false; + fetchByte(); + return m_memory.reference(); + } + + uint8_t& AM_Absolute() { + m_busRW = true; + Address_Absolute(); + m_memory.ADDRESS() = MEMPTR(); + return m_memory.reference(); + } + + uint8_t& AM_ZeroPage() { + m_busRW = true; + Address_ZeroPage(); + m_memory.ADDRESS() = MEMPTR(); + return m_memory.reference(); + } + + uint8_t& AM_AbsoluteX(BusDirection direction = BusDirection::Read) { + m_busRW = true; + Address_AbsoluteX(); + m_memory.ADDRESS() = MEMPTR(); + if ((direction == BusDirection::Read) && (m_memory.ADDRESS().low == Mask8)) + ++cycles; + return m_memory.reference(); + } + + uint8_t& AM_AbsoluteY(BusDirection direction = BusDirection::Read) { + m_busRW = true; + Address_AbsoluteY(); + m_memory.ADDRESS() = MEMPTR(); + if ((direction == BusDirection::Read) && (m_memory.ADDRESS().low == Mask8)) + ++cycles; + return m_memory.reference(); + } + + uint8_t& AM_ZeroPageX() { + m_busRW = true; + Address_ZeroPageX(); + m_memory.ADDRESS() = MEMPTR(); + return m_memory.reference(); + } + + uint8_t& AM_ZeroPageY() { + m_busRW = true; + Address_ZeroPageY(); + m_memory.ADDRESS() = MEMPTR(); + return m_memory.reference(); + } + + uint8_t& AM_IndexedIndirectX() { + m_busRW = true; + Address_IndexedIndirectX(); + m_memory.ADDRESS() = MEMPTR(); + return m_memory.reference(); + } + + uint8_t& AM_IndirectIndexedY(BusDirection direction = BusDirection::Read) { + m_busRW = true; + Address_IndirectIndexedY(); + m_memory.ADDRESS() = MEMPTR(); + if ((direction == BusDirection::Read) && (m_memory.ADDRESS().low == Mask8)) + ++cycles; + return m_memory.reference(); + } + +#pragma endregion References + +#pragma region 6502 addressing mode switching + + uint8_t& AM_00(int bbb, BusDirection direction = BusDirection::Read) { + switch (bbb) { + case 0b000: + return AM_Immediate(); + case 0b001: + return AM_ZeroPage(); + case 0b011: + return AM_Absolute(); + case 0b101: + return AM_ZeroPageX(); + case 0b111: + return AM_AbsoluteX(direction); + case 0b010: + case 0b100: + case 0b110: + throw std::domain_error("Illegal addressing mode"); + default: + __assume(0); + } + } + + uint8_t& AM_01(int bbb, BusDirection direction = BusDirection::Read) { + switch (bbb) { + case 0b000: + return AM_IndexedIndirectX(); + case 0b001: + return AM_ZeroPage(); + case 0b010: + return AM_Immediate(); + case 0b011: + return AM_Absolute(); + case 0b100: + return AM_IndirectIndexedY(direction); + case 0b101: + return AM_ZeroPageX(); + case 0b110: + return AM_AbsoluteY(direction); + case 0b111: + return AM_AbsoluteX(direction); + default: + __assume(0); + } + } + + uint8_t& AM_10(int bbb, BusDirection direction = BusDirection::Read) { + switch (bbb) { + case 0b000: + return AM_Immediate(); + case 0b001: + return AM_ZeroPage(); + case 0b010: + return AM_A(); + case 0b011: + return AM_Absolute(); + case 0b101: + return AM_ZeroPageX(); + case 0b111: + return AM_AbsoluteX(direction); + case 0b100: + case 0b110: + throw std::domain_error("Illegal addressing mode"); + default: + __assume(0); + } + } + + uint8_t& AM_10_x(int bbb, BusDirection direction = BusDirection::Read) { + switch (bbb) { + case 0b000: + return AM_Immediate(); + case 0b001: + return AM_ZeroPage(); + case 0b010: + return AM_A(); + case 0b011: + return AM_Absolute(); + case 0b101: + return AM_ZeroPageY(); + case 0b111: + return AM_AbsoluteY(direction); + case 0b100: + case 0b110: + throw std::domain_error("Illegal addressing mode"); + default: + __assume(0); + } + } + +#pragma endregion 6502 addressing mode switching + +#pragma endregion 6502 addressing modes + + void firePendingBusEvents(BusDirection direction) { + if (m_busRW) { + switch (direction) { + case BusDirection::Read: + m_memory.fireReadBusEvent(); + break; + case BusDirection::Write: + m_memory.fireWriteBusEvent(); + break; + } + } + } + + void ASL(int bbb) { + auto& reference = AM_10(bbb, BusDirection::Write); + firePendingBusEvents(BusDirection::Read); + ASL(reference); + firePendingBusEvents(BusDirection::Write); + } + + void ROL(int bbb) { + auto& reference = AM_10(bbb, BusDirection::Write); + firePendingBusEvents(BusDirection::Read); + ROL(reference); + firePendingBusEvents(BusDirection::Write); + } + + void LSR(int bbb) { + auto& reference = AM_10(bbb, BusDirection::Write); + firePendingBusEvents(BusDirection::Read); + LSR(reference); + firePendingBusEvents(BusDirection::Write); + } + + void ROR(int bbb) { + auto& reference = AM_10(bbb, BusDirection::Write); + firePendingBusEvents(BusDirection::Read); + ROR(reference); + firePendingBusEvents(BusDirection::Write); + } + + void DEC(int bbb) { + auto& reference = AM_10(bbb, BusDirection::Write); + firePendingBusEvents(BusDirection::Read); + adjustNZ(--reference); + firePendingBusEvents(BusDirection::Write); + } + + void INC(int bbb) { + auto& reference = AM_10(bbb, BusDirection::Write); + firePendingBusEvents(BusDirection::Read); + adjustNZ(++reference); + firePendingBusEvents(BusDirection::Write); + } + + void ROR(uint8_t& output); + + void LSR(uint8_t& output); + + void BIT(uint8_t data); + + void ROL(uint8_t& output); + + void ASL(uint8_t& output); + + void SBC(uint8_t data); + void SBC_b(uint8_t data); + void SBC_d(uint8_t data); + + void CMP(uint8_t first, uint8_t second); + + void ADC(uint8_t data); + void ADC_b(uint8_t data); + void ADC_d(uint8_t data); + + void Branch(int8_t displacement); + + void Branch(bool flag); + + void PHP(); + void PLP(); + + void JSR_abs(); + void RTI(); + void RTS(); + void JMP_abs(); + void JMP_ind(); + void BRK(); + + const uint16_t PageOne = 0x100; + const uint16_t IRQvector = 0xfffe; + const uint16_t RSTvector = 0xfffc; + const uint16_t NMIvector = 0xfffa; + + uint8_t x; // index register X + uint8_t y; // index register Y + uint8_t a; // accumulator + uint8_t s; // stack pointer + uint8_t p; // processor status + + register16_t m_memptr; + + std::array m_timings; + std::array m_decodedOpcodes; + + bool m_busRW; }; - - MOS6502(ProcessorType level); - - ProcessorType getLevel() const { return level; } - uint64_t getCycles() const { return cycles; } - - bool getProceed() const { return proceed; } - void setProceed(bool value) { proceed = value; } - - uint16_t getPC() const { return pc; } - uint8_t getX() const { return x; } - uint8_t getY() const { return y; } - uint8_t getA() const { return a; } - uint8_t getS() const { return s; } - - const StatusFlags& getP() const { return p; } - - const Instruction& getInstruction(uint8_t code) const { - return instructions[code]; - } - - virtual void Initialise(); - - virtual void Start(uint16_t address); - virtual void Run(); - virtual void Step(); - - virtual void Reset(); - - virtual void TriggerIRQ(); - virtual void TriggerNMI(); - - uint16_t GetWord(uint16_t offset) const; - - virtual uint8_t GetByte(uint16_t offset) const = 0; - virtual void SetByte(uint16_t offset, uint8_t value) = 0; - -protected: - virtual void Interrupt(uint16_t vector); - - virtual void Execute(uint8_t cell); - - void ___(); - - void ResetRegisters(); - -private: - static Instruction INS(instruction_t method, uint64_t cycles, AddressingMode addressing, std::string display); - - static uint8_t LowNybble(uint8_t value); - static uint8_t HighNybble(uint8_t value); - static uint8_t PromoteNybble(uint8_t value); - static uint8_t DemoteNybble(uint8_t value); - static uint8_t LowByte(uint16_t value); - static uint8_t HighByte(uint16_t value); - static uint16_t MakeWord(uint8_t low, uint8_t high); - - void Install6502Instructions(); - void Install65sc02Instructions(); - void Install65c02Instructions(); - - void InstallInstructionSet(std::array basis); - void OverlayInstructionSet(std::array overlay); - void OverlayInstructionSet(std::array overlay, bool includeIllegal); - - bool UpdateZeroFlag(uint8_t datum); - bool UpdateNegativeFlag(int8_t datum); - void UpdateZeroNegativeFlags(uint8_t datum); - - void PushByte(uint8_t value); - uint8_t PopByte(); - void PushWord(uint16_t value); - uint16_t PopWord(); - - uint8_t FetchByte(); - uint16_t FetchWord(); - - uint16_t Address_ZeroPage(); - uint16_t Address_ZeroPageX(); - uint16_t Address_ZeroPageY(); - uint16_t Address_IndexedIndirectX(); - uint16_t Address_IndexedIndirectY_Read(); - uint16_t Address_IndexedIndirectY_Write(); - uint16_t Address_Absolute(); - uint16_t Address_AbsoluteXIndirect(); - uint16_t Address_AbsoluteX_Read(); - uint16_t Address_AbsoluteX_Write(); - uint16_t Address_AbsoluteY_Read(); - uint16_t Address_AbsoluteY_Write(); - uint16_t Address_ZeroPageIndirect(); - - uint8_t ReadByte_Immediate(); - int8_t ReadByte_ImmediateDisplacement(); - uint8_t ReadByte_ZeroPage(); - uint8_t ReadByte_ZeroPageX(); - uint8_t ReadByte_ZeroPageY(); - uint8_t ReadByte_Absolute(); - uint8_t ReadByte_AbsoluteX(); - uint8_t ReadByte_AbsoluteY(); - uint8_t ReadByte_IndexedIndirectX(); - uint8_t ReadByte_IndirectIndexedY(); - uint8_t ReadByte_ZeroPageIndirect(); - - void WriteByte_ZeroPage(uint8_t value); - void WriteByte_Absolute(uint8_t value); - void WriteByte_AbsoluteX(uint8_t value); - void WriteByte_AbsoluteY(uint8_t value); - void WriteByte_ZeroPageX(uint8_t value); - void WriteByte_ZeroPageY(uint8_t value); - void WriteByte_IndirectIndexedY(uint8_t value); - void WriteByte_IndexedIndirectX(uint8_t value); - void WriteByte_ZeroPageIndirect(uint8_t value); - - void DEC(uint16_t offset); - - uint8_t ROR(uint8_t data); - void ROR(uint16_t offset); - - uint8_t LSR(uint8_t data); - void LSR(uint16_t offset); - - void BIT_immediate(uint8_t data); - void BIT(uint8_t data); - - void TSB(uint16_t address); - void TRB(uint16_t address); - - void INC(uint16_t offset); - - void ROL(uint16_t offset); - uint8_t ROL(uint8_t data); - - void ASL(uint16_t offset); - uint8_t ASL(uint8_t data); - - void ORA(uint8_t data); - - void AND(uint8_t data); - - void SBC(uint8_t data); - void SBC_b(uint8_t data); - void SBC_d(uint8_t data); - - void EOR(uint8_t data); - - void CPX(uint8_t data); - void CPY(uint8_t data); - void CMP(uint8_t data); - void CMP(uint8_t first, uint8_t second); - - void LDA(uint8_t data); - void LDY(uint8_t data); - void LDX(uint8_t data); - - void ADC(uint8_t data); - void ADC_b(uint8_t data); - void ADC_d(uint8_t data); - - void RMB(uint16_t address, uint8_t flag); - void SMB(uint16_t address, uint8_t flag); - - void Branch(int8_t displacement); - void Branch(); - void Branch(bool flag); - void BitBranch_Clear(uint8_t check); - void BitBranch_Set(uint8_t check); - - void NOP_imp(); - void NOP2_imp(); - void NOP3_imp(); - - void ORA_xind(); - void ORA_zp(); - void ORA_imm(); - void ORA_abs(); - void ORA_absx(); - void ORA_absy(); - void ORA_zpx(); - void ORA_indy(); - void ORA_zpind(); - - void AND_zpx(); - void AND_indy(); - void AND_zp(); - void AND_absx(); - void AND_absy(); - void AND_imm(); - void AND_xind(); - void AND_abs(); - void AND_zpind(); - - void EOR_absx(); - void EOR_absy(); - void EOR_zpx(); - void EOR_indy(); - void EOR_abs(); - void EOR_imm(); - void EOR_zp(); - void EOR_xind(); - void EOR_zpind(); - - void LDA_absx(); - void LDA_absy(); - void LDA_zpx(); - void LDA_indy(); - void LDA_abs(); - void LDA_imm(); - void LDA_zp(); - void LDA_xind(); - void LDA_zpind(); - - void LDX_imm(); - void LDX_zp(); - void LDX_abs(); - void LDX_zpy(); - void LDX_absy(); - - void LDY_imm(); - void LDY_zp(); - void LDY_abs(); - void LDY_zpx(); - void LDY_absx(); - - void CMP_absx(); - void CMP_absy(); - void CMP_zpx(); - void CMP_indy(); - void CMP_abs(); - void CMP_imm(); - void CMP_zp(); - void CMP_xind(); - void CMP_zpind(); - - void CPX_abs(); - void CPX_zp(); - void CPX_imm(); - - void CPY_imm(); - void CPY_zp(); - void CPY_abs(); - - void ADC_zp(); - void ADC_xind(); - void ADC_imm(); - void ADC_abs(); - void ADC_zpx(); - void ADC_indy(); - void ADC_absx(); - void ADC_absy(); - void ADC_zpind(); - - void SBC_xind(); - void SBC_zp(); - void SBC_imm(); - void SBC_abs(); - void SBC_zpx(); - void SBC_indy(); - void SBC_absx(); - void SBC_absy(); - void SBC_zpind(); - - void BIT_imm(); - void BIT_zp(); - void BIT_zpx(); - void BIT_abs(); - void BIT_absx(); - - void DEC_a(); - void DEC_absx(); - void DEC_zpx(); - void DEC_abs(); - void DEC_zp(); - - void DEX_imp(); - void DEY_imp(); - - void INC_a(); - void INC_zp(); - void INC_absx(); - void INC_zpx(); - void INC_abs(); - - void INX_imp(); - void INY_imp(); - - void STX_zpy(); - void STX_abs(); - void STX_zp(); - - void STY_zpx(); - void STY_abs(); - void STY_zp(); - - void STA_absx(); - void STA_absy(); - void STA_zpx(); - void STA_indy(); - void STA_abs(); - void STA_zp(); - void STA_xind(); - void STA_zpind(); - - void STZ_zp(); - void STZ_zpx(); - void STZ_abs(); - void STZ_absx(); - - void TSX_imp(); - void TAX_imp(); - void TAY_imp(); - void TXS_imp(); - void TYA_imp(); - void TXA_imp(); - - void PHP_imp(); - void PLP_imp(); - void PLA_imp(); - void PHA_imp(); - void PHX_imp(); - void PHY_imp(); - void PLX_imp(); - void PLY_imp(); - - void ASL_a(); - void ASL_zp(); - void ASL_abs(); - void ASL_absx(); - void ASL_zpx(); - - void LSR_absx(); - void LSR_zpx(); - void LSR_abs(); - void LSR_a(); - void LSR_zp(); - - void ROL_absx(); - void ROL_zpx(); - void ROL_abs(); - void ROL_a(); - void ROL_zp(); - - void ROR_absx(); - void ROR_zpx(); - void ROR_abs(); - void ROR_a(); - void ROR_zp(); - - void TSB_zp(); - void TSB_abs(); - - void TRB_zp(); - void TRB_abs(); - - void RMB0_zp(); - void RMB1_zp(); - void RMB2_zp(); - void RMB3_zp(); - void RMB4_zp(); - void RMB5_zp(); - void RMB6_zp(); - void RMB7_zp(); - - void SMB0_zp(); - void SMB1_zp(); - void SMB2_zp(); - void SMB3_zp(); - void SMB4_zp(); - void SMB5_zp(); - void SMB6_zp(); - void SMB7_zp(); - - void JSR_abs(); - void RTI_imp(); - void RTS_imp(); - void JMP_abs(); - void JMP_ind(); - void JMP_absxind(); - void BRK_imp(); - - void WAI_imp(); - void STP_imp(); - - void SED_imp(); - void CLD_imp(); - void CLV_imp(); - void SEI_imp(); - void CLI_imp(); - void CLC_imp(); - void SEC_imp(); - - void BMI_rel(); - void BPL_rel(); - void BVC_rel(); - void BVS_rel(); - void BCC_rel(); - void BCS_rel(); - void BNE_rel(); - void BEQ_rel(); - void BRA_rel(); - - void BBR0_zprel(); - void BBR1_zprel(); - void BBR2_zprel(); - void BBR3_zprel(); - void BBR4_zprel(); - void BBR5_zprel(); - void BBR6_zprel(); - void BBR7_zprel(); - - void BBS0_zprel(); - void BBS1_zprel(); - void BBS2_zprel(); - void BBS3_zprel(); - void BBS4_zprel(); - void BBS5_zprel(); - void BBS6_zprel(); - void BBS7_zprel(); - - const uint16_t PageOne = 0x100; - const uint16_t IRQvector = 0xfffe; - const uint16_t RSTvector = 0xfffc; - const uint16_t NMIvector = 0xfffa; - - uint16_t pc; // program counter - uint8_t x; // index register X - uint8_t y; // index register Y - uint8_t a; // accumulator - uint8_t s; // stack pointer - - StatusFlags p = 0; // processor status - - uint64_t cycles; - - bool proceed = true; - - std::array instructions; - - ProcessorType level; - - std::array overlay6502; - std::array overlay65sc02; - std::array overlay65c02; -}; \ No newline at end of file +} \ No newline at end of file diff --git a/M6502/inc/system6502.h b/M6502/inc/system6502.h deleted file mode 100644 index 1d47e1e..0000000 --- a/M6502/inc/system6502.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "mos6502.h" -#include "EventArgs.h" -#include "AddressEventArgs.h" -#include "Signal.h" -#include "Memory.h" - -class System6502 : public MOS6502 -{ -public: - const double Mega = 1000000; - const double Milli = 0.001; - const unsigned MemorySize = 0x10000; - - uint64_t getHeldCycles() const { return heldCycles; } - -public: - System6502(ProcessorType level, double speed, clock_t pollInterval); - - Memory& getMemory() { - return memory; - } - - virtual void Initialise(); - virtual void Run(); - - Signal Starting; - Signal Finished; - Signal Polling; - Signal ExecutingInstruction; - Signal ExecutedInstruction; - - virtual uint8_t GetByte(uint16_t offset) const; - virtual void SetByte(uint16_t offset, uint8_t value); - -protected: - virtual void Execute(uint8_t cell); - -private: - void CheckPoll(); - - void System6502_Starting(); - void System6502_Finished(); - - void Throttle(); - - double speed; // Speed in MHz, e.g. 2.0 == 2Mhz, 1.79 = 1.79Mhz - - double cyclesPerSecond; - double cyclesPerMillisecond; - uint64_t cyclesPerInterval; - uint64_t intervalCycles; - - uint64_t heldCycles = 0; - - bool running; - - Memory memory; - - std::chrono::high_resolution_clock::time_point startTime; -}; diff --git a/M6502/src/AddressingMode.cpp b/M6502/src/AddressingMode.cpp deleted file mode 100644 index d11aa76..0000000 --- a/M6502/src/AddressingMode.cpp +++ /dev/null @@ -1,23 +0,0 @@ -namespace Processor -{ - public enum AddressingMode - { - Illegal, - Implied, - Accumulator, - Immediate, - Relative, - XIndexed, - IndexedY, - ZeroPage, - ZeroPageX, - ZeroPageY, - Absolute, - AbsoluteX, - AbsoluteY, - AbsoluteXIndirect, - Indirect, - ZeroPageIndirect, - ZeroPageRelative - } -} diff --git a/M6502/src/Disassembly.cpp b/M6502/src/Disassembly.cpp index 7f25442..edb1654 100644 --- a/M6502/src/Disassembly.cpp +++ b/M6502/src/Disassembly.cpp @@ -7,226 +7,365 @@ using namespace std::placeholders; -Disassembly::Disassembly(MOS6502& targetProcessor, const Symbols& targetSymbols) +EightBit::Disassembly::Disassembly(MOS6502& targetProcessor, const Symbols& targetSymbols) : processor(targetProcessor), - symbols(targetSymbols) -{ - dumpers = { - { AddressingMode::Illegal, { std::bind(&Disassembly::Dump_Nothing, this, std::placeholders::_1), std::bind(&Disassembly::Dump_Nothing, this, std::placeholders::_1) } }, - { AddressingMode::Implied, { std::bind(&Disassembly::Dump_Nothing, this, std::placeholders::_1), std::bind(&Disassembly::Dump_Nothing, this, std::placeholders::_1) } }, - { AddressingMode::Accumulator, { std::bind(&Disassembly::Dump_Nothing, this, std::placeholders::_1), std::bind(&Disassembly::Dump_A, this, std::placeholders::_1) } }, - { AddressingMode::Immediate, { std::bind(&Disassembly::Dump_Byte, this, std::placeholders::_1), std::bind(&Disassembly::Dump_imm, this, std::placeholders::_1) } }, - { AddressingMode::Relative, { std::bind(&Disassembly::Dump_Byte, this, std::placeholders::_1), std::bind(&Disassembly::Dump_rel, this, std::placeholders::_1) } }, - { AddressingMode::XIndexed, { std::bind(&Disassembly::Dump_Byte, this, std::placeholders::_1), std::bind(&Disassembly::Dump_xind, this, std::placeholders::_1) } }, - { AddressingMode::IndexedY, { std::bind(&Disassembly::Dump_Byte, this, std::placeholders::_1), std::bind(&Disassembly::Dump_indy, this, std::placeholders::_1) } }, - { AddressingMode::ZeroPageIndirect, { std::bind(&Disassembly::Dump_Byte, this, std::placeholders::_1), std::bind(&Disassembly::Dump_zpind, this, std::placeholders::_1) } }, - { AddressingMode::ZeroPage, { std::bind(&Disassembly::Dump_Byte, this, std::placeholders::_1), std::bind(&Disassembly::Dump_zp, this, std::placeholders::_1) } }, - { AddressingMode::ZeroPageX, { std::bind(&Disassembly::Dump_Byte, this, std::placeholders::_1), std::bind(&Disassembly::Dump_zpx, this, std::placeholders::_1) } }, - { AddressingMode::ZeroPageY, { std::bind(&Disassembly::Dump_Byte, this, std::placeholders::_1), std::bind(&Disassembly::Dump_zpy, this, std::placeholders::_1) } }, - { AddressingMode::Absolute, { std::bind(&Disassembly::Dump_DByte, this, std::placeholders::_1), std::bind(&Disassembly::Dump_abs, this, std::placeholders::_1) } }, - { AddressingMode::AbsoluteX, { std::bind(&Disassembly::Dump_DByte, this, std::placeholders::_1), std::bind(&Disassembly::Dump_absx, this, std::placeholders::_1) } }, - { AddressingMode::AbsoluteY, { std::bind(&Disassembly::Dump_DByte, this, std::placeholders::_1), std::bind(&Disassembly::Dump_absy, this, std::placeholders::_1) } }, - { AddressingMode::AbsoluteXIndirect, { std::bind(&Disassembly::Dump_DByte, this, std::placeholders::_1), std::bind(&Disassembly::Dump_absxind, this, std::placeholders::_1) } }, - { AddressingMode::Indirect, { std::bind(&Disassembly::Dump_DByte, this, std::placeholders::_1), std::bind(&Disassembly::Dump_ind, this, std::placeholders::_1) } }, - { AddressingMode::ZeroPageRelative, { std::bind(&Disassembly::Dump_DByte, this, std::placeholders::_1), std::bind(&Disassembly::Dump_zprel, this, std::placeholders::_1) } }, - }; + symbols(targetSymbols) { } -std::string Disassembly::Dump_ByteValue(uint8_t value) const { +std::string EightBit::Disassembly::dump_Flags(uint8_t value) const { + std::string returned; + returned += (value & MOS6502::NF) ? "N" : "-"; + returned += (value & MOS6502::VF) ? "O" : "-"; + returned += (value & MOS6502::RF) ? "R" : "-"; + returned += (value & MOS6502::BF) ? "B" : "-"; + returned += (value & MOS6502::DF) ? "D" : "-"; + returned += (value & MOS6502::IF) ? "I" : "-"; + returned += (value & MOS6502::ZF) ? "Z" : "-"; + returned += (value & MOS6502::CF) ? "C" : "-"; + return returned; +} + +std::string EightBit::Disassembly::dump_ByteValue(uint8_t value) const { std::ostringstream output; output << std::hex << std::setw(2) << std::setfill('0') << (int)value; return output.str(); } -std::string Disassembly::Dump_WordValue(uint16_t value) const { +std::string EightBit::Disassembly::dump_WordValue(uint16_t value) const { std::ostringstream output; output << std::hex << std::setw(4) << std::setfill('0') << (int)value; return output.str(); } -std::string Disassembly::DumpBytes(AddressingMode mode, uint16_t current) const { - return getDumper(mode).byteDumper(current); -} +std::string EightBit::Disassembly::disassemble(uint16_t current) const { -std::string Disassembly::Disassemble(uint16_t current) const { + m_address = current; std::ostringstream output; - auto content = processor.GetByte(current); - const auto& instruction = processor.getInstruction(content); + auto memory = processor.getMemory(); - auto mode = instruction.mode; - auto mnemomic = instruction.display; + auto cell = memory.peek(current); - auto operand = DumpOperand(mode, current + 1); + output << dump_ByteValue(cell); - auto label = symbols.getLabels().find(current); - if (label != symbols.getLabels().end()) - output << label->second << ": "; - output << mnemomic << " " << operand; + auto byte = memory.peek(current + 1); + uint16_t relative = processor.PC().word + 2 + (int8_t)byte; + + auto aaa = (cell & 0b11100000) >> 5; + auto bbb = (cell & 0b00011100) >> 2; + auto cc = (cell & 0b00000011); + + switch (cc) { + case 0b00: + switch (aaa) { + case 0b000: + switch (bbb) { + case 0b000: // BRK + output << disassemble_Implied("BRK"); + break; + case 0b010: // PHP + output << disassemble_Implied("PHP"); + break; + case 0b100: // BPL + output << disassemble_Relative("BPL", relative); + break; + case 0b110: // CLC + output << disassemble_Implied("CLC"); + break; + default: + throw std::domain_error("Illegal instruction"); + } + break; + case 0b001: + switch (bbb) { + case 0b000: // JSR + output << disassemble_Absolute("JSR"); + break; + case 0b010: // PLP + output << disassemble_Implied("PLP"); + break; + case 0b100: // BMI + output << disassemble_Relative("BMI", relative); + break; + case 0b110: // SEC + output << disassemble_Implied("SEC"); + break; + default: // BIT + output << disassemble_AM_00(bbb, "BIT"); + break; + } + break; + case 0b010: + switch (bbb) { + case 0b000: // RTI + output << disassemble_Implied("RTI"); + break; + case 0b010: // PHA + output << disassemble_Implied("PHA"); + break; + case 0b011: // JMP + output << disassemble_Absolute("JMP"); + break; + case 0b100: // BVC + output << disassemble_Relative("BVC", relative); + break; + case 0b110: // CLI + output << disassemble_Implied("CLI"); + break; + default: + throw std::domain_error("Illegal addressing mode"); + } + break; + case 0b011: + switch (bbb) { + case 0b000: // RTS + output << disassemble_Implied("RTS"); + break; + case 0b010: // PLA + output << disassemble_Implied("PLA"); + break; + case 0b011: // JMP (abs) + output << disassemble_Indirect("JMP"); + break; + case 0b100: // BVS + output << disassemble_Relative("BVS", relative); + break; + case 0b110: // SEI + output << disassemble_Implied("SEI"); + break; + default: + throw std::domain_error("Illegal addressing mode"); + } + break; + case 0b100: + switch (bbb) { + case 0b010: // DEY + output << disassemble_Implied("DEY"); + break; + case 0b100: // BCC + output << disassemble_Relative("BCC", relative); + break; + case 0b110: // TYA + output << disassemble_Implied("TYA"); + break; + default: // STY + output << disassemble_AM_00(bbb, "STY"); + break; + } + break; + case 0b101: + switch (bbb) { + case 0b010: // TAY + output << disassemble_Implied("TAY"); + break; + case 0b100: // BCS + output << disassemble_Relative("BCS", relative); + break; + case 0b110: // CLV + output << disassemble_Implied("CLV"); + break; + default: // LDY + output << disassemble_AM_00(bbb, "LDY"); + break; + } + break; + case 0b110: + switch (bbb) { + case 0b010: // INY + output << disassemble_Implied("INY"); + break; + case 0b100: // BNE + output << disassemble_Relative("BNE", relative); + break; + case 0b110: // CLD + output << disassemble_Implied("CLD"); + break; + default: // CPY + output << disassemble_AM_00(bbb, "CPY"); + break; + } + break; + case 0b111: + switch (bbb) { + case 0b010: // INX + output << disassemble_Implied("INX"); + break; + case 0b100: // BEQ + output << disassemble_Relative("BEQ", relative); + break; + case 0b110: // SED + output << disassemble_Implied("SED"); + break; + default: // CPX + output << disassemble_AM_00(bbb, "CPX"); + break; + } + break; + } + break; + case 0b01: + switch (aaa) { + case 0b000: // ORA + output << disassemble_AM_01(bbb, "ORA"); + break; + case 0b001: // AND + output << disassemble_AM_01(bbb, "AND"); + break; + case 0b010: // EOR + output << disassemble_AM_01(bbb, "EOR"); + break; + case 0b011: // ADC + output << disassemble_AM_01(bbb, "ADC"); + break; + case 0b100: // STA + output << disassemble_AM_01(bbb, "STA"); + break; + case 0b101: // LDA + output << disassemble_AM_01(bbb, "LDA"); + break; + case 0b110: // CMP + output << disassemble_AM_01(bbb, "CMP"); + break; + case 0b111: // SBC + output << disassemble_AM_01(bbb, "SBC"); + break; + default: + __assume(0); + } + break; + case 0b10: + switch (aaa) { + case 0b000: // ASL + output << disassemble_AM_10(bbb, "ASL"); + break; + case 0b001: // ROL + output << disassemble_AM_10(bbb, "ROL"); + break; + case 0b010: // LSR + output << disassemble_AM_10(bbb, "LSR"); + break; + case 0b011: // ROR + output << disassemble_AM_10(bbb, "ROR"); + break; + case 0b100: + switch (bbb) { + case 0b010: // TXA + output << disassemble_Implied("TXA"); + break; + case 0b110: // TXS + output << disassemble_Implied("TXS"); + break; + default: // STX + output << disassemble_AM_10_x(bbb, "STX"); + break; + } + break; + case 0b101: + switch (bbb) { + case 0b010: // TAX + output << disassemble_Implied("TAX"); + break; + case 0b110: // TSX + output << disassemble_Implied("TSX"); + break; + default: // LDX + output << disassemble_AM_10_x(bbb, "LDX"); + break; + } + break; + case 0b110: + switch (bbb) { + case 0b010: // DEX + output << disassemble_Implied("DEX"); + break; + default: // DEC + output << disassemble_AM_10(bbb, "DEC"); + break; + } + break; + case 0b111: + switch (bbb) { + case 0b010: // NOP + output << disassemble_Implied("NOP"); + break; + default: // INC + output << disassemble_AM_10(bbb, "INC"); + break; + } + break; + default: + __assume(0); + } + break; + case 0b11: + throw std::domain_error("Illegal instruction group"); + default: + __assume(0); + } + + //const auto& instruction = processor.getInstruction(content); + + //auto mode = instruction.mode; + //auto mnemomic = instruction.display; + + //auto operand = DumpOperand(mode, current + 1); + + //auto label = symbols.getLabels().find(current); + //if (label != symbols.getLabels().end()) + // output << label->second << ": "; + //output << mnemomic << " " << operand; return output.str(); } -std::string Disassembly::DumpOperand(AddressingMode mode, uint16_t current) const { - return getDumper(mode).disassemblyDumper(current); +//// + +uint8_t EightBit::Disassembly::getByte(uint16_t address) const { + return processor.getMemory().peek(address); } //// -uint8_t Disassembly::GetByte(uint16_t address) const { - return processor.GetByte(address); +std::string EightBit::Disassembly::dump_Byte(uint16_t address) const { + return dump_ByteValue(getByte(address)); } -uint16_t Disassembly::GetWord(uint16_t address) const { - return processor.GetWord(address); +std::string EightBit::Disassembly::dump_DByte(uint16_t address) const { + return dump_Byte(address) + dump_Byte(address + 1); } //// -std::string Disassembly::Dump_Nothing(uint16_t) const { - return ""; -} - -std::string Disassembly::Dump_Byte(uint16_t address) const { - return Dump_ByteValue(GetByte(address)); -} - -std::string Disassembly::Dump_DByte(uint16_t address) const { - return Dump_Byte(address) + Dump_Byte(address + 1); -} - -//// - -std::string Disassembly::ConvertAddress(uint16_t address) const { +std::string EightBit::Disassembly::convertAddress(uint16_t address) const { auto label = symbols.getLabels().find(address); if (label != symbols.getLabels().end()) return label->second; std::ostringstream output; - output << "$" << Dump_WordValue(address); + output << "$" << dump_WordValue(address); return output.str(); } -std::string Disassembly::ConvertAddress(uint8_t address) const { +std::string EightBit::Disassembly::convertAddress(uint8_t address) const { auto label = symbols.getLabels().find(address); if (label != symbols.getLabels().end()) return label->second; std::ostringstream output; - output << "$" << Dump_ByteValue(address); + output << "$" << dump_ByteValue(address); return output.str(); } -std::string Disassembly::ConvertConstant(uint16_t constant) const { +std::string EightBit::Disassembly::convertConstant(uint16_t constant) const { auto label = symbols.getConstants().find(constant); if (label != symbols.getConstants().end()) return label->second; - return Dump_DByte(constant); + return dump_DByte(constant); } -std::string Disassembly::ConvertConstant(uint8_t constant) const { +std::string EightBit::Disassembly::convertConstant(uint8_t constant) const { auto label = symbols.getConstants().find(constant); if (label != symbols.getConstants().end()) return label->second; - return Dump_ByteValue(constant); -} - -//// - -std::string Disassembly::Dump_A(uint16_t) const { - return "A"; -} - -std::string Disassembly::Dump_imm(uint16_t current) const { - std::ostringstream output; - auto immediate = GetByte(current); - output << "#" << ConvertConstant(immediate); - return output.str(); -} - -std::string Disassembly::Dump_abs(uint16_t current) const { - auto address = GetWord(current); - return ConvertAddress(address); -} - -std::string Disassembly::Dump_zp(uint16_t current) const { - auto zp = GetByte(current); - return ConvertAddress(zp); -} - -std::string Disassembly::Dump_zpx(uint16_t current) const { - std::ostringstream output; - auto zp = GetByte(current); - output << ConvertAddress(zp) << ",X"; - return output.str(); -} - -std::string Disassembly::Dump_zpy(uint16_t current) const { - std::ostringstream output; - auto zp = GetByte(current); - output << ConvertAddress(zp) << ",Y"; - return output.str(); -} - -std::string Disassembly::Dump_absx(uint16_t current) const { - std::ostringstream output; - auto address = GetWord(current); - output << ConvertAddress(address) << ",X"; - return output.str(); -} - -std::string Disassembly::Dump_absy(uint16_t current) const { - std::ostringstream output; - auto address = GetWord(current); - output << ConvertAddress(address) << ",Y"; - return output.str(); -} - -std::string Disassembly::Dump_absxind(uint16_t current) const { - std::ostringstream output; - auto address = GetWord(current); - output << "(" << ConvertAddress(address) << ",X)"; - return output.str(); -} - -std::string Disassembly::Dump_xind(uint16_t current) const { - std::ostringstream output; - auto zp = GetByte(current); - output << "(" << ConvertAddress(zp) << ",X)"; - return output.str(); -} - -std::string Disassembly::Dump_indy(uint16_t current) const { - std::ostringstream output; - auto zp = GetByte(current); - output << "(" << ConvertAddress(zp) << "),Y"; - return output.str(); -} - -std::string Disassembly::Dump_ind(uint16_t current) const { - std::ostringstream output; - auto address = GetWord(current); - output << "(" << ConvertAddress(address) << ")"; - return output.str(); -} - -std::string Disassembly::Dump_zpind(uint16_t current) const { - std::ostringstream output; - auto zp = GetByte(current); - output << "(" << ConvertAddress(zp) << ")"; - return output.str(); -} - -std::string Disassembly::Dump_rel(uint16_t current) const { - uint16_t relative = 1 + current + (int8_t)GetByte(current); - return ConvertAddress(relative); -} - -std::string Disassembly::Dump_zprel(uint16_t current) const { - std::ostringstream output; - auto zp = GetByte(current); - int8_t displacement = GetByte(current + 1); - uint16_t address = 1 + current + displacement; - output << ConvertAddress(zp) << "," << ConvertAddress(address); - return output.str(); + return dump_ByteValue(constant); } \ No newline at end of file diff --git a/M6502/src/M6502.vcxproj b/M6502/src/M6502.vcxproj new file mode 100644 index 0000000..089b74f --- /dev/null +++ b/M6502/src/M6502.vcxproj @@ -0,0 +1,164 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {D8726A1B-BBFE-47EF-9860-26B90140BA66} + Win32Proj + M6502 + + + + StaticLibrary + true + v140 + Unicode + + + StaticLibrary + false + v140 + true + Unicode + + + StaticLibrary + true + v140 + Unicode + + + StaticLibrary + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + + + ..\inc;..\..\inc;C:\local\boost_1_64_0;$(IncludePath) + + + ..\inc;..\..\inc;C:\local\boost_1_64_0;$(IncludePath) + + + ..\inc;..\..\inc;C:\local\boost_1_64_0;$(IncludePath) + + + ..\inc;..\..\inc;C:\local\boost_1_64_0;$(IncludePath) + + + + Level3 + Use + MaxSpeed + true + true + NDEBUG;_LIB;%(PreprocessorDefinitions) + false + AdvancedVectorExtensions + + + Windows + true + true + + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + + + Windows + + + + + Use + Level3 + Disabled + _DEBUG;_LIB;%(PreprocessorDefinitions) + + + Windows + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + + + Windows + true + true + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + + \ No newline at end of file diff --git a/M6502/src/M6502.vcxproj.filters b/M6502/src/M6502.vcxproj.filters new file mode 100644 index 0000000..ca67e17 --- /dev/null +++ b/M6502/src/M6502.vcxproj.filters @@ -0,0 +1,59 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/M6502/src/ProcessorType.cpp b/M6502/src/ProcessorType.cpp deleted file mode 100644 index aa0fe7b..0000000 --- a/M6502/src/ProcessorType.cpp +++ /dev/null @@ -1,9 +0,0 @@ -namespace Processor -{ - public enum ProcessorType - { - Cpu6502, - Cpu65SC02, - Cpu65C02 - } -} diff --git a/M6502/src/Profiler.cpp b/M6502/src/Profiler.cpp index b5b53ca..8f2efa5 100644 --- a/M6502/src/Profiler.cpp +++ b/M6502/src/Profiler.cpp @@ -1,44 +1,35 @@ #include "stdafx.h" #include "Profiler.h" -Profiler::Profiler(System6502& targetProcessor, Disassembly& disassemblerTarget, Symbols& symbolsTarget, bool instructions, bool addresses) -: processor(targetProcessor), - disassembler(disassemblerTarget), - symbols(symbolsTarget), - countInstructions(instructions), - profileAddresses(addresses) -{ +EightBit::Profiler::Profiler(MOS6502& targetProcessor, Disassembly& disassemblerTarget, Symbols& symbolsTarget) +: processor(targetProcessor), + disassembler(disassemblerTarget), + symbols(symbolsTarget) { + instructionCounts.fill(0); addressProfiles.fill(0); addressCounts.fill(0); - if (profileAddresses) - processor.ExecutingInstruction.connect(std::bind(&Profiler::Processor_ExecutingInstruction_ProfileAddresses, this, std::placeholders::_1)); - if (countInstructions) - processor.ExecutingInstruction.connect(std::bind(&Profiler::Processor_ExecutingInstruction_CountInstructions, this, std::placeholders::_1)); - if (profileAddresses) - processor.ExecutedInstruction.connect(std::bind(&Profiler::Processor_ExecutedInstruction_ProfileAddresses, this, std::placeholders::_1)); - BuildAddressScopes(); } -void Profiler::Generate() { +void EightBit::Profiler::Generate() { StartingOutput.fire(EventArgs()); EmitProfileInformation(); StartingOutput.fire(EventArgs()); } -void Profiler::EmitProfileInformation() { +void EightBit::Profiler::EmitProfileInformation() { { StartingLineOutput.fire(EventArgs()); // For each memory address - for (uint16_t address = 0; address < 0x10000; ++address) { + for (int address = 0; address < 0x10000; ++address) { // If there are any cycles associated auto cycles = addressProfiles[address]; if (cycles > 0) { // Dump a profile/disassembly line - auto source = disassembler.Disassemble(address); + auto source = disassembler.disassemble(address); EmitLine.fire(ProfileLineEventArgs(source, cycles)); } } @@ -58,22 +49,16 @@ void Profiler::EmitProfileInformation() { } } -void Profiler::Processor_ExecutingInstruction_ProfileAddresses(const AddressEventArgs& addressEvent) { - assert(profileAddresses); - priorCycleCount = processor.getCycles(); - addressCounts[addressEvent.getAddress()]++; +void EightBit::Profiler::addInstruction(uint8_t instruction) { + ++instructionCounts[instruction]; } -void Profiler::Processor_ExecutingInstruction_CountInstructions(const AddressEventArgs& addressEvent) { - assert(countInstructions); - ++instructionCounts[addressEvent.getCell()]; -} +void EightBit::Profiler::addAddress(uint16_t address, int cycles) { -void Profiler::Processor_ExecutedInstruction_ProfileAddresses(const AddressEventArgs& addressEvent) { - assert(profileAddresses); - auto cycles = processor.getCycles() - priorCycleCount; - addressProfiles[addressEvent.getAddress()] += cycles; - auto addressScope = addressScopes[addressEvent.getAddress()]; + addressCounts[address]++; + + addressProfiles[address] += cycles; + auto addressScope = addressScopes[address]; if (!addressScope.empty()) { if (scopeCycles.find(addressScope) == scopeCycles.end()) scopeCycles[addressScope] = 0; @@ -81,7 +66,7 @@ void Profiler::Processor_ExecutedInstruction_ProfileAddresses(const AddressEvent } } -void Profiler::BuildAddressScopes() { +void EightBit::Profiler::BuildAddressScopes() { for (auto& label : symbols.getLabels()) { auto address = label.first; auto key = label.second; diff --git a/M6502/src/Symbols.cpp b/M6502/src/Symbols.cpp index 47b6283..93cc4a4 100644 --- a/M6502/src/Symbols.cpp +++ b/M6502/src/Symbols.cpp @@ -10,7 +10,7 @@ #include #include -Symbols::Symbols(std::string path) { +EightBit::Symbols::Symbols(std::string path) { if (!path.empty()) { Parse(path); AssignSymbols(); @@ -18,7 +18,7 @@ Symbols::Symbols(std::string path) { } } -void Symbols::AssignScopes() { +void EightBit::Symbols::AssignScopes() { auto parsedScopes = parsed["scope"]; for(auto& parsedScopeElement : parsedScopes) { auto& parsedScope = parsedScopeElement.second.element; @@ -29,7 +29,7 @@ void Symbols::AssignScopes() { } } -void Symbols::AssignSymbols() { +void EightBit::Symbols::AssignSymbols() { auto symbols = parsed["sym"]; for(auto& symbolElement : symbols) { auto& symbol = symbolElement.second.element; @@ -47,7 +47,7 @@ void Symbols::AssignSymbols() { } } -void Symbols::Parse(std::string path) { +void EightBit::Symbols::Parse(std::string path) { std::string line; std::ifstream reader(path); while (std::getline(reader, line)) { @@ -73,7 +73,7 @@ void Symbols::Parse(std::string path) { } } -std::vector Symbols::split(const std::string& input, const std::vector& delimiters) { +std::vector EightBit::Symbols::split(const std::string& input, const std::vector& delimiters) { std::vector tokens; boost::algorithm::split_regex( tokens, diff --git a/M6502/src/mos6502.cpp b/M6502/src/mos6502.cpp index e6f6554..563cbc1 100644 --- a/M6502/src/mos6502.cpp +++ b/M6502/src/mos6502.cpp @@ -1,1653 +1,580 @@ #include "stdafx.h" #include "mos6502.h" -MOS6502::MOS6502(ProcessorType cpuLevel) -: level(cpuLevel) -{ - Install6502Instructions(); - Install65sc02Instructions(); - Install65c02Instructions(); -} - -void MOS6502::Initialise() { - cycles = 0; - ResetRegisters(); -} - -void MOS6502::Start(uint16_t address) { - pc = address; -} - -void MOS6502::Run() { - while (proceed) - Step(); -} - -void MOS6502::Step() { - Execute(FetchByte()); -} - -void MOS6502::Reset() { - pc = GetWord(RSTvector); -} - -void MOS6502::TriggerIRQ() { - Interrupt(IRQvector); -} - -void MOS6502::TriggerNMI() { - Interrupt(NMIvector); -} - -uint16_t MOS6502::GetWord(uint16_t offset) const { - auto low = GetByte(offset); - auto high = GetByte((uint16_t)(offset + 1)); - return MakeWord(low, high); -} - -void MOS6502::Interrupt(uint16_t vector) { - PushWord(pc); - PushByte(p); - p.interrupt = true; - pc = GetWord(vector); -} - -void MOS6502::Execute(uint8_t cell) { - const auto& instruction = instructions[cell]; - const auto& method = instruction.vector; - method(); - cycles += instruction.count; -} - -void MOS6502::___() { - if (level >= ProcessorType::Cpu65SC02) { - // Generally, missing instructions act as a one byte, - // one cycle NOP instruction on 65c02 (ish) processors. - NOP_imp(); - cycles++; - } else { - throw new std::domain_error("Whoops: Invalid instruction."); - } -} - -void MOS6502::ResetRegisters() { - pc = 0; - x = 0x80; - y = 0; - a = 0; - - p = 0; - p.reserved = true; - - s = 0xff; -} - -MOS6502::Instruction MOS6502::INS(instruction_t method, uint64_t cycles, AddressingMode addressing, std::string display) { - MOS6502::Instruction returnValue; - returnValue.vector = method; - returnValue.count = cycles; - returnValue.mode = addressing; - returnValue.display = display; - return returnValue; -} - -uint8_t MOS6502::LowNybble(uint8_t value) { - return value & 0xf; -} - -uint8_t MOS6502::HighNybble(uint8_t value) { - return DemoteNybble(value); -} - -uint8_t MOS6502::PromoteNybble(uint8_t value) { - return value << 4; -} - -uint8_t MOS6502::DemoteNybble(uint8_t value) { - return value >> 4; -} - -uint8_t MOS6502::LowByte(uint16_t value) { - return value & 0xff; -} - -uint8_t MOS6502::HighByte(uint16_t value) { - return (value & ~0xff) >> 8; -} - -uint16_t MOS6502::MakeWord(uint8_t low, uint8_t high) { - return (high << 8) + low; -} - -//// - -#define BIND(method) std::bind(&MOS6502:: method, this) - -void MOS6502::Install6502Instructions() { - overlay6502 = { - //// 0 1 2 3 4 5 6 7 8 9 A B C D E F - /* 0 */ INS(BIND(BRK_imp), 7, AddressingMode::Implied, "BRK"), INS(BIND(ORA_xind), 6, AddressingMode::XIndexed, "ORA"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(ORA_zp), 4, AddressingMode::ZeroPage, "ORA"), INS(BIND(ASL_zp), 5, AddressingMode::ZeroPage, "ASL"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(PHP_imp), 3, AddressingMode::Implied, "PHP"), INS(BIND(ORA_imm), 2, AddressingMode::Immediate, "ORA"), INS(BIND(ASL_a), 2, AddressingMode::Accumulator, "ASL"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(ORA_abs), 4, AddressingMode::Absolute, "ORA"), INS(BIND(ASL_abs), 6, AddressingMode::Absolute, "ASL"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), - /* 1 */ INS(BIND(BPL_rel), 2, AddressingMode::Relative, "BPL"), INS(BIND(ORA_indy), 5, AddressingMode::IndexedY, "ORA"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(ORA_zpx), 4, AddressingMode::ZeroPageX, "ORA"), INS(BIND(ASL_zpx), 6, AddressingMode::ZeroPageX, "ASL"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(CLC_imp), 2, AddressingMode::Implied, "CLC"), INS(BIND(ORA_absy), 4, AddressingMode::AbsoluteY, "ORA"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(ORA_absx), 4, AddressingMode::AbsoluteX, "ORA"), INS(BIND(ASL_absx), 7, AddressingMode::AbsoluteX, "ASL"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), - /* 2 */ INS(BIND(JSR_abs), 6, AddressingMode::Absolute, "JSR"), INS(BIND(AND_xind), 6, AddressingMode::XIndexed, "AND"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(BIT_zp), 3, AddressingMode::ZeroPage, "BIT"), INS(BIND(AND_zp), 3, AddressingMode::ZeroPage, "AND"), INS(BIND(ROL_zp), 5, AddressingMode::ZeroPage, "ROL"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(PLP_imp), 4, AddressingMode::Implied, "PLP"), INS(BIND(AND_imm),2, AddressingMode::Immediate, "AND"), INS(BIND(ROL_a), 2, AddressingMode::Accumulator, "ROL"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(BIT_abs), 4, AddressingMode::Absolute, "BIT"), INS(BIND(AND_abs), 4, AddressingMode::Absolute, "AND"), INS(BIND(ROL_abs), 6, AddressingMode::Absolute, "ROL"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), - /* 3 */ INS(BIND(BMI_rel), 2, AddressingMode::Relative, "BMI"), INS(BIND(AND_indy), 5, AddressingMode::IndexedY, "AND"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(AND_zpx), 4, AddressingMode::ZeroPageX, "AND"), INS(BIND(ROL_zpx), 6, AddressingMode::ZeroPageX, "ROL"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(SEC_imp), 2, AddressingMode::Implied, "SEC"), INS(BIND(AND_absy), 4, AddressingMode::AbsoluteY, "AND"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(AND_absx), 4, AddressingMode::AbsoluteX, "AND"), INS(BIND(ROL_absx), 7, AddressingMode::AbsoluteX, "ROL"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), - /* 4 */ INS(BIND(RTI_imp), 6, AddressingMode::Implied, "RTI"), INS(BIND(EOR_xind), 6, AddressingMode::XIndexed, "EOR"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(EOR_zp), 3, AddressingMode::ZeroPage, "EOR"), INS(BIND(LSR_zp), 5, AddressingMode::ZeroPage, "LSR"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(PHA_imp), 3, AddressingMode::Implied, "PHA"), INS(BIND(EOR_imm),2, AddressingMode::Immediate, "EOR"), INS(BIND(LSR_a), 2, AddressingMode::Accumulator, "LSR"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(JMP_abs), 3, AddressingMode::Absolute, "JMP"), INS(BIND(EOR_abs), 4, AddressingMode::Absolute, "EOR"), INS(BIND(LSR_abs), 6, AddressingMode::Absolute, "LSR"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), - /* 5 */ INS(BIND(BVC_rel), 2, AddressingMode::Relative, "BVC"), INS(BIND(EOR_indy), 5, AddressingMode::IndexedY, "EOR"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(EOR_zpx), 4, AddressingMode::ZeroPageX, "EOR"), INS(BIND(LSR_zpx), 6, AddressingMode::ZeroPageX, "LSR"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(CLI_imp), 2, AddressingMode::Implied, "CLI"), INS(BIND(EOR_absy), 4, AddressingMode::AbsoluteY, "EOR"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(EOR_absx), 4, AddressingMode::AbsoluteX, "EOR"), INS(BIND(LSR_absx), 7, AddressingMode::AbsoluteX, "LSR"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), - /* 6 */ INS(BIND(RTS_imp), 6, AddressingMode::Implied, "RTS"), INS(BIND(ADC_xind), 6, AddressingMode::XIndexed, "ADC"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(ADC_zp), 3, AddressingMode::ZeroPage, "ADC"), INS(BIND(ROR_zp), 5, AddressingMode::ZeroPage, "ROR"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(PLA_imp), 4, AddressingMode::Implied, "PLA"), INS(BIND(ADC_imm),2, AddressingMode::Immediate, "ADC"), INS(BIND(ROR_a), 2, AddressingMode::Accumulator, "ROR"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(JMP_ind), 5, AddressingMode::Indirect, "JMP"), INS(BIND(ADC_abs), 4, AddressingMode::Absolute, "ADC"), INS(BIND(ROR_abs), 6, AddressingMode::Absolute, "ROR"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), - /* 7 */ INS(BIND(BVS_rel), 2, AddressingMode::Relative, "BVS"), INS(BIND(ADC_indy), 5, AddressingMode::IndexedY, "ADC"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(ADC_zpx), 4, AddressingMode::ZeroPageX, "ADC"), INS(BIND(ROR_zpx), 6, AddressingMode::ZeroPageX, "ROR"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(SEI_imp), 2, AddressingMode::Implied, "SEI"), INS(BIND(ADC_absy), 4, AddressingMode::AbsoluteY, "ADC"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(ADC_absx), 4, AddressingMode::AbsoluteX, "ADC"), INS(BIND(ROR_absx), 7, AddressingMode::AbsoluteX, "ROR"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), - /* 8 */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(STA_xind), 6, AddressingMode::XIndexed, "STA"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(STY_zp), 3, AddressingMode::ZeroPage, "STY"), INS(BIND(STA_zp), 3, AddressingMode::ZeroPage, "STA"), INS(BIND(STX_zp), 3, AddressingMode::ZeroPage, "STX"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(DEY_imp), 2, AddressingMode::Implied, "DEY"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(TXA_imp), 2, AddressingMode::Implied, "TXA"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(STY_abs), 4, AddressingMode::Absolute, "STY"), INS(BIND(STA_abs), 4, AddressingMode::Absolute, "STA"), INS(BIND(STX_abs), 4, AddressingMode::Absolute, "STX"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), - /* 9 */ INS(BIND(BCC_rel), 2, AddressingMode::Relative, "BCC"), INS(BIND(STA_indy), 6, AddressingMode::IndexedY, "STA"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(STY_zpx), 4, AddressingMode::ZeroPageX, "STY"), INS(BIND(STA_zpx), 4, AddressingMode::ZeroPageX, "STA"), INS(BIND(STX_zpy), 4, AddressingMode::ZeroPageY, "STX"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(TYA_imp), 2, AddressingMode::Implied, "TYA"), INS(BIND(STA_absy), 5, AddressingMode::AbsoluteY, "STA"), INS(BIND(TXS_imp), 2, AddressingMode::Implied, "TXS"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(STA_absx), 5, AddressingMode::AbsoluteX, "STA"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), - /* A */ INS(BIND(LDY_imm), 2, AddressingMode::Immediate, "LDY"), INS(BIND(LDA_xind), 6, AddressingMode::XIndexed, "LDA"), INS(BIND(LDX_imm), 2, AddressingMode::Immediate, "LDX"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(LDY_zp), 3, AddressingMode::ZeroPage, "LDY"), INS(BIND(LDA_zp), 3, AddressingMode::ZeroPage, "LDA"), INS(BIND(LDX_zp), 3, AddressingMode::ZeroPage, "LDX"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(TAY_imp), 2, AddressingMode::Implied, "TAY"), INS(BIND(LDA_imm),2, AddressingMode::Immediate, "LDA"), INS(BIND(TAX_imp), 2, AddressingMode::Implied, "TAX"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(LDY_abs), 4, AddressingMode::Absolute, "LDY"), INS(BIND(LDA_abs), 4, AddressingMode::Absolute, "LDA"), INS(BIND(LDX_abs), 4, AddressingMode::Absolute, "LDX"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), - /* B */ INS(BIND(BCS_rel), 2, AddressingMode::Relative, "BCS"), INS(BIND(LDA_indy), 5, AddressingMode::IndexedY, "LDA"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(LDY_zpx), 4, AddressingMode::ZeroPageX, "LDY"), INS(BIND(LDA_zpx), 4, AddressingMode::ZeroPageX, "LDA"), INS(BIND(LDX_zpy), 4, AddressingMode::ZeroPageY, "LDX"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(CLV_imp), 2, AddressingMode::Implied, "CLV"), INS(BIND(LDA_absy), 4, AddressingMode::AbsoluteY, "LDA"), INS(BIND(TSX_imp), 2, AddressingMode::Implied, "TSX"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(LDY_absx), 4, AddressingMode::AbsoluteX, "LDY"), INS(BIND(LDA_absx), 4, AddressingMode::AbsoluteX, "LDA"), INS(BIND(LDX_absy), 4, AddressingMode::AbsoluteY, "LDX"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), - /* C */ INS(BIND(CPY_imm), 2, AddressingMode::Immediate, "CPY"), INS(BIND(CMP_xind), 6, AddressingMode::XIndexed, "CMP"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(CPY_zp), 3, AddressingMode::ZeroPage, "CPY"), INS(BIND(CMP_zp), 3, AddressingMode::ZeroPage, "CMP"), INS(BIND(DEC_zp), 5, AddressingMode::ZeroPage, "DEC"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(INY_imp), 2, AddressingMode::Implied, "INY"), INS(BIND(CMP_imm),2, AddressingMode::Immediate, "CMP"), INS(BIND(DEX_imp), 2, AddressingMode::Implied, "DEX"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(CPY_abs), 4, AddressingMode::Absolute, "CPY"), INS(BIND(CMP_abs), 4, AddressingMode::Absolute, "CMP"), INS(BIND(DEC_abs), 6, AddressingMode::Absolute, "DEC"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), - /* D */ INS(BIND(BNE_rel), 2, AddressingMode::Relative, "BNE"), INS(BIND(CMP_indy), 5, AddressingMode::IndexedY, "CMP"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(CMP_zpx), 4, AddressingMode::ZeroPageX, "CMP"), INS(BIND(DEC_zpx), 6, AddressingMode::ZeroPageX, "DEC"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(CLD_imp), 2, AddressingMode::Implied, "CLD"), INS(BIND(CMP_absy), 4, AddressingMode::AbsoluteY, "CMP"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(CMP_absx), 4, AddressingMode::AbsoluteX, "CMP"), INS(BIND(DEC_absx), 7, AddressingMode::AbsoluteX, "DEC"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), - /* E */ INS(BIND(CPX_imm), 2, AddressingMode::Immediate, "CPX"), INS(BIND(SBC_xind), 6, AddressingMode::XIndexed, "SBC"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(CPX_zp), 3, AddressingMode::ZeroPage, "CPX"), INS(BIND(SBC_zp), 3, AddressingMode::ZeroPage, "SBC"), INS(BIND(INC_zp), 5, AddressingMode::ZeroPage, "INC"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(INX_imp), 2, AddressingMode::Implied, "INX"), INS(BIND(SBC_imm),2, AddressingMode::Immediate, "SBC"), INS(BIND(NOP_imp), 2, AddressingMode::Implied, "NOP"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(CPX_abs), 4, AddressingMode::Absolute, "CPX"), INS(BIND(SBC_abs), 4, AddressingMode::Absolute, "SBC"), INS(BIND(INC_abs), 6, AddressingMode::Absolute, "INC"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), - /* F */ INS(BIND(BEQ_rel), 2, AddressingMode::Relative, "BEQ"), INS(BIND(SBC_indy), 5, AddressingMode::IndexedY, "SBC"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(SBC_zpx), 4, AddressingMode::ZeroPageX, "SBC"), INS(BIND(INC_zpx), 6, AddressingMode::ZeroPageX, "INC"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(SED_imp), 2, AddressingMode::Implied, "SED"), INS(BIND(SBC_absy), 4, AddressingMode::AbsoluteY, "SBC"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(SBC_absx), 4, AddressingMode::AbsoluteX, "SBC"), INS(BIND(INC_absx), 7, AddressingMode::AbsoluteX, "INC"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), +EightBit::MOS6502::MOS6502(Memory& memory) +: Processor(memory) { + m_timings = { + //// 0 1 2 3 4 5 6 7 8 9 A B C D E F + /* 0 */ 7, 6, 0, 0, 0, 4, 5, 0, 3, 2, 2, 0, 0, 4, 6, 0, + /* 1 */ 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, + /* 2 */ 6, 6, 0, 0, 3, 3, 5, 0, 4, 2, 2, 0, 4, 4, 6, 0, + /* 3 */ 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, + /* 4 */ 6, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 3, 4, 6, 0, + /* 5 */ 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, + /* 6 */ 6, 6, 0, 0, 0, 3, 5, 0, 4, 2, 2, 0, 5, 4, 6, 0, + /* 7 */ 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, + /* 8 */ 0, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0, + /* 9 */ 2, 6, 0, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0, + /* A */ 2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0, + /* B */ 2, 5, 0, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0, + /* C */ 2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, + /* D */ 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, + /* E */ 2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, + /* F */ 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, }; - - InstallInstructionSet(overlay6502); } -void MOS6502::Install65sc02Instructions() { - if (level >= ProcessorType::Cpu65SC02) { - overlay65sc02 = { - //// 0 1 2 3 4 5 6 7 8 9 A B C D E F - /* 0 */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(NOP2_imp), 2, AddressingMode::Implied, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(TSB_zp), 5, AddressingMode::ZeroPage, "TSB"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(TSB_abs), 6, AddressingMode::Absolute, "TSB"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), - /* 1 */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(ORA_zpind), 5, AddressingMode::ZeroPageIndirect, "ORA"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(TRB_zp), 5, AddressingMode::ZeroPage, "TRB"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(INC_a), 2, AddressingMode::Accumulator, "INC"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(TRB_abs), 6, AddressingMode::Absolute, "TRB"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), - /* 2 */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(NOP2_imp), 2, AddressingMode::Implied, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), - /* 3 */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(AND_zpind), 5, AddressingMode::ZeroPageIndirect, "AND"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(BIT_zpx), 4, AddressingMode::ZeroPageX, "BIT"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(DEC_a), 2, AddressingMode::Accumulator, "DEC"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(BIT_absx), 4, AddressingMode::AbsoluteX, "BIT"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), - /* 4 */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(NOP2_imp), 2, AddressingMode::Implied, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(NOP2_imp), 3, AddressingMode::Implied, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), - /* 5 */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(EOR_zpind), 5, AddressingMode::ZeroPageIndirect, "EOR"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(NOP2_imp), 4, AddressingMode::Implied, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(PHY_imp), 2, AddressingMode::Implied, "PHY"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(NOP3_imp), 8, AddressingMode::Implied, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), - /* 6 */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(NOP2_imp), 2, AddressingMode::Implied, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(STZ_zp), 3, AddressingMode::ZeroPage, "STZ"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), - /* 7 */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(ADC_zpind), 5, AddressingMode::ZeroPageIndirect, "ADC"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(STZ_zpx), 4, AddressingMode::ZeroPageX, "STZ"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(PLY_imp), 2, AddressingMode::Implied, "PLY"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(JMP_absxind), 6, AddressingMode::AbsoluteXIndirect, "JMP"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), - /* 8 */ INS(BIND(BRA_rel), 2, AddressingMode::Relative, "BRA"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(NOP2_imp), 2, AddressingMode::Implied, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(BIT_imm),2, AddressingMode::Immediate, "BIT"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), - /* 9 */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(STA_zpind), 5, AddressingMode::ZeroPageIndirect, "STA"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(STZ_abs), 4, AddressingMode::Absolute, "STZ"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(STZ_absx), 2, AddressingMode::AbsoluteX, "STZ"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), - /* A */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), - /* B */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(LDA_zpind), 5, AddressingMode::ZeroPageIndirect, "LDA"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), - /* C */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(NOP2_imp), 2, AddressingMode::Implied, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), - /* D */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(CMP_zpind), 5, AddressingMode::ZeroPageIndirect, "CMP"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(NOP2_imp), 4, AddressingMode::Implied, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(PHX_imp), 2, AddressingMode::Implied, "PHX"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(NOP3_imp), 4, AddressingMode::Implied, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), - /* E */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(NOP2_imp), 2, AddressingMode::Implied, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), - /* F */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(SBC_zpind), 5, AddressingMode::ZeroPageIndirect, "SBC"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(NOP2_imp), 4, AddressingMode::Implied, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(PLX_imp), 2, AddressingMode::Implied, "PLX"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(NOP3_imp), 4, AddressingMode::Implied, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), - }; +EightBit::MOS6502::~MOS6502() { +} - OverlayInstructionSet(overlay65sc02); +void EightBit::MOS6502::initialise() { + + Processor::initialise(); + + for (int i = 0; i < 0x100; ++i) { + m_decodedOpcodes[i] = i; } + + PC().word = 0; + X() = Bit7; + Y() = 0; + A() = 0; + + P() = 0; + setFlag(P(), RF); + + S() = Mask8; + + MEMPTR().word = 0; } -void MOS6502::Install65c02Instructions() { - if (level >= ProcessorType::Cpu65C02) { - overlay65c02 = { - //// 0 1 2 3 4 5 6 7 8 9 A B C D E F - /* 0 */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(RMB0_zp), 5, AddressingMode::ZeroPage, "RMB0"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(BBR0_zprel), 5, AddressingMode::ZeroPageRelative, "BBR0"), - /* 1 */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(RMB1_zp), 5, AddressingMode::ZeroPage, "RMB1"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(BBR1_zprel), 5, AddressingMode::ZeroPageRelative, "BBR1"), - /* 2 */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(RMB2_zp), 5, AddressingMode::ZeroPage, "RMB2"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(BBR2_zprel), 5, AddressingMode::ZeroPageRelative, "BBR2"), - /* 3 */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(RMB3_zp), 5, AddressingMode::ZeroPage, "RMB3"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(BBR3_zprel), 5, AddressingMode::ZeroPageRelative, "BBR3"), - /* 4 */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(RMB4_zp), 5, AddressingMode::ZeroPage, "RMB4"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(BBR4_zprel), 5, AddressingMode::ZeroPageRelative, "BBR4"), - /* 5 */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(RMB5_zp), 5, AddressingMode::ZeroPage, "RMB5"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(BBR5_zprel), 5, AddressingMode::ZeroPageRelative, "BBR5"), - /* 6 */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(RMB6_zp), 5, AddressingMode::ZeroPage, "RMB6"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(BBR6_zprel), 5, AddressingMode::ZeroPageRelative, "BBR6"), - /* 7 */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(RMB7_zp), 5, AddressingMode::ZeroPage, "RMB7"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(BBR7_zprel), 5, AddressingMode::ZeroPageRelative, "BBR7"), - /* 8 */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(SMB0_zp), 5, AddressingMode::ZeroPage, "SMB0"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(BBS0_zprel), 5, AddressingMode::ZeroPageRelative, "BBS0"), - /* 9 */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(SMB1_zp), 5, AddressingMode::ZeroPage, "SMB1"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(BBS1_zprel), 5, AddressingMode::ZeroPageRelative, "BBS1"), - /* A */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(SMB2_zp), 5, AddressingMode::ZeroPage, "SMB2"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(BBS2_zprel), 5, AddressingMode::ZeroPageRelative, "BBS2"), - /* B */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(SMB3_zp), 5, AddressingMode::ZeroPage, "SMB3"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(BBS3_zprel), 5, AddressingMode::ZeroPageRelative, "BBS3"), - /* C */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(SMB4_zp), 5, AddressingMode::ZeroPage, "SMB4"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(WAI_imp), 3, AddressingMode::Implied, "WAI"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(BBS4_zprel), 5, AddressingMode::ZeroPageRelative, "BBS4"), - /* D */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(SMB5_zp), 5, AddressingMode::ZeroPage, "SMB5"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(STP_imp), 3, AddressingMode::Implied, "STP"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(BBS5_zprel), 5, AddressingMode::ZeroPageRelative, "BBS5"), - /* E */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(SMB6_zp), 5, AddressingMode::ZeroPage, "SMB6"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(BBS6_zprel), 5, AddressingMode::ZeroPageRelative, "BBS6"), - /* F */ INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(SMB7_zp), 5, AddressingMode::ZeroPage, "SMB7"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 0, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(___), 2, AddressingMode::Illegal, "___"), INS(BIND(BBS7_zprel), 5, AddressingMode::ZeroPageRelative, "BBS7"), - }; - - OverlayInstructionSet(overlay65c02); - } +int EightBit::MOS6502::step() { + ExecutingInstruction.fire(*this); + auto returned = execute(fetchByte()); + ExecutedInstruction.fire(*this); + return returned; } -void MOS6502::InstallInstructionSet(std::array basis) { - OverlayInstructionSet(basis, true); +void EightBit::MOS6502::reset() { + getWord(RSTvector, PC()); } -void MOS6502::OverlayInstructionSet(std::array overlay) { - OverlayInstructionSet(overlay, false); +void EightBit::MOS6502::triggerIRQ() { + interrupt(IRQvector); } -void MOS6502::OverlayInstructionSet(std::array overlay, bool includeIllegal) { - for (uint16_t i = 0; i < 0x100; ++i) { - auto newInstruction = overlay[i]; - auto illegal = newInstruction.mode == AddressingMode::Illegal; - if (includeIllegal || !illegal) { - auto oldInstruction = instructions[i]; - if (oldInstruction.mode != AddressingMode::Illegal) { - throw new std::domain_error("Whoops: replacing a non-missing instruction."); +void EightBit::MOS6502::triggerNMI() { + interrupt(NMIvector); +} + +void EightBit::MOS6502::getWord(register16_t& output) { + output.low = getByte(); + m_memory.ADDRESS().word++; + output.high = getByte(); +} + +void EightBit::MOS6502::getWord(uint16_t offset, register16_t& output) { + m_memory.ADDRESS().word = offset; + getWord(output); +} + +void EightBit::MOS6502::interrupt(uint16_t vector) { + pushWord(PC()); + pushByte(P()); + setFlag(P(), IF); + getWord(vector, PC()); +} + +int EightBit::MOS6502::execute(uint8_t cell) { + + cycles = m_timings[cell]; + + // http://www.llx.com/~nparker/a2/opcodes.html + + // Most instructions that explicitly reference memory + // locations have bit patterns of the form aaabbbcc. + // The aaa and cc bits determine the opcode, and the bbb + // bits determine the addressing mode. + + const auto& decoded = m_decodedOpcodes[cell]; + + switch (decoded.cc) { + case 0b00: + switch (decoded.aaa) { + case 0b000: + switch (decoded.bbb) { + case 0b000: // BRK + BRK(); + break; + case 0b010: // PHP + PHP(); + break; + case 0b100: // BPL + Branch(!(P() & NF)); + break; + case 0b110: // CLC + clearFlag(P(), CF); + break; + default: + throw std::domain_error("Illegal instruction"); } - - instructions[i] = newInstruction; + break; + case 0b001: + switch (decoded.bbb) { + case 0b000: // JSR + JSR_abs(); + break; + case 0b010: // PLP + PLP(); + break; + case 0b100: // BMI + Branch((P() & NF) != 0); + break; + case 0b110: // SEC + setFlag(P(), CF); + break; + default: // BIT + BIT(AM_00(decoded.bbb)); + firePendingBusEvents(BusDirection::Read); + break; + } + break; + case 0b010: + switch (decoded.bbb) { + case 0b000: // RTI + RTI(); + break; + case 0b010: // PHA + pushByte(A()); + break; + case 0b011: // JMP + JMP_abs(); + break; + case 0b100: // BVC + Branch(!(P() & VF)); + break; + case 0b110: // CLI + clearFlag(P(), IF); + break; + default: + throw std::domain_error("Illegal addressing mode"); + } + break; + case 0b011: + switch (decoded.bbb) { + case 0b000: // RTS + RTS(); + break; + case 0b010: // PLA + adjustNZ(A() = popByte()); + break; + case 0b011: // JMP (abs) + JMP_ind(); + break; + case 0b100: // BVS + Branch((P() & VF) != 0); + break; + case 0b110: // SEI + setFlag(P(), IF); + break; + default: + throw std::domain_error("Illegal addressing mode"); + } + break; + case 0b100: + switch (decoded.bbb) { + case 0b010: // DEY + adjustNZ(--Y()); + break; + case 0b100: // BCC + Branch(!(P() & CF)); + break; + case 0b110: // TYA + adjustNZ(A() = Y()); + break; + default: // STY + AM_00(decoded.bbb, BusDirection::Write) = Y(); + firePendingBusEvents(BusDirection::Write); + break; + } + break; + case 0b101: + switch (decoded.bbb) { + case 0b010: // TAY + adjustNZ(Y() = A()); + break; + case 0b100: // BCS + Branch((P() & CF) != 0); + break; + case 0b110: // CLV + clearFlag(P(), VF); + break; + default: // LDY + adjustNZ(Y() = AM_00(decoded.bbb)); + firePendingBusEvents(BusDirection::Read); + break; + } + break; + case 0b110: + switch (decoded.bbb) { + case 0b010: // INY + adjustNZ(++Y()); + break; + case 0b100: // BNE + Branch(!(P() & ZF)); + break; + case 0b110: // CLD + clearFlag(P(), DF); + break; + default: // CPY + CMP(Y(), AM_00(decoded.bbb)); + firePendingBusEvents(BusDirection::Read); + break; + } + break; + case 0b111: + switch (decoded.bbb) { + case 0b010: // INX + adjustNZ(++X()); + break; + case 0b100: // BEQ + Branch((P() & ZF) != 0); + break; + case 0b110: // SED + setFlag(P(), DF); + break; + default: // CPX + CMP(X(), AM_00(decoded.bbb)); + firePendingBusEvents(BusDirection::Read); + break; + } + break; } + break; + case 0b01: + switch (decoded.aaa) { + case 0b000: // ORA + adjustNZ(A() |= AM_01(decoded.bbb)); + firePendingBusEvents(BusDirection::Read); + break; + case 0b001: // AND + adjustNZ(A() &= AM_01(decoded.bbb)); + firePendingBusEvents(BusDirection::Read); + break; + case 0b010: // EOR + adjustNZ(A() ^= AM_01(decoded.bbb)); + firePendingBusEvents(BusDirection::Read); + break; + case 0b011: // ADC + ADC(AM_01(decoded.bbb)); + firePendingBusEvents(BusDirection::Read); + break; + case 0b100: // STA + AM_01(decoded.bbb, BusDirection::Write) = A(); + firePendingBusEvents(BusDirection::Write); + break; + case 0b101: // LDA + adjustNZ(A() = AM_01(decoded.bbb)); + firePendingBusEvents(BusDirection::Read); + break; + case 0b110: // CMP + CMP(A(), AM_01(decoded.bbb)); + firePendingBusEvents(BusDirection::Read); + break; + case 0b111: // SBC + SBC(AM_01(decoded.bbb)); + firePendingBusEvents(BusDirection::Read); + break; + default: + __assume(0); + } + break; + case 0b10: + switch (decoded.aaa) { + case 0b000: // ASL + ASL(decoded.bbb); + break; + case 0b001: // ROL + ROL(decoded.bbb); + break; + case 0b010: // LSR + LSR(decoded.bbb); + break; + case 0b011: // ROR + ROR(decoded.bbb); + break; + case 0b100: + switch (decoded.bbb) { + case 0b010: // TXA + adjustNZ(A() = X()); + break; + case 0b110: // TXS + S() = X(); + break; + default: // STX + AM_10_x(decoded.bbb, BusDirection::Write) = X(); + firePendingBusEvents(BusDirection::Write); + break; + } + break; + case 0b101: + switch (decoded.bbb) { + case 0b110: // TSX + adjustNZ(X() = S()); + break; + default: // LDX + adjustNZ(X() = AM_10_x(decoded.bbb)); + firePendingBusEvents(BusDirection::Read); + break; + } + break; + case 0b110: + switch (decoded.bbb) { + case 0b010: // DEX + adjustNZ(--X()); + break; + default: // DEC + DEC(decoded.bbb); + break; + } + break; + case 0b111: + switch (decoded.bbb) { + case 0b010: // NOP + break; + default: // INC + INC(decoded.bbb); + break; + } + break; + default: + __assume(0); + } + break; + case 0b11: + throw std::domain_error("Illegal instruction group"); + default: + __assume(0); } + + return cycles; } //// -bool MOS6502::UpdateZeroFlag(uint8_t datum) { - return p.zero = datum == 0; +void EightBit::MOS6502::pushByte(uint8_t value) { + setByte(PageOne + S()--, value); } -bool MOS6502::UpdateNegativeFlag(int8_t datum) { - return p.negative = datum < 0; +uint8_t EightBit::MOS6502::popByte() { + return getByte(PageOne + ++S()); } -void MOS6502::UpdateZeroNegativeFlags(uint8_t datum) { - if (UpdateNegativeFlag((int8_t)datum)) - p.zero = false; - else - UpdateZeroFlag(datum); +void EightBit::MOS6502::pushWord(register16_t value) { + pushByte(value.high); + pushByte(value.low); +} + +void EightBit::MOS6502::popWord(register16_t& output) { + output.low = popByte(); + output.high = popByte(); +} + +uint8_t EightBit::MOS6502::fetchByte() { + m_memory.ADDRESS().word = PC().word++; + return getByte(); +} + +void EightBit::MOS6502::fetchWord(register16_t& output) { + m_memory.ADDRESS().word = PC().word++; + getWord(output); + PC().word++; } //// -void MOS6502::PushByte(uint8_t value) { - SetByte(PageOne + s--, value); +void EightBit::MOS6502::ROR(uint8_t& output) { + auto carry = P() & CF; + setFlag(P(), CF, output & CF); + output = (output >> 1) | (carry << 7); + adjustNZ(output); } -uint8_t MOS6502::PopByte() { - return GetByte(PageOne + ++s); +void EightBit::MOS6502::LSR(uint8_t& output) { + setFlag(P(), CF, output & CF); + adjustNZ(output >>= 1); } -void MOS6502::PushWord(uint16_t value) { - PushByte(HighByte(value)); - PushByte(LowByte(value)); +void EightBit::MOS6502::BIT(uint8_t data) { + adjustZero(A() & data); + adjustNegative(data); + setFlag(P(), VF, data & VF); } -uint16_t MOS6502::PopWord() { - auto low = PopByte(); - auto high = PopByte(); - return MakeWord(low, high); +void EightBit::MOS6502::ROL(uint8_t& output) { + uint8_t result = (output << 1) | (P() & CF); + setFlag(P(), CF, output & Bit7); + adjustNZ(output = result); } -uint8_t MOS6502::FetchByte() { - return GetByte(pc++); +void EightBit::MOS6502::ASL(uint8_t& output) { + setFlag(P(), CF, (output & Bit7) >> 7); + adjustNZ(output <<= 1); } -uint16_t MOS6502::FetchWord() { - auto word = GetWord(pc); - pc += 2; - return word; -} - -//// - -uint16_t MOS6502::Address_ZeroPage() { - return FetchByte(); -} - -uint16_t MOS6502::Address_ZeroPageX() { - return LowByte(FetchByte() + x); -} - -uint16_t MOS6502::Address_ZeroPageY() { - return LowByte(FetchByte() + y); -} - -uint16_t MOS6502::Address_IndexedIndirectX() { - return GetWord(Address_ZeroPageX()); -} - -uint16_t MOS6502::Address_IndexedIndirectY_Read() { - auto indirection = GetWord(FetchByte()); - if (LowByte(indirection) == 0xff) - ++cycles; - return indirection + y; -} - -uint16_t MOS6502::Address_IndexedIndirectY_Write() { - return GetWord(FetchByte()) + y; -} - -uint16_t MOS6502::Address_Absolute() { - return FetchWord(); -} - -uint16_t MOS6502::Address_AbsoluteXIndirect() { - return GetWord(FetchWord() + x); -} - -uint16_t MOS6502::Address_AbsoluteX_Read() { - auto address = FetchWord(); - auto offset = (uint16_t)(address + x); - if (LowByte(offset) == 0xff) - ++cycles; - return offset; -} - -uint16_t MOS6502::Address_AbsoluteX_Write() { - return FetchWord() + x; -} - -uint16_t MOS6502::Address_AbsoluteY_Read() { - auto address = FetchWord(); - auto offset = (uint16_t)(address + y); - if (LowByte(offset) == 0xff) - ++cycles; - return offset; -} - -uint16_t MOS6502::Address_AbsoluteY_Write() { - return FetchWord() + y; -} - -uint16_t MOS6502::Address_ZeroPageIndirect() { - return GetWord(FetchByte()); -} - -//// - -uint8_t MOS6502::ReadByte_Immediate() { - return FetchByte(); -} - -int8_t MOS6502::ReadByte_ImmediateDisplacement() { - return FetchByte(); -} - -uint8_t MOS6502::ReadByte_ZeroPage() { - return GetByte(Address_ZeroPage()); -} - -uint8_t MOS6502::ReadByte_ZeroPageX() { - return GetByte(Address_ZeroPageX()); -} - -uint8_t MOS6502::ReadByte_ZeroPageY() { - return GetByte(Address_ZeroPageY()); -} - -uint8_t MOS6502::ReadByte_Absolute() { - return GetByte(Address_Absolute()); -} - -uint8_t MOS6502::ReadByte_AbsoluteX() { - return GetByte(Address_AbsoluteX_Read()); -} - -uint8_t MOS6502::ReadByte_AbsoluteY() { - return GetByte(Address_AbsoluteY_Read()); -} - -uint8_t MOS6502::ReadByte_IndexedIndirectX() { - return GetByte(Address_IndexedIndirectX()); -} - -uint8_t MOS6502::ReadByte_IndirectIndexedY() { - return GetByte(Address_IndexedIndirectY_Read()); -} - -uint8_t MOS6502::ReadByte_ZeroPageIndirect() { - return GetByte(Address_ZeroPageIndirect()); -} - -//// - -void MOS6502::WriteByte_ZeroPage(uint8_t value) { - SetByte(Address_ZeroPage(), value); -} - -void MOS6502::WriteByte_Absolute(uint8_t value) { - SetByte(Address_Absolute(), value); -} - -void MOS6502::WriteByte_AbsoluteX(uint8_t value) { - SetByte(Address_AbsoluteX_Write(), value); -} - -void MOS6502::WriteByte_AbsoluteY(uint8_t value) { - SetByte(Address_AbsoluteY_Write(), value); -} - -void MOS6502::WriteByte_ZeroPageX(uint8_t value) { - SetByte(Address_ZeroPageX(), value); -} - -void MOS6502::WriteByte_ZeroPageY(uint8_t value) { - SetByte(Address_ZeroPageY(), value); -} - -void MOS6502::WriteByte_IndirectIndexedY(uint8_t value) { - SetByte(Address_IndexedIndirectY_Write(), value); -} - -void MOS6502::WriteByte_IndexedIndirectX(uint8_t value) { - SetByte(Address_IndexedIndirectX(), value); -} - -void MOS6502::WriteByte_ZeroPageIndirect(uint8_t value) { - SetByte(Address_ZeroPageIndirect(), value); -} - -//// - -void MOS6502::DEC(uint16_t offset) { - auto content = GetByte(offset); - SetByte(offset, --content); - UpdateZeroNegativeFlags(content); -} - -uint8_t MOS6502::ROR(uint8_t data) { - auto carry = p.carry; - - p.carry = (data & 1) != 0; - - auto result = (uint8_t)(data >> 1); - if (carry) - result |= 0x80; - - UpdateZeroNegativeFlags(result); - - return result; -} - -void MOS6502::ROR(uint16_t offset) { - SetByte(offset, ROR(GetByte(offset))); -} - -uint8_t MOS6502::LSR(uint8_t data) { - p.carry = (data & 1) != 0; - - auto result = (uint8_t)(data >> 1); - - UpdateZeroNegativeFlags(result); - - return result; -} - -void MOS6502::LSR(uint16_t offset) { - SetByte(offset, LSR(GetByte(offset))); -} - -void MOS6502::BIT_immediate(uint8_t data) { - auto result = (uint8_t)(a & data); - UpdateZeroFlag(result); -} - -void MOS6502::BIT(uint8_t data) { - BIT_immediate(data); - p.negative = (data & 0x80) != 0; - p.overflow = (data & 0x40) != 0; -} - -void MOS6502::TSB(uint16_t address) { - auto content = GetByte(address); - BIT_immediate(content); - uint8_t result = content | a; - SetByte(address, result); -} - -void MOS6502::TRB(uint16_t address) { - auto content = GetByte(address); - BIT_immediate(content); - uint8_t result = content & ~a; - SetByte(address, result); -} - -void MOS6502::INC(uint16_t offset) { - auto content = GetByte(offset); - SetByte(offset, ++content); - UpdateZeroNegativeFlags(content); -} - -void MOS6502::ROL(uint16_t offset) { - SetByte(offset, ROL(GetByte(offset))); -} - -uint8_t MOS6502::ROL(uint8_t data) { - auto carry = p.carry; - - p.carry = (data & 0x80) != 0; - - uint8_t result = data << 1; - - if (carry) - result |= 1; - - UpdateZeroNegativeFlags(result); - - return result; -} - -void MOS6502::ASL(uint16_t offset) { - SetByte(offset, ASL(GetByte(offset))); -} - -uint8_t MOS6502::ASL(uint8_t data) { - uint8_t result = data << 1; - UpdateZeroNegativeFlags(result); - p.carry = (data & 0x80) != 0; - return result; -} - -void MOS6502::ORA(uint8_t data) { - a |= data; - UpdateZeroNegativeFlags(a); -} - -void MOS6502::AND(uint8_t data) { - a &= data; - UpdateZeroNegativeFlags(a); -} - -void MOS6502::SBC(uint8_t data) { - if (p.decimal) +void EightBit::MOS6502::SBC(uint8_t data) { + if (P() & DF) SBC_d(data); else SBC_b(data); } -void MOS6502::SBC_b(uint8_t data) { - auto carry = p.carry ? 0 : 1; - auto difference = a - data - carry; +void EightBit::MOS6502::SBC_b(uint8_t data) { + register16_t difference; + difference.word = A() - data - (~P() & CF); - UpdateZeroNegativeFlags((uint8_t)difference); - p.overflow = ((a ^ data) & (a ^ difference) & 0x80) != 0; - p.carry = HighByte((uint16_t)difference) == 0; + adjustNZ(difference.low); + setFlag(P(), VF, (A() ^ data) & (A() ^ difference.low) & NF); + clearFlag(P(), CF, difference.high); - a = (uint8_t)difference; + A() = difference.low; } -void MOS6502::SBC_d(uint8_t data) { - auto carry = p.carry ? 0 : 1; - auto difference = a - data - carry; +void EightBit::MOS6502::SBC_d(uint8_t data) { + auto carry = ~P() & CF; - if (level < ProcessorType::Cpu65SC02) - UpdateZeroNegativeFlags((uint8_t)difference); + register16_t difference; + difference.word = A() - data - carry; - p.overflow = ((a ^ data) & (a ^ difference) & 0x80) != 0; - p.carry = HighByte((uint16_t)difference) == 0; + adjustNZ(difference.low); - auto low = (uint8_t)(LowNybble(a) - LowNybble(data) - carry); + setFlag(P(), VF, (A() ^ data) & (A() ^ difference.low) & NF); + clearFlag(P(), CF, difference.high); + + auto low = (uint8_t)(lowNibble(A()) - lowNibble(data) - carry); auto lowNegative = (int8_t)low < 0; if (lowNegative) low -= 6; - uint8_t high = HighNybble(a) - HighNybble(data) - (lowNegative ? 1 : 0); + uint8_t high = highNibble(A()) - highNibble(data) - (lowNegative ? 1 : 0); if ((int8_t)high < 0) high -= 6; - a = PromoteNybble(high) | LowNybble(low); - if (level >= ProcessorType::Cpu65SC02) - UpdateZeroNegativeFlags(a); + A() = promoteNibble(high) | lowNibble(low); } -void MOS6502::EOR(uint8_t data) { - a ^= data; - UpdateZeroNegativeFlags(a); +void EightBit::MOS6502::CMP(uint8_t first, uint8_t second) { + register16_t result; + result.word = first - second; + adjustNZ(result.low); + clearFlag(P(), CF, result.high); } -void MOS6502::CPX(uint8_t data) { - CMP(x, data); -} - -void MOS6502::CPY(uint8_t data) { - CMP(y, data); -} - -void MOS6502::CMP(uint8_t data) { - CMP(a, data); -} - -void MOS6502::CMP(uint8_t first, uint8_t second) { - uint16_t result = first - second; - UpdateZeroNegativeFlags((uint8_t)result); - p.carry = HighByte(result) == 0; -} - -void MOS6502::LDA(uint8_t data) { - a = data; - UpdateZeroNegativeFlags(a); -} - -void MOS6502::LDY(uint8_t data) { - y = data; - UpdateZeroNegativeFlags(y); -} - -void MOS6502::LDX(uint8_t data) { - x = data; - UpdateZeroNegativeFlags(x); -} - -void MOS6502::ADC(uint8_t data) { - if (p.decimal) +void EightBit::MOS6502::ADC(uint8_t data) { + if (P() & DF) ADC_d(data); else ADC_b(data); } -void MOS6502::ADC_b(uint8_t data) { - auto carry = (uint8_t)(p.carry ? 1 : 0); - auto sum = (uint16_t)(a + data + carry); +void EightBit::MOS6502::ADC_b(uint8_t data) { - UpdateZeroNegativeFlags((uint8_t)sum); - p.overflow = (~(a ^ data) & (a ^ sum) & 0x80) != 0; - p.carry = HighByte(sum) != 0; + register16_t sum; + sum.word = A() + data + (P() & CF); - a = (uint8_t)sum; + adjustNZ(sum.low); + setFlag(P(), VF, ~(A() ^ data) & (A() ^ sum.low) & NF); + setFlag(P(), CF, sum.high & CF); + + A() = sum.low; } -void MOS6502::ADC_d(uint8_t data) { - auto carry = (uint8_t)(p.carry ? 1 : 0); - auto sum = (uint16_t)(a + data + carry); +void EightBit::MOS6502::ADC_d(uint8_t data) { + auto carry = P() & CF; - if (level < ProcessorType::Cpu65SC02) - UpdateZeroNegativeFlags((uint8_t)sum); + register16_t sum; + sum.word = A() + data + carry; - auto low = (uint8_t)(LowNybble(a) + LowNybble(data) + carry); + adjustNZ(sum.low); + + auto low = (uint8_t)(lowNibble(A()) + lowNibble(data) + carry); if (low > 9) low += 6; - auto high = (uint8_t)(HighNybble(a) + HighNybble(data) + (low > 0xf ? 1 : 0)); - p.overflow = (~(a ^ data) & (a ^ PromoteNybble(high)) & 0x80) != 0; + auto high = (uint8_t)(highNibble(A()) + highNibble(data) + (low > 0xf ? 1 : 0)); + setFlag(P(), VF, ~(A() ^ data) & (A() ^ promoteNibble(high)) & NF); if (high > 9) high += 6; - p.carry = high > 0xf; + setFlag(P(), CF, high > 0xf); - a = (uint8_t)(PromoteNybble(high) | LowNybble(low)); - if (level >= ProcessorType::Cpu65SC02) - UpdateZeroNegativeFlags(a); + A() = (uint8_t)(promoteNibble(high) | lowNibble(low)); } //// -void MOS6502::RMB(uint16_t address, uint8_t flag) { - auto data = GetByte(address); - data &= (uint8_t)~flag; - SetByte(address, data); +void EightBit::MOS6502::Branch(int8_t displacement) { + auto page = PC().high; + PC().word += displacement; + if (PC().high != page) + cycles++; + cycles++; } -void MOS6502::SMB(uint16_t address, uint8_t flag) { - auto data = GetByte(address); - data |= flag; - SetByte(address, data); -} - -//// - -void MOS6502::Branch(int8_t displacement) { - ++cycles; - auto oldPage = HighByte(pc); - pc += (uint16_t)((short)displacement); - auto newPage = HighByte(pc); - if (oldPage != newPage) - cycles += 2; -} - -void MOS6502::Branch() { - auto displacement = ReadByte_ImmediateDisplacement(); - Branch(displacement); -} - -void MOS6502::Branch(bool flag) { - auto displacement = ReadByte_ImmediateDisplacement(); +void EightBit::MOS6502::Branch(bool flag) { + int8_t displacement = AM_Immediate(); if (flag) Branch(displacement); } -void MOS6502::BitBranch_Clear(uint8_t check) { - auto zp = FetchByte(); - auto contents = GetByte(zp); - auto displacement = FetchByte(); - if ((contents & check) == 0) - pc += (uint16_t)displacement; +// + +void EightBit::MOS6502::PHP() { + setFlag(P(), BF); + pushByte(P()); } -void MOS6502::BitBranch_Set(uint8_t check) { - auto zp = FetchByte(); - auto contents = GetByte(zp); - auto displacement = FetchByte(); - if ((contents & check) != 0) - pc += (uint16_t)displacement; +void EightBit::MOS6502::PLP() { + P() = popByte(); + setFlag(P(), RF); } // -void MOS6502::NOP_imp() { +void EightBit::MOS6502::JSR_abs() { + Address_Absolute(); + PC().word--; + pushWord(PC()); + PC() = MEMPTR(); } -void MOS6502::NOP2_imp() { - FetchByte(); +void EightBit::MOS6502::RTI() { + PLP(); + popWord(PC()); } -void MOS6502::NOP3_imp() { - FetchWord(); +void EightBit::MOS6502::RTS() { + popWord(PC()); + PC().word++; } -// - -void MOS6502::ORA_xind() { - ORA(ReadByte_IndexedIndirectX()); -} - -void MOS6502::ORA_zp() { - ORA(ReadByte_ZeroPage()); -} - -void MOS6502::ORA_imm() { - ORA(ReadByte_Immediate()); -} - -void MOS6502::ORA_abs() { - ORA(ReadByte_Absolute()); -} - -void MOS6502::ORA_absx() { - ORA(ReadByte_AbsoluteX()); -} - -void MOS6502::ORA_absy() { - ORA(ReadByte_AbsoluteY()); -} - -void MOS6502::ORA_zpx() { - ORA(ReadByte_ZeroPageX()); -} - -void MOS6502::ORA_indy() { - ORA(ReadByte_IndirectIndexedY()); -} - -void MOS6502::ORA_zpind() { - ORA(ReadByte_ZeroPageIndirect()); -} - -// - -void MOS6502::AND_zpx() { - AND(ReadByte_ZeroPageX()); -} - -void MOS6502::AND_indy() { - AND(ReadByte_IndirectIndexedY()); -} - -void MOS6502::AND_zp() { - AND(ReadByte_ZeroPage()); -} - -void MOS6502::AND_absx() { - AND(ReadByte_AbsoluteX()); -} - -void MOS6502::AND_absy() { - AND(ReadByte_AbsoluteY()); -} - -void MOS6502::AND_imm() { - AND(ReadByte_Immediate()); -} - -void MOS6502::AND_xind() { - AND(ReadByte_IndexedIndirectX()); -} - -void MOS6502::AND_abs() { - AND(ReadByte_Absolute()); -} - -void MOS6502::AND_zpind() { - AND(ReadByte_ZeroPageIndirect()); -} - -// - -void MOS6502::EOR_absx() { - EOR(ReadByte_AbsoluteX()); -} - -void MOS6502::EOR_absy() { - EOR(ReadByte_AbsoluteY()); -} - -void MOS6502::EOR_zpx() { - EOR(ReadByte_ZeroPageX()); -} - -void MOS6502::EOR_indy() { - EOR(ReadByte_IndirectIndexedY()); -} - -void MOS6502::EOR_abs() { - EOR(ReadByte_Absolute()); -} - -void MOS6502::EOR_imm() { - EOR(ReadByte_Immediate()); -} - -void MOS6502::EOR_zp() { - EOR(ReadByte_ZeroPage()); -} - -void MOS6502::EOR_xind() { - EOR(ReadByte_IndexedIndirectX()); -} - -void MOS6502::EOR_zpind() { - EOR(ReadByte_ZeroPageIndirect()); -} - -// - -void MOS6502::LDA_absx() { - LDA(ReadByte_AbsoluteX()); -} - -void MOS6502::LDA_absy() { - LDA(ReadByte_AbsoluteY()); -} - -void MOS6502::LDA_zpx() { - LDA(ReadByte_ZeroPageX()); -} - -void MOS6502::LDA_indy() { - LDA(ReadByte_IndirectIndexedY()); -} - -void MOS6502::LDA_abs() { - LDA(ReadByte_Absolute()); -} - -void MOS6502::LDA_imm() { - LDA(ReadByte_Immediate()); -} - -void MOS6502::LDA_zp() { - LDA(ReadByte_ZeroPage()); -} - -void MOS6502::LDA_xind() { - LDA(ReadByte_IndexedIndirectX()); -} - -void MOS6502::LDA_zpind() { - LDA(ReadByte_ZeroPageIndirect()); -} - -// - -void MOS6502::LDX_imm() { - LDX(ReadByte_Immediate()); -} - -void MOS6502::LDX_zp() { - LDX(ReadByte_ZeroPage()); -} - -void MOS6502::LDX_abs() { - LDX(ReadByte_Absolute()); -} - -void MOS6502::LDX_zpy() { - LDX(ReadByte_ZeroPageY()); -} - -void MOS6502::LDX_absy() { - LDX(ReadByte_AbsoluteY()); -} - -// - -void MOS6502::LDY_imm() { - LDY(ReadByte_Immediate()); -} - -void MOS6502::LDY_zp() { - LDY(ReadByte_ZeroPage()); -} - -void MOS6502::LDY_abs() { - LDY(ReadByte_Absolute()); -} - -void MOS6502::LDY_zpx() { - LDY(ReadByte_ZeroPageX()); -} - -void MOS6502::LDY_absx() { - LDY(ReadByte_AbsoluteX()); -} - -// - -void MOS6502::CMP_absx() { - CMP(ReadByte_AbsoluteX()); -} - -void MOS6502::CMP_absy() { - CMP(ReadByte_AbsoluteY()); -} - -void MOS6502::CMP_zpx() { - CMP(ReadByte_ZeroPageX()); -} - -void MOS6502::CMP_indy() { - CMP(ReadByte_IndirectIndexedY()); -} - -void MOS6502::CMP_abs() { - CMP(ReadByte_Absolute()); -} - -void MOS6502::CMP_imm() { - CMP(ReadByte_Immediate()); -} - -void MOS6502::CMP_zp() { - CMP(ReadByte_ZeroPage()); -} - -void MOS6502::CMP_xind() { - CMP(ReadByte_IndexedIndirectX()); -} - -void MOS6502::CMP_zpind() { - CMP(ReadByte_ZeroPageIndirect()); -} - -// - -void MOS6502::CPX_abs() { - CPX(ReadByte_Absolute()); -} - -void MOS6502::CPX_zp() { - CPX(ReadByte_ZeroPage()); -} - -void MOS6502::CPX_imm() { - CPX(ReadByte_Immediate()); -} - -// - -void MOS6502::CPY_imm() { - CPY(ReadByte_Immediate()); -} - -void MOS6502::CPY_zp() { - CPY(ReadByte_ZeroPage()); -} - -void MOS6502::CPY_abs() { - CPY(ReadByte_Absolute()); -} - -// - -void MOS6502::ADC_zp() { - ADC(ReadByte_ZeroPage()); -} - -void MOS6502::ADC_xind() { - ADC(ReadByte_IndexedIndirectX()); -} - -void MOS6502::ADC_imm() { - ADC(ReadByte_Immediate()); -} - -void MOS6502::ADC_abs() { - ADC(ReadByte_Absolute()); -} - -void MOS6502::ADC_zpx() { - ADC(ReadByte_ZeroPageX()); -} - -void MOS6502::ADC_indy() { - ADC(ReadByte_IndirectIndexedY()); -} - -void MOS6502::ADC_absx() { - ADC(ReadByte_AbsoluteX()); -} - -void MOS6502::ADC_absy() { - ADC(ReadByte_AbsoluteY()); -} - -void MOS6502::ADC_zpind() { - ADC(ReadByte_ZeroPageIndirect()); -} - -// - -void MOS6502::SBC_xind() { - SBC(ReadByte_IndexedIndirectX()); -} - -void MOS6502::SBC_zp() { - SBC(ReadByte_ZeroPage()); -} - -void MOS6502::SBC_imm() { - SBC(ReadByte_Immediate()); -} - -void MOS6502::SBC_abs() { - SBC(ReadByte_Absolute()); -} - -void MOS6502::SBC_zpx() { - SBC(ReadByte_ZeroPageX()); -} - -void MOS6502::SBC_indy() { - SBC(ReadByte_IndirectIndexedY()); -} - -void MOS6502::SBC_absx() { - SBC(ReadByte_AbsoluteX()); -} - -void MOS6502::SBC_absy() { - SBC(ReadByte_AbsoluteY()); -} - -void MOS6502::SBC_zpind() { - SBC(ReadByte_ZeroPageIndirect()); -} - -// - -void MOS6502::BIT_imm() { - BIT_immediate(ReadByte_Immediate()); -} - -void MOS6502::BIT_zp() { - BIT(ReadByte_ZeroPage()); -} - -void MOS6502::BIT_zpx() { - BIT(ReadByte_ZeroPageX()); -} - -void MOS6502::BIT_abs() { - BIT(ReadByte_Absolute()); -} - -void MOS6502::BIT_absx() { - BIT(ReadByte_AbsoluteX()); -} - -// - -void MOS6502::DEC_a() { - UpdateZeroNegativeFlags(--a); -} - -void MOS6502::DEC_absx() { - DEC(Address_AbsoluteX_Write()); -} - -void MOS6502::DEC_zpx() { - DEC(Address_ZeroPageX()); -} - -void MOS6502::DEC_abs() { - DEC(Address_Absolute()); -} - -void MOS6502::DEC_zp() { - DEC(Address_ZeroPage()); -} - -// - -void MOS6502::DEX_imp() { - UpdateZeroNegativeFlags(--x); -} - -void MOS6502::DEY_imp() { - UpdateZeroNegativeFlags(--y); -} - -// - -void MOS6502::INC_a() { - UpdateZeroNegativeFlags(++a); -} - -void MOS6502::INC_zp() { - INC(Address_ZeroPage()); -} - -void MOS6502::INC_absx() { - INC(Address_AbsoluteX_Write()); -} - -void MOS6502::INC_zpx() { - INC(Address_ZeroPageX()); -} - -void MOS6502::INC_abs() { - INC(Address_Absolute()); -} - -// - -void MOS6502::INX_imp() { - UpdateZeroNegativeFlags(++x); -} - -void MOS6502::INY_imp() { - UpdateZeroNegativeFlags(++y); -} - -// - -void MOS6502::STX_zpy() { - WriteByte_ZeroPageY(x); -} - -void MOS6502::STX_abs() { - WriteByte_Absolute(x); -} - -void MOS6502::STX_zp() { - WriteByte_ZeroPage(x); -} - -// - -void MOS6502::STY_zpx() { - WriteByte_ZeroPageX(y); -} - -void MOS6502::STY_abs() { - WriteByte_Absolute(y); -} - -void MOS6502::STY_zp() { - WriteByte_ZeroPage(y); -} - -// - -void MOS6502::STA_absx() { - WriteByte_AbsoluteX(a); -} - -void MOS6502::STA_absy() { - WriteByte_AbsoluteY(a); -} - -void MOS6502::STA_zpx() { - WriteByte_ZeroPageX(a); -} - -void MOS6502::STA_indy() { - WriteByte_IndirectIndexedY(a); -} - -void MOS6502::STA_abs() { - WriteByte_Absolute(a); -} - -void MOS6502::STA_zp() { - WriteByte_ZeroPage(a); -} - -void MOS6502::STA_xind() { - WriteByte_IndexedIndirectX(a); -} - -void MOS6502::STA_zpind() { - WriteByte_ZeroPageIndirect(a); -} - -// - -void MOS6502::STZ_zp() { - WriteByte_ZeroPage((uint8_t)0); -} - -void MOS6502::STZ_zpx() { - WriteByte_ZeroPageX((uint8_t)0); -} - -void MOS6502::STZ_abs() { - WriteByte_Absolute((uint8_t)0); -} - -void MOS6502::STZ_absx() { - WriteByte_AbsoluteX((uint8_t)0); -} - -// - -void MOS6502::TSX_imp() { - x = s; - UpdateZeroNegativeFlags(x); -} - -void MOS6502::TAX_imp() { - x = a; - UpdateZeroNegativeFlags(x); -} - -void MOS6502::TAY_imp() { - y = a; - UpdateZeroNegativeFlags(y); -} - -void MOS6502::TXS_imp() { - s = x; -} - -void MOS6502::TYA_imp() { - a = y; - UpdateZeroNegativeFlags(a); -} - -void MOS6502::TXA_imp() { - a = x; - UpdateZeroNegativeFlags(a); -} - -// - -void MOS6502::PHP_imp() { - p.brk = true; - PushByte(p); -} - -void MOS6502::PLP_imp() { - p = PopByte(); - p.reserved = true; -} - -void MOS6502::PLA_imp() { - a = PopByte(); - UpdateZeroNegativeFlags(a); -} - -void MOS6502::PHA_imp() { - PushByte(a); -} - -void MOS6502::PHX_imp() { - PushByte(x); -} - -void MOS6502::PHY_imp() { - PushByte(y); -} - -void MOS6502::PLX_imp() { - x = PopByte(); - UpdateZeroNegativeFlags(x); -} - -void MOS6502::PLY_imp() { - y = PopByte(); - UpdateZeroNegativeFlags(y); -} - -// - -void MOS6502::ASL_a() { - a = ASL(a); -} - -void MOS6502::ASL_zp() { - ASL(Address_ZeroPage()); -} - -void MOS6502::ASL_abs() { - ASL(Address_Absolute()); -} - -void MOS6502::ASL_absx() { - ASL(Address_AbsoluteX_Write()); -} - -void MOS6502::ASL_zpx() { - ASL(Address_ZeroPageX()); -} - -// - -void MOS6502::LSR_absx() { - LSR(Address_AbsoluteX_Write()); -} - -void MOS6502::LSR_zpx() { - LSR(Address_ZeroPageX()); -} - -void MOS6502::LSR_abs() { - LSR(Address_Absolute()); -} - -void MOS6502::LSR_a() { - a = LSR(a); -} - -void MOS6502::LSR_zp() { - LSR(Address_ZeroPage()); -} - -// - -void MOS6502::ROL_absx() { - ROL(Address_AbsoluteX_Write()); -} - -void MOS6502::ROL_zpx() { - ROL(Address_ZeroPageX()); -} - -void MOS6502::ROL_abs() { - ROL(Address_Absolute()); -} - -void MOS6502::ROL_a() { - a = ROL(a); -} - -void MOS6502::ROL_zp() { - ROL(Address_ZeroPage()); -} - -// - -void MOS6502::ROR_absx() { - ROR(Address_AbsoluteX_Write()); -} - -void MOS6502::ROR_zpx() { - ROR(Address_ZeroPageX()); -} - -void MOS6502::ROR_abs() { - ROR(Address_Absolute()); -} - -void MOS6502::ROR_a() { - a = ROR(a); -} - -void MOS6502::ROR_zp() { - ROR(Address_ZeroPage()); -} - -// - -void MOS6502::TSB_zp() { - TSB(Address_ZeroPage()); -} - -void MOS6502::TSB_abs() { - TSB(Address_Absolute()); -} - -// - -void MOS6502::TRB_zp() { - TRB(Address_ZeroPage()); -} - -void MOS6502::TRB_abs() { - TRB(Address_Absolute()); -} - -// - -void MOS6502::RMB0_zp() { - RMB(Address_ZeroPage(), 1); -} - -void MOS6502::RMB1_zp() { - RMB(Address_ZeroPage(), 2); -} - -void MOS6502::RMB2_zp() { - RMB(Address_ZeroPage(), 4); -} - -void MOS6502::RMB3_zp() { - RMB(Address_ZeroPage(), 8); -} - -void MOS6502::RMB4_zp() { - RMB(Address_ZeroPage(), 0x10); -} - -void MOS6502::RMB5_zp() { - RMB(Address_ZeroPage(), 0x20); -} - -void MOS6502::RMB6_zp() { - RMB(Address_ZeroPage(), 0x40); -} - -void MOS6502::RMB7_zp() { - RMB(Address_ZeroPage(), 0x80); -} - -// - -void MOS6502::SMB0_zp() { - SMB(Address_ZeroPage(), 1); -} - -void MOS6502::SMB1_zp() { - SMB(Address_ZeroPage(), 2); -} - -void MOS6502::SMB2_zp() { - SMB(Address_ZeroPage(), 4); -} - -void MOS6502::SMB3_zp() { - SMB(Address_ZeroPage(), 8); -} - -void MOS6502::SMB4_zp() { - SMB(Address_ZeroPage(), 0x10); -} - -void MOS6502::SMB5_zp() { - SMB(Address_ZeroPage(), 0x20); -} - -void MOS6502::SMB6_zp() { - SMB(Address_ZeroPage(), 0x40); -} - -void MOS6502::SMB7_zp() { - SMB(Address_ZeroPage(), 0x80); -} - -// - -void MOS6502::JSR_abs() { - auto destination = Address_Absolute(); - PushWord((uint16_t)(pc - 1)); - pc = destination; -} - -void MOS6502::RTI_imp() { - PLP_imp(); - pc = PopWord(); -} - -void MOS6502::RTS_imp() { - pc = (uint16_t)(PopWord() + 1); -} - -void MOS6502::JMP_abs() { - pc = Address_Absolute(); -} - -void MOS6502::JMP_ind() { - pc = GetWord(Address_Absolute()); -} - -void MOS6502::JMP_absxind() { - pc = Address_AbsoluteXIndirect(); -} - -void MOS6502::BRK_imp() { - PushWord((uint16_t)(pc + 1)); - PHP_imp(); - p.interrupt = true; - if (level >= ProcessorType::Cpu65SC02) - p.decimal = false; - - pc = GetWord(IRQvector); -} - -// - -void MOS6502::WAI_imp() { - throw std::runtime_error("Not implemented"); -} - -void MOS6502::STP_imp() { - throw std::runtime_error("Not implemented"); -} - -// - -void MOS6502::SED_imp() { - p.decimal = true; -} - -void MOS6502::CLD_imp() { - p.decimal = false; -} - -void MOS6502::CLV_imp() { - p.overflow = false; -} - -void MOS6502::SEI_imp() { - p.interrupt = true; -} - -void MOS6502::CLI_imp() { - p.interrupt = false; -} - -void MOS6502::CLC_imp() { - p.carry = false; -} - -void MOS6502::SEC_imp() { - p.carry = true; -} - -// - -void MOS6502::BMI_rel() { - Branch(p.negative); -} - -void MOS6502::BPL_rel() { - Branch(!p.negative); -} - -void MOS6502::BVC_rel() { - Branch(!p.overflow); -} - -void MOS6502::BVS_rel() { - Branch(p.overflow); -} - -void MOS6502::BCC_rel() { - Branch(!p.carry); -} - -void MOS6502::BCS_rel() { - Branch(p.carry); -} - -void MOS6502::BNE_rel() { - Branch(!p.zero); -} - -void MOS6502::BEQ_rel() { - Branch(p.zero); -} - -void MOS6502::BRA_rel() { - Branch(); -} - -// - -void MOS6502::BBR0_zprel() { - BitBranch_Clear(0x1); -} - -void MOS6502::BBR1_zprel() { - BitBranch_Clear(0x2); -} - -void MOS6502::BBR2_zprel() { - BitBranch_Clear(0x4); -} - -void MOS6502::BBR3_zprel() { - BitBranch_Clear(0x8); -} - -void MOS6502::BBR4_zprel() { - BitBranch_Clear(0x10); -} - -void MOS6502::BBR5_zprel() { - BitBranch_Clear(0x20); -} - -void MOS6502::BBR6_zprel() { - BitBranch_Clear(0x40); -} - -void MOS6502::BBR7_zprel() { - BitBranch_Clear(0x80); -} - -void MOS6502::BBS0_zprel() { - BitBranch_Set(0x1); -} - -void MOS6502::BBS1_zprel() { - BitBranch_Set(0x2); -} - -void MOS6502::BBS2_zprel() { - BitBranch_Set(0x4); -} - -void MOS6502::BBS3_zprel() { - BitBranch_Set(0x8); -} - -void MOS6502::BBS4_zprel() { - BitBranch_Set(0x10); -} - -void MOS6502::BBS5_zprel() { - BitBranch_Set(0x20); +void EightBit::MOS6502::JMP_abs() { + Address_Absolute(); + PC() = MEMPTR(); } -void MOS6502::BBS6_zprel() { - BitBranch_Set(0x40); +void EightBit::MOS6502::JMP_ind() { + Address_Indirect(); + PC() = MEMPTR(); } -void MOS6502::BBS7_zprel() { - BitBranch_Set(0x80); +void EightBit::MOS6502::BRK() { + PC().word++; + pushWord(PC()); + PHP(); + setFlag(P(), IF); + getWord(IRQvector, PC()); } diff --git a/M6502/src/stdafx.cpp b/M6502/src/stdafx.cpp new file mode 100644 index 0000000..fd4f341 --- /dev/null +++ b/M6502/src/stdafx.cpp @@ -0,0 +1 @@ +#include "stdafx.h" diff --git a/M6502/src/stdafx.h b/M6502/src/stdafx.h new file mode 100644 index 0000000..9b131a4 --- /dev/null +++ b/M6502/src/stdafx.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include \ No newline at end of file diff --git a/M6502/src/system6502.cpp b/M6502/src/system6502.cpp deleted file mode 100644 index 72a3356..0000000 --- a/M6502/src/system6502.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include "stdafx.h" -#include "system6502.h" - -#include -#include - -System6502::System6502(ProcessorType level, double processorSpeed, clock_t pollInterval) -: MOS6502(level), - memory(MemorySize) -{ - speed = processorSpeed; - - cyclesPerSecond = speed * Mega; // speed is in MHz - cyclesPerMillisecond = cyclesPerSecond * Milli; - cyclesPerInterval = (uint64_t)(cyclesPerMillisecond * pollInterval); - - Starting.connect(std::bind(&System6502::System6502_Starting, this)); - Finished.connect(std::bind(&System6502::System6502_Finished, this)); -} - -void System6502::Initialise() { - __super::Initialise(); - memory.ClearLocking(); - memory.ClearMemory(); - intervalCycles = 0; -} - -void System6502::Run() { - Starting.fire(EventArgs()); - __super::Run(); - Finished.fire(EventArgs()); -} - -uint8_t System6502::GetByte(uint16_t offset) const { - return memory.GetByte(offset); -} - -void System6502::SetByte(uint16_t offset, uint8_t value) { - memory.SetByte(offset, value); -} - -void System6502::Execute(uint8_t cell) { - - auto oldCycles = getCycles(); - - CheckPoll(); - - // XXXX Fetch byte has already incremented PC. - auto executingAddress = (uint16_t)(getPC() - 1); - - AddressEventArgs e(executingAddress, cell); - ExecutingInstruction.fire(e); - __super::Execute(cell); - ExecutedInstruction.fire(e); - - auto deltaCycles = getCycles() - oldCycles; - intervalCycles += deltaCycles; -} - -void System6502::CheckPoll() { - if (intervalCycles >= cyclesPerInterval) { - intervalCycles -= cyclesPerInterval; - Throttle(); - Polling.fire(EventArgs()); - } -} - -void System6502::System6502_Starting() { - startTime = std::chrono::high_resolution_clock::now(); - running = true; -} - -void System6502::System6502_Finished() { - running = false; -} - -void System6502::Throttle() { - auto now = std::chrono::high_resolution_clock::now(); - auto elapsed = now - startTime; - auto timerCurrent = std::chrono::duration_cast(elapsed).count(); - - auto cyclesAllowed = timerCurrent * cyclesPerMillisecond; - auto cyclesMismatch = getCycles() - cyclesAllowed; - if (cyclesMismatch > 0.0) { - auto delay = cyclesMismatch / cyclesPerMillisecond; - if (delay > 0) { - heldCycles += (uint64_t)cyclesMismatch; - std::this_thread::sleep_for(std::chrono::milliseconds((long long)delay)); - } - } -} \ No newline at end of file diff --git a/M6502/test_M6502/Board.cpp b/M6502/test_M6502/Board.cpp new file mode 100644 index 0000000..14768b4 --- /dev/null +++ b/M6502/test_M6502/Board.cpp @@ -0,0 +1,139 @@ +#include "stdafx.h" +#include "Board.h" +#include "Disassembly.h" + +#include +#include +#include +#include + +Board::Board(const Configuration& configuration) +: m_configuration(configuration), + m_memory(0xffff), + m_cpu(EightBit::MOS6502(m_memory)), + m_symbols(""), + m_disassembler(m_cpu, m_symbols), + m_profiler(m_cpu, m_disassembler, m_symbols), + m_oldPC(0xffff), + m_stopped(false) { +} + +void Board::initialise() { + + m_memory.clear(); + + auto programFilename = m_configuration.getProgram(); + auto programPath = m_configuration.getRomDirectory() + "\\" + m_configuration.getProgram(); + auto loadAddress = m_configuration.getLoadAddress(); + + switch (m_configuration.getLoadMethod()) { + case Configuration::LoadMethod::Ram: + m_memory.loadRam(programPath, loadAddress); + break; + case Configuration::LoadMethod::Rom: + m_memory.loadRom(programPath, loadAddress); + break; + } + + if (m_configuration.isProfileMode()) { + m_cpu.ExecutingInstruction.connect(std::bind(&Board::Cpu_ExecutingInstruction_Profile, this, std::placeholders::_1)); + } + + if (m_configuration.isDebugMode()) { + m_cpu.ExecutingInstruction.connect(std::bind(&Board::Cpu_ExecutingInstruction_Debug, this, std::placeholders::_1)); + } + + switch (m_configuration.getStopCondition()) { + case Configuration::StopCondition::Loop: + m_cpu.ExecutedInstruction.connect(std::bind(&Board::Cpu_ExecutedInstruction_StopLoop, this, std::placeholders::_1)); + break; + case Configuration::StopCondition::Halt: + break; + default: + throw std::domain_error("Unknown stop condition"); + } + + if (m_configuration.allowInput()) { + m_memory.ReadByte.connect(std::bind(&Board::Memory_ReadByte_Input, this, std::placeholders::_1)); + m_cpu.ExecutedInstruction.connect(std::bind(&Board::Cpu_ExecutedInstruction_Poll, this, std::placeholders::_1)); + } + + if (m_configuration.allowOutput()) + m_memory.WrittenByte.connect(std::bind(&Board::Memory_WrittenByte_Output, this, std::placeholders::_1)); + + m_pollCounter = 0; + m_pollInterval = m_configuration.getPollInterval(); + + m_cpu.initialise(); + m_cpu.PC().word = m_configuration.getStartAddress(); +} + +void Board::Cpu_ExecutingInstruction_Profile(const EightBit::MOS6502& cpu) { + + const auto pc = m_cpu.PC(); + + //m_profiler.addAddress(pc.word, m_cpu.); + //m_profiler.addInstruction(m_memory.peek(pc.word)); +} + +void Board::Cpu_ExecutedInstruction_StopLoop(const EightBit::MOS6502& cpu) { + + auto pc = m_cpu.PC().word; + if (m_oldPC == pc) { + m_cpu.halt(); + auto test = m_memory.peek(0x0200); + std::cout << std::endl << "** Test=" << std::hex << (int)test; + } else { + m_oldPC = pc; + } +} + +void Board::Cpu_ExecutingInstruction_Debug(const EightBit::MOS6502& cpu) { + + auto address = m_cpu.PC().word; + auto cell = m_memory.peek(address); + + std::cout << std::hex; + std::cout << "PC=" << std::setw(4) << std::setfill('0') << address << ":"; + std::cout << "P=" << m_disassembler.dump_Flags(m_cpu.P()) << ", "; + std::cout << std::setw(2) << std::setfill('0'); + std::cout << "A=" << (int)m_cpu.A() << ", "; + std::cout << "X=" << (int)m_cpu.X() << ", "; + std::cout << "Y=" << (int)m_cpu.Y() << ", "; + std::cout << "S=" << (int)m_cpu.S() << "\t"; + + std::cout << m_disassembler.disassemble(address); + + std::cout << "\n"; +} + +void Board::Memory_ReadByte_Input(const EightBit::AddressEventArgs& e) { + auto address = e.getAddress(); + if (address == m_configuration.getInputAddress()) { + auto cell = e.getCell(); + if (cell != 0) { + assert(address == m_memory.ADDRESS().word); + m_memory.reference() = 0; + } + } +} + +void Board::Memory_WrittenByte_Output(const EightBit::AddressEventArgs& e) { + if (e.getAddress() == m_configuration.getOutputAddress()) { + _putch(e.getCell()); + } +} + +void Board::Cpu_ExecutedInstruction_Poll(const EightBit::MOS6502& cpu) { + if (++m_pollCounter == m_pollInterval) { + m_pollCounter = 0; + pollKeyboard(); + } +} + +void Board::pollKeyboard() { + if (_kbhit()) { + m_memory.ADDRESS().word = m_configuration.getInputAddress(); + m_memory.reference() = _getch(); + } +} \ No newline at end of file diff --git a/M6502/test_M6502/Board.h b/M6502/test_M6502/Board.h new file mode 100644 index 0000000..ab8510e --- /dev/null +++ b/M6502/test_M6502/Board.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +#include "Memory.h" +#include "Configuration.h" +#include "Profiler.h" +#include "EventArgs.h" +#include "Disassembly.h" +#include "mos6502.h" +#include "Symbols.h" + +class Board { +public: + Board(const Configuration& configuration); + + EightBit::Memory& Memory() { return m_memory; } + EightBit::MOS6502& CPU() { return m_cpu; } + + void initialise(); + +private: + const Configuration& m_configuration; + EightBit::Memory m_memory; + EightBit::MOS6502 m_cpu; + EightBit::Symbols m_symbols; + EightBit::Disassembly m_disassembler; + EightBit::Profiler m_profiler; + + uint16_t m_oldPC; + bool m_stopped; + uint64_t m_pollCounter; + uint64_t m_pollInterval; + + void pollKeyboard(); + + void Cpu_ExecutingInstruction_Debug(const EightBit::MOS6502& cpu); + void Cpu_ExecutingInstruction_Profile(const EightBit::MOS6502& cpu); + + void Cpu_ExecutedInstruction_StopLoop(const EightBit::MOS6502& cpu); + + void Memory_ReadByte_Input(const EightBit::AddressEventArgs& e); + void Memory_WrittenByte_Output(const EightBit::AddressEventArgs& e); + + void Cpu_ExecutedInstruction_Poll(const EightBit::MOS6502& cpu); +}; diff --git a/M6502/test_M6502/Configuration.cpp b/M6502/test_M6502/Configuration.cpp new file mode 100644 index 0000000..297621d --- /dev/null +++ b/M6502/test_M6502/Configuration.cpp @@ -0,0 +1,18 @@ +#include "stdafx.h" +#include "Configuration.h" + +Configuration::Configuration() +: m_debugMode(false), + m_profileMode(false), + m_loadAddress(0x400), + m_startAddress(0x400), + m_allowInput(false), + m_inputAddress(0xbff0), + m_allowOutput(false), + m_outputAddress(0xbff0), + m_pollInterval(2000000 / 50), // 2Mhz, 50 times a second; + m_stopCondition(StopCondition::Loop), + m_romDirectory("roms"), + m_program("6502_functional_test.bin"), + m_loadMethod(LoadMethod::Ram) { +} diff --git a/M6502/test_M6502/Configuration.h b/M6502/test_M6502/Configuration.h new file mode 100644 index 0000000..d28479e --- /dev/null +++ b/M6502/test_M6502/Configuration.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include + +class Configuration { +public: + enum StopCondition { + Halt, + Loop + }; + + enum LoadMethod { + Ram, + Rom + }; + + Configuration(); + + bool isDebugMode() const { return m_debugMode; } + void setDebugMode(bool value) { m_debugMode = value; } + + bool isProfileMode() const { return m_profileMode; } + void setProfileMode(bool value) { m_profileMode = value; } + + uint16_t getLoadAddress() const { return m_loadAddress; } + uint16_t getStartAddress() const { return m_startAddress; } + + bool allowInput() const { return m_allowInput; } + uint16_t getInputAddress() const { return m_inputAddress; } + + bool allowOutput() const { return m_allowOutput; } + uint16_t getOutputAddress() const { return m_outputAddress; } + + uint64_t getPollInterval() const { return m_pollInterval; } + + StopCondition getStopCondition() const { return m_stopCondition; } + + std::string getRomDirectory() const { return m_romDirectory; } + std::string getProgram() const { return m_program; } + + LoadMethod getLoadMethod() const { return m_loadMethod; } + +private: + bool m_debugMode; + bool m_profileMode; + uint16_t m_loadAddress; + uint16_t m_startAddress; + bool m_allowInput; + uint16_t m_inputAddress; + bool m_allowOutput; + uint16_t m_outputAddress; + uint64_t m_pollInterval; + StopCondition m_stopCondition; + std::string m_romDirectory; + std::string m_program; + LoadMethod m_loadMethod; +}; diff --git a/M6502/test_M6502/roms/.gitattributes b/M6502/test_M6502/roms/.gitattributes new file mode 100644 index 0000000..412eeda --- /dev/null +++ b/M6502/test_M6502/roms/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/M6502/test_M6502/roms/.gitignore b/M6502/test_M6502/roms/.gitignore new file mode 100644 index 0000000..b9d6bd9 --- /dev/null +++ b/M6502/test_M6502/roms/.gitignore @@ -0,0 +1,215 @@ +################# +## Eclipse +################# + +*.pydevproject +.project +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results + +[Dd]ebug/ +[Rr]elease/ +x64/ +build/ +[Bb]in/ +[Oo]bj/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.log +*.scc + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml +*.pubxml + +# NuGet Packages Directory +## TODO: If you have NuGet Package Restore enabled, uncomment the next line +#packages/ + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.[Pp]ublish.xml +*.pfx +*.publishsettings + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +App_Data/*.mdf +App_Data/*.ldf + +############# +## Windows detritus +############# + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Mac crap +.DS_Store + + +############# +## Python +############# + +*.py[co] + +# Packages +*.egg +*.egg-info +dist/ +build/ +eggs/ +parts/ +var/ +sdist/ +develop-eggs/ +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +#Translations +*.mo + +#Mr Developer +.mr.developer.cfg diff --git a/M6502/test_M6502/roms/6502_decimal_test.a65 b/M6502/test_M6502/roms/6502_decimal_test.a65 new file mode 100644 index 0000000..7a76ad1 --- /dev/null +++ b/M6502/test_M6502/roms/6502_decimal_test.a65 @@ -0,0 +1,355 @@ +; Verify decimal mode behavior +; Written by Bruce Clark. This code is public domain. +; see http://www.6502.org/tutorials/decimal_mode.html +; +; Returns: +; ERROR = 0 if the test passed +; ERROR = 1 if the test failed +; modify the code at the DONE label for desired program end +; +; This routine requires 17 bytes of RAM -- 1 byte each for: +; AR, CF, DA, DNVZC, ERROR, HA, HNVZC, N1, N1H, N1L, N2, N2L, NF, VF, and ZF +; and 2 bytes for N2H +; +; Variables: +; N1 and N2 are the two numbers to be added or subtracted +; N1H, N1L, N2H, and N2L are the upper 4 bits and lower 4 bits of N1 and N2 +; DA and DNVZC are the actual accumulator and flag results in decimal mode +; HA and HNVZC are the accumulator and flag results when N1 and N2 are +; added or subtracted using binary arithmetic +; AR, NF, VF, ZF, and CF are the predicted decimal mode accumulator and +; flag results, calculated using binary arithmetic +; +; This program takes approximately 1 minute at 1 MHz (a few seconds more on +; a 65C02 than a 6502 or 65816) +; + +; Configuration: +cputype = 0 ; 0 = 6502, 1 = 65C02, 2 = 65C816 +vld_bcd = 0 ; 0 = allow invalid bcd, 1 = valid bcd only +chk_a = 1 ; check accumulator +chk_n = 0 ; check sign (negative) flag +chk_v = 0 ; check overflow flag +chk_z = 0 ; check zero flag +chk_c = 1 ; check carry flag + +end_of_test macro + db $db ;execute 65C02 stop instruction + endm + + bss + org 0 +; operands - register Y = carry in +N1 ds 1 +N2 ds 1 +; binary result +HA ds 1 +HNVZC ds 1 + ;04 +; decimal result +DA ds 1 +DNVZC ds 1 +; predicted results +AR ds 1 +NF ds 1 + ;08 +VF ds 1 +ZF ds 1 +CF ds 1 +ERROR ds 1 + ;0C +; workspace +N1L ds 1 +N1H ds 1 +N2L ds 1 +N2H ds 2 + + code + org $200 +TEST ldy #1 ; initialize Y (used to loop through carry flag values) + sty ERROR ; store 1 in ERROR until the test passes + lda #0 ; initialize N1 and N2 + sta N1 + sta N2 +LOOP1 lda N2 ; N2L = N2 & $0F + and #$0F ; [1] see text + if vld_bcd = 1 + cmp #$0a + bcs NEXT2 + endif + sta N2L + lda N2 ; N2H = N2 & $F0 + and #$F0 ; [2] see text + if vld_bcd = 1 + cmp #$a0 + bcs NEXT2 + endif + sta N2H + ora #$0F ; N2H+1 = (N2 & $F0) + $0F + sta N2H+1 +LOOP2 lda N1 ; N1L = N1 & $0F + and #$0F ; [3] see text + if vld_bcd = 1 + cmp #$0a + bcs NEXT1 + endif + sta N1L + lda N1 ; N1H = N1 & $F0 + and #$F0 ; [4] see text + if vld_bcd = 1 + cmp #$a0 + bcs NEXT1 + endif + sta N1H + jsr ADD + jsr A6502 + jsr COMPARE + bne DONE + jsr SUB + jsr S6502 + jsr COMPARE + bne DONE +NEXT1 inc N1 ; [5] see text + bne LOOP2 ; loop through all 256 values of N1 +NEXT2 inc N2 ; [6] see text + bne LOOP1 ; loop through all 256 values of N2 + dey + bpl LOOP1 ; loop through both values of the carry flag + lda #0 ; test passed, so store 0 in ERROR + sta ERROR +DONE + end_of_test + +; Calculate the actual decimal mode accumulator and flags, the accumulator +; and flag results when N1 is added to N2 using binary arithmetic, the +; predicted accumulator result, the predicted carry flag, and the predicted +; V flag +; +ADD sed ; decimal mode + cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1 + adc N2 + sta DA ; actual accumulator result in decimal mode + php + pla + sta DNVZC ; actual flags result in decimal mode + cld ; binary mode + cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1 + adc N2 + sta HA ; accumulator result of N1+N2 using binary arithmetic + + php + pla + sta HNVZC ; flags result of N1+N2 using binary arithmetic + cpy #1 + lda N1L + adc N2L + cmp #$0A + ldx #0 + bcc A1 + inx + adc #5 ; add 6 (carry is set) + and #$0F + sec +A1 ora N1H +; +; if N1L + N2L < $0A, then add N2 & $F0 +; if N1L + N2L >= $0A, then add (N2 & $F0) + $0F + 1 (carry is set) +; + adc N2H,x + php + bcs A2 + cmp #$A0 + bcc A3 +A2 adc #$5F ; add $60 (carry is set) + sec +A3 sta AR ; predicted accumulator result + php + pla + sta CF ; predicted carry result + pla +; +; note that all 8 bits of the P register are stored in VF +; + sta VF ; predicted V flags + rts + +; Calculate the actual decimal mode accumulator and flags, and the +; accumulator and flag results when N2 is subtracted from N1 using binary +; arithmetic +; +SUB sed ; decimal mode + cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1 + sbc N2 + sta DA ; actual accumulator result in decimal mode + php + pla + sta DNVZC ; actual flags result in decimal mode + cld ; binary mode + cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1 + sbc N2 + sta HA ; accumulator result of N1-N2 using binary arithmetic + + php + pla + sta HNVZC ; flags result of N1-N2 using binary arithmetic + rts + + if cputype != 1 +; Calculate the predicted SBC accumulator result for the 6502 and 65816 +; +SUB1 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1L + sbc N2L + ldx #0 + bcs S11 + inx + sbc #5 ; subtract 6 (carry is clear) + and #$0F + clc +S11 ora N1H +; +; if N1L - N2L >= 0, then subtract N2 & $F0 +; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) +; + sbc N2H,x + bcs S12 + sbc #$5F ; subtract $60 (carry is clear) +S12 sta AR + rts + endif + + if cputype = 1 +; Calculate the predicted SBC accumulator result for the 6502 and 65C02 +; +SUB2 cpy #1 ; set carry if Y = 1, clear carry if Y = 0 + lda N1L + sbc N2L + ldx #0 + bcs S21 + inx + and #$0F + clc +S21 ora N1H +; +; if N1L - N2L >= 0, then subtract N2 & $F0 +; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) +; + sbc N2H,x + bcs S22 + sbc #$5F ; subtract $60 (carry is clear) +S22 cpx #0 + beq S23 + sbc #6 +S23 sta AR ; predicted accumulator result + rts + endif + +; Compare accumulator actual results to predicted results +; +; Return: +; Z flag = 1 (BEQ branch) if same +; Z flag = 0 (BNE branch) if different +; +COMPARE + if chk_a = 1 + lda DA + cmp AR + bne C1 + endif + if chk_n = 1 + lda DNVZC ; [7] see text + eor NF + and #$80 ; mask off N flag + bne C1 + endif + if chk_v = 1 + lda DNVZC ; [8] see text + eor VF + and #$40 ; mask off V flag + bne C1 ; [9] see text + endif + if chk_z = 1 + lda DNVZC + eor ZF ; mask off Z flag + and #2 + bne C1 ; [10] see text + endif + if chk_c = 1 + lda DNVZC + eor CF + and #1 ; mask off C flag + endif +C1 rts + +; These routines store the predicted values for ADC and SBC for the 6502, +; 65C02, and 65816 in AR, CF, NF, VF, and ZF + + if cputype = 0 + +A6502 lda VF ; 6502 +; +; since all 8 bits of the P register were stored in VF, bit 7 of VF contains +; the N flag for NF +; + sta NF + lda HNVZC + sta ZF + rts + +S6502 jsr SUB1 + lda HNVZC + sta NF + sta VF + sta ZF + sta CF + rts + + endif + if cputype = 1 + +A6502 lda AR ; 65C02 + php + pla + sta NF + sta ZF + rts + +S6502 jsr SUB2 + lda AR + php + pla + sta NF + sta ZF + lda HNVZC + sta VF + sta CF + rts + + endif + if cputype = 2 + +A6502 lda AR ; 65C816 + php + pla + sta NF + sta ZF + rts + +S6502 jsr SUB1 + lda AR + php + pla + sta NF + sta ZF + lda HNVZC + sta VF + sta CF + rts + + endif + + end TEST diff --git a/M6502/test_M6502/roms/6502_functional_test.a65 b/M6502/test_M6502/roms/6502_functional_test.a65 new file mode 100644 index 0000000..333497d --- /dev/null +++ b/M6502/test_M6502/roms/6502_functional_test.a65 @@ -0,0 +1,6010 @@ +; +; 6 5 0 2 F U N C T I O N A L T E S T +; +; Copyright (C) 2012-2015 Klaus Dormann +; +; This program is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . + + +; This program is designed to test all opcodes of a 6502 emulator using all +; addressing modes with focus on propper setting of the processor status +; register bits. +; +; version 21-oct-2015 +; contact info at http://2m5.de or email K@2m5.de +; +; assembled with AS65 from http://www.kingswood-consulting.co.uk/assemblers/ +; command line switches: -l -m -s2 -w -h0 +; | | | | no page headers in listing +; | | | wide listing (133 char/col) +; | | write intel hex file instead of binary +; | expand macros in listing +; generate pass2 listing +; +; No IO - should be run from a monitor with access to registers. +; To run load intel hex image with a load command, than alter PC to 400 hex +; (code_segment) and enter a go command. +; Loop on program counter determines error or successful completion of test. +; Check listing for relevant traps (jump/branch *). +; Please note that in early tests some instructions will have to be used before +; they are actually tested! +; +; RESET, NMI or IRQ should not occur and will be trapped if vectors are enabled. +; Tests documented behavior of the original NMOS 6502 only! No unofficial +; opcodes. Additional opcodes of newer versions of the CPU (65C02, 65816) will +; not be tested. Decimal ops will only be tested with valid BCD operands and +; N V Z flags will be ignored. +; +; Debugging hints: +; Most of the code is written sequentially. if you hit a trap, check the +; immediately preceeding code for the instruction to be tested. Results are +; tested first, flags are checked second by pushing them onto the stack and +; pulling them to the accumulator after the result was checked. The "real" +; flags are no longer valid for the tested instruction at this time! +; If the tested instruction was indexed, the relevant index (X or Y) must +; also be checked. Opposed to the flags, X and Y registers are still valid. +; +; versions: +; 28-jul-2012 1st version distributed for testing +; 29-jul-2012 fixed references to location 0, now #0 +; added license - GPLv3 +; 30-jul-2012 added configuration options +; 01-aug-2012 added trap macro to allow user to change error handling +; 01-dec-2012 fixed trap in branch field must be a branch +; 02-mar-2013 fixed PLA flags not tested +; 19-jul-2013 allowed ROM vectors to be loaded when load_data_direct = 0 +; added test sequence check to detect if tests jump their fence +; 23-jul-2013 added RAM integrity check option +; 16-aug-2013 added error report to standard output option +; 13-dec-2014 added binary/decimal opcode table switch test +; 14-dec-2014 improved relative address test +; 23-aug-2015 added option to disable self modifying tests +; 24-aug-2015 all self modifying immediate opcodes now execute in data RAM +; added small branch offset pretest +; 21-oct-2015 added option to disable decimal mode ADC & SBC tests + + +; C O N F I G U R A T I O N + +;ROM_vectors writable (0=no, 1=yes) +;if ROM vectors can not be used interrupts will not be trapped +;as a consequence BRK can not be tested but will be emulated to test RTI +ROM_vectors = 1 + +;load_data_direct (0=move from code segment, 1=load directly) +;loading directly is preferred but may not be supported by your platform +;0 produces only consecutive object code, 1 is not suitable for a binary image +load_data_direct = 0 + +;I_flag behavior (0=force enabled, 1=force disabled, 2=prohibit change, 3=allow +;change) 2 requires extra code and is not recommended. SEI & CLI can only be +;tested if you allow changing the interrupt status (I_flag = 3) +I_flag = 3 + +;configure memory - try to stay away from memory used by the system +;zero_page memory start address, $50 (80) consecutive Bytes required +; add 2 if I_flag = 2 +zero_page = $a + +;data_segment memory start address, $6A (106) consecutive Bytes required +data_segment = $200 + if (data_segment & $ff) != 0 + ERROR ERROR ERROR low byte of data_segment MUST be $00 !! + endif + +;code_segment memory start address, 13kB of consecutive space required +; add 2.5 kB if I_flag = 2 +code_segment = $400 + +;self modifying code may be disabled to allow running in ROM +;0=part of the code is self modifying and must reside in RAM +;1=tests disabled: branch range +disable_selfmod = 0 + +;report errors through I/O channel (0=use standard self trap loops, 1=include +;report.i65 as I/O channel, add 3.5 kB) +report = 0 + +;RAM integrity test option. Checks for undesired RAM writes. +;set lowest non RAM or RAM mirror address page (-1=disable, 0=64k, $40=16k) +;leave disabled if a monitor, OS or background interrupt is allowed to alter RAM +ram_top = -1 + +;disable test decimal mode ADC & SBC, 0=enable, 1=disable, +;2=disable including decimal flag in processor status +disable_decimal = 0 + + noopt ;do not take shortcuts + +;macros for error & success traps to allow user modification +;example: +;trap macro +; jsr my_error_handler +; endm +;trap_eq macro +; bne skip\? +; trap ;failed equal (zero) +;skip\? +; endm +; +; my_error_handler should pop the calling address from the stack and report it. +; putting larger portions of code (more than 3 bytes) inside the trap macro +; may lead to branch range problems for some tests. + if report = 0 +trap macro + jmp * ;failed anyway + endm +trap_eq macro + beq * ;failed equal (zero) + endm +trap_ne macro + bne * ;failed not equal (non zero) + endm +trap_cs macro + bcs * ;failed carry set + endm +trap_cc macro + bcc * ;failed carry clear + endm +trap_mi macro + bmi * ;failed minus (bit 7 set) + endm +trap_pl macro + bpl * ;failed plus (bit 7 clear) + endm +trap_vs macro + bvs * ;failed overflow set + endm +trap_vc macro + bvc * ;failed overflow clear + endm +; please observe that during the test the stack gets invalidated +; therefore a RTS inside the success macro is not possible +success macro + jmp * ;test passed, no errors + endm + endif + if report = 1 +trap macro + jsr report_error + endm +trap_eq macro + bne skip\? + trap ;failed equal (zero) +skip\? + endm +trap_ne macro + beq skip\? + trap ;failed not equal (non zero) +skip\? + endm +trap_cs macro + bcc skip\? + trap ;failed carry set +skip\? + endm +trap_cc macro + bcs skip\? + trap ;failed carry clear +skip\? + endm +trap_mi macro + bpl skip\? + trap ;failed minus (bit 7 set) +skip\? + endm +trap_pl macro + bmi skip\? + trap ;failed plus (bit 7 clear) +skip\? + endm +trap_vs macro + bvc skip\? + trap ;failed overflow set +skip\? + endm +trap_vc macro + bvs skip\? + trap ;failed overflow clear +skip\? + endm +; please observe that during the test the stack gets invalidated +; therefore a RTS inside the success macro is not possible +success macro + jsr report_success + endm + endif + + +carry equ %00000001 ;flag bits in status +zero equ %00000010 +intdis equ %00000100 +decmode equ %00001000 +break equ %00010000 +reserv equ %00100000 +overfl equ %01000000 +minus equ %10000000 + +fc equ carry +fz equ zero +fzc equ carry+zero +fv equ overfl +fvz equ overfl+zero +fn equ minus +fnc equ minus+carry +fnz equ minus+zero +fnzc equ minus+zero+carry +fnv equ minus+overfl + +fao equ break+reserv ;bits always on after PHP, BRK +fai equ fao+intdis ;+ forced interrupt disable +faod equ fao+decmode ;+ ignore decimal +faid equ fai+decmode ;+ ignore decimal +m8 equ $ff ;8 bit mask +m8i equ $ff&~intdis ;8 bit mask - interrupt disable + +;macros to allow masking of status bits. +;masking test of decimal bit +;masking of interrupt enable/disable on load and compare +;masking of always on bits after PHP or BRK (unused & break) on compare + if disable_decimal < 2 + if I_flag = 0 +load_flag macro + lda #\1&m8i ;force enable interrupts (mask I) + endm +cmp_flag macro + cmp #(\1|fao)&m8i ;I_flag is always enabled + always on bits + endm +eor_flag macro + eor #(\1&m8i|fao) ;mask I, invert expected flags + always on bits + endm + endif + if I_flag = 1 +load_flag macro + lda #\1|intdis ;force disable interrupts + endm +cmp_flag macro + cmp #(\1|fai)&m8 ;I_flag is always disabled + always on bits + endm +eor_flag macro + eor #(\1|fai) ;invert expected flags + always on bits + I + endm + endif + if I_flag = 2 +load_flag macro + lda #\1 + ora flag_I_on ;restore I-flag + and flag_I_off + endm +cmp_flag macro + eor flag_I_on ;I_flag is never changed + cmp #(\1|fao)&m8i ;expected flags + always on bits, mask I + endm +eor_flag macro + eor flag_I_on ;I_flag is never changed + eor #(\1&m8i|fao) ;mask I, invert expected flags + always on bits + endm + endif + if I_flag = 3 +load_flag macro + lda #\1 ;allow test to change I-flag (no mask) + endm +cmp_flag macro + cmp #(\1|fao)&m8 ;expected flags + always on bits + endm +eor_flag macro + eor #\1|fao ;invert expected flags + always on bits + endm + endif + else + if I_flag = 0 +load_flag macro + lda #\1&m8i ;force enable interrupts (mask I) + endm +cmp_flag macro + ora #decmode ;ignore decimal mode bit + cmp #(\1|faod)&m8i ;I_flag is always enabled + always on bits + endm +eor_flag macro + ora #decmode ;ignore decimal mode bit + eor #(\1&m8i|faod) ;mask I, invert expected flags + always on bits + endm + endif + if I_flag = 1 +load_flag macro + lda #\1|intdis ;force disable interrupts + endm +cmp_flag macro + ora #decmode ;ignore decimal mode bit + cmp #(\1|faid)&m8 ;I_flag is always disabled + always on bits + endm +eor_flag macro + ora #decmode ;ignore decimal mode bit + eor #(\1|faid) ;invert expected flags + always on bits + I + endm + endif + if I_flag = 2 +load_flag macro + lda #\1 + ora flag_I_on ;restore I-flag + and flag_I_off + endm +cmp_flag macro + eor flag_I_on ;I_flag is never changed + ora #decmode ;ignore decimal mode bit + cmp #(\1|faod)&m8i ;expected flags + always on bits, mask I + endm +eor_flag macro + eor flag_I_on ;I_flag is never changed + ora #decmode ;ignore decimal mode bit + eor #(\1&m8i|faod) ;mask I, invert expected flags + always on bits + endm + endif + if I_flag = 3 +load_flag macro + lda #\1 ;allow test to change I-flag (no mask) + endm +cmp_flag macro + ora #decmode ;ignore decimal mode bit + cmp #(\1|faod)&m8 ;expected flags + always on bits + endm +eor_flag macro + ora #decmode ;ignore decimal mode bit + eor #\1|faod ;invert expected flags + always on bits + endm + endif + endif + +;macros to set (register|memory|zeropage) & status +set_stat macro ;setting flags in the processor status register + load_flag \1 + pha ;use stack to load status + plp + endm + +set_a macro ;precharging accu & status + load_flag \2 + pha ;use stack to load status + lda #\1 ;precharge accu + plp + endm + +set_x macro ;precharging index & status + load_flag \2 + pha ;use stack to load status + ldx #\1 ;precharge index x + plp + endm + +set_y macro ;precharging index & status + load_flag \2 + pha ;use stack to load status + ldy #\1 ;precharge index y + plp + endm + +set_ax macro ;precharging indexed accu & immediate status + load_flag \2 + pha ;use stack to load status + lda \1,x ;precharge accu + plp + endm + +set_ay macro ;precharging indexed accu & immediate status + load_flag \2 + pha ;use stack to load status + lda \1,y ;precharge accu + plp + endm + +set_z macro ;precharging indexed zp & immediate status + load_flag \2 + pha ;use stack to load status + lda \1,x ;load to zeropage + sta zpt + plp + endm + +set_zx macro ;precharging zp,x & immediate status + load_flag \2 + pha ;use stack to load status + lda \1,x ;load to indexed zeropage + sta zpt,x + plp + endm + +set_abs macro ;precharging indexed memory & immediate status + load_flag \2 + pha ;use stack to load status + lda \1,x ;load to memory + sta abst + plp + endm + +set_absx macro ;precharging abs,x & immediate status + load_flag \2 + pha ;use stack to load status + lda \1,x ;load to indexed memory + sta abst,x + plp + endm + +;macros to test (register|memory|zeropage) & status & (mask) +tst_stat macro ;testing flags in the processor status register + php ;save status + pla ;use stack to retrieve status + pha + cmp_flag \1 + trap_ne + plp ;restore status + endm + +tst_a macro ;testing result in accu & flags + php ;save flags + cmp #\1 ;test result + trap_ne + pla ;load status + pha + cmp_flag \2 + trap_ne + plp ;restore status + endm + +tst_x macro ;testing result in x index & flags + php ;save flags + cpx #\1 ;test result + trap_ne + pla ;load status + pha + cmp_flag \2 + trap_ne + plp ;restore status + endm + +tst_y macro ;testing result in y index & flags + php ;save flags + cpy #\1 ;test result + trap_ne + pla ;load status + pha + cmp_flag \2 + trap_ne + plp ;restore status + endm + +tst_ax macro ;indexed testing result in accu & flags + php ;save flags + cmp \1,x ;test result + trap_ne + pla ;load status + eor_flag \3 + cmp \2,x ;test flags + trap_ne ; + endm + +tst_ay macro ;indexed testing result in accu & flags + php ;save flags + cmp \1,y ;test result + trap_ne ; + pla ;load status + eor_flag \3 + cmp \2,y ;test flags + trap_ne + endm + +tst_z macro ;indexed testing result in zp & flags + php ;save flags + lda zpt + cmp \1,x ;test result + trap_ne + pla ;load status + eor_flag \3 + cmp \2,x ;test flags + trap_ne + endm + +tst_zx macro ;testing result in zp,x & flags + php ;save flags + lda zpt,x + cmp \1,x ;test result + trap_ne + pla ;load status + eor_flag \3 + cmp \2,x ;test flags + trap_ne + endm + +tst_abs macro ;indexed testing result in memory & flags + php ;save flags + lda abst + cmp \1,x ;test result + trap_ne + pla ;load status + eor_flag \3 + cmp \2,x ;test flags + trap_ne + endm + +tst_absx macro ;testing result in abs,x & flags + php ;save flags + lda abst,x + cmp \1,x ;test result + trap_ne + pla ;load status + eor_flag \3 + cmp \2,x ;test flags + trap_ne + endm + +; RAM integrity test +; verifies that none of the previous tests has altered RAM outside of the +; designated write areas. +; uses zpt word as indirect pointer, zpt+2 word as checksum + if ram_top > -1 +check_ram macro + cld + lda #0 + sta zpt ;set low byte of indirect pointer + sta zpt+3 ;checksum high byte + if disable_selfmod = 0 + sta range_adr ;reset self modifying code + endif + clc + ldx #zp_bss-zero_page ;zeropage - write test area +ccs3\? adc zero_page,x + bcc ccs2\? + inc zpt+3 ;carry to high byte + clc +ccs2\? inx + bne ccs3\? + ldx #hi(abs1) ;set high byte of indirect pointer + stx zpt+1 + ldy #lo(abs1) ;data after write & execute test area +ccs5\? adc (zpt),y + bcc ccs4\? + inc zpt+3 ;carry to high byte + clc +ccs4\? iny + bne ccs5\? + inx ;advance RAM high address + stx zpt+1 + cpx #ram_top + bne ccs5\? + sta zpt+2 ;checksum low is + cmp ram_chksm ;checksum low expected + trap_ne ;checksum mismatch + lda zpt+3 ;checksum high is + cmp ram_chksm+1 ;checksum high expected + trap_ne ;checksum mismatch + endm + else +check_ram macro + ;RAM check disabled - RAM size not set + endm + endif + +next_test macro ;make sure, tests don't jump the fence + lda test_case ;previous test + cmp #test_num + trap_ne ;test is out of sequence +test_num = test_num + 1 + lda #test_num ;*** next tests' number + sta test_case + ;check_ram ;uncomment to find altered RAM after each test + endm + + if load_data_direct = 1 + data + else + bss ;uninitialized segment, copy of data at end of code! + endif + org zero_page +;break test interrupt save +irq_a ds 1 ;a register +irq_x ds 1 ;x register + if I_flag = 2 +;masking for I bit in status +flag_I_on ds 1 ;or mask to load flags +flag_I_off ds 1 ;and mask to load flags + endif +zpt ;5 bytes store/modify test area +;add/subtract operand generation and result/flag prediction +adfc ds 1 ;carry flag before op +ad1 ds 1 ;operand 1 - accumulator +ad2 ds 1 ;operand 2 - memory / immediate +adrl ds 1 ;expected result bits 0-7 +adrh ds 1 ;expected result bit 8 (carry) +adrf ds 1 ;expected flags NV0000ZC (only binary mode) +sb2 ds 1 ;operand 2 complemented for subtract +zp_bss +zp1 db $c3,$82,$41,0 ;test patterns for LDx BIT ROL ROR ASL LSR +zp7f db $7f ;test pattern for compare +;logical zeropage operands +zpOR db 0,$1f,$71,$80 ;test pattern for OR +zpAN db $0f,$ff,$7f,$80 ;test pattern for AND +zpEO db $ff,$0f,$8f,$8f ;test pattern for EOR +;indirect addressing pointers +ind1 dw abs1 ;indirect pointer to pattern in absolute memory + dw abs1+1 + dw abs1+2 + dw abs1+3 + dw abs7f +inw1 dw abs1-$f8 ;indirect pointer for wrap-test pattern +indt dw abst ;indirect pointer to store area in absolute memory + dw abst+1 + dw abst+2 + dw abst+3 +inwt dw abst-$f8 ;indirect pointer for wrap-test store +indAN dw absAN ;indirect pointer to AND pattern in absolute memory + dw absAN+1 + dw absAN+2 + dw absAN+3 +indEO dw absEO ;indirect pointer to EOR pattern in absolute memory + dw absEO+1 + dw absEO+2 + dw absEO+3 +indOR dw absOR ;indirect pointer to OR pattern in absolute memory + dw absOR+1 + dw absOR+2 + dw absOR+3 +;add/subtract indirect pointers +adi2 dw ada2 ;indirect pointer to operand 2 in absolute memory +sbi2 dw sba2 ;indirect pointer to complemented operand 2 (SBC) +adiy2 dw ada2-$ff ;with offset for indirect indexed +sbiy2 dw sba2-$ff +zp_bss_end + + org data_segment +test_case ds 1 ;current test number +ram_chksm ds 2 ;checksum for RAM integrity test +;add/subtract operand copy - abs tests write area +abst ;5 bytes store/modify test area +ada2 ds 1 ;operand 2 +sba2 ds 1 ;operand 2 complemented for subtract + ds 3 ;fill remaining bytes +data_bss + if load_data_direct = 1 +ex_andi and #0 ;execute immediate opcodes + rts +ex_eori eor #0 ;execute immediate opcodes + rts +ex_orai ora #0 ;execute immediate opcodes + rts +ex_adci adc #0 ;execute immediate opcodes + rts +ex_sbci sbc #0 ;execute immediate opcodes + rts + else +ex_andi ds 3 +ex_eori ds 3 +ex_orai ds 3 +ex_adci ds 3 +ex_sbci ds 3 + endif +abs1 db $c3,$82,$41,0 ;test patterns for LDx BIT ROL ROR ASL LSR +abs7f db $7f ;test pattern for compare +;loads +fLDx db fn,fn,0,fz ;expected flags for load +;shifts +rASL ;expected result ASL & ROL -carry +rROL db $86,$04,$82,0 ; " +rROLc db $87,$05,$83,1 ;expected result ROL +carry +rLSR ;expected result LSR & ROR -carry +rROR db $61,$41,$20,0 ; " +rRORc db $e1,$c1,$a0,$80 ;expected result ROR +carry +fASL ;expected flags for shifts +fROL db fnc,fc,fn,fz ;no carry in +fROLc db fnc,fc,fn,0 ;carry in +fLSR +fROR db fc,0,fc,fz ;no carry in +fRORc db fnc,fn,fnc,fn ;carry in +;increments (decrements) +rINC db $7f,$80,$ff,0,1 ;expected result for INC/DEC +fINC db 0,fn,fn,fz,0 ;expected flags for INC/DEC +;logical memory operand +absOR db 0,$1f,$71,$80 ;test pattern for OR +absAN db $0f,$ff,$7f,$80 ;test pattern for AND +absEO db $ff,$0f,$8f,$8f ;test pattern for EOR +;logical accu operand +absORa db 0,$f1,$1f,0 ;test pattern for OR +absANa db $f0,$ff,$ff,$ff ;test pattern for AND +absEOa db $ff,$f0,$f0,$0f ;test pattern for EOR +;logical results +absrlo db 0,$ff,$7f,$80 +absflo db fz,fn,0,fn +data_bss_end + + + code + org code_segment +start cld + ldx #$ff + txs + lda #0 ;*** test 0 = initialize + sta test_case +test_num = 0 + +;stop interrupts before initializing BSS + if I_flag = 1 + sei + endif + +;initialize I/O for report channel + if report = 1 + jsr report_init + endif + +;pretest small branch offset + ldx #5 + jmp psb_test +psb_bwok + ldy #5 + bne psb_forw + trap ;branch should be taken + dey ;forward landing zone + dey + dey + dey + dey +psb_forw + dey + dey + dey + dey + dey + beq psb_fwok + trap ;forward offset + + dex ;backward landing zone + dex + dex + dex + dex +psb_back + dex + dex + dex + dex + dex + beq psb_bwok + trap ;backward offset +psb_test + bne psb_back + trap ;branch should be taken +psb_fwok + +;initialize BSS segment + if load_data_direct != 1 + ldx #zp_end-zp_init-1 +ld_zp lda zp_init,x + sta zp_bss,x + dex + bpl ld_zp + ldx #data_end-data_init-1 +ld_data lda data_init,x + sta data_bss,x + dex + bpl ld_data + if ROM_vectors = 1 + ldx #5 +ld_vect lda vec_init,x + sta vec_bss,x + dex + bpl ld_vect + endif + endif + +;retain status of interrupt flag + if I_flag = 2 + php + pla + and #4 ;isolate flag + sta flag_I_on ;or mask + eor #lo(~4) ;reverse + sta flag_I_off ;and mask + endif + +;generate checksum for RAM integrity test + if ram_top > -1 + lda #0 + sta zpt ;set low byte of indirect pointer + sta ram_chksm+1 ;checksum high byte + if disable_selfmod = 0 + sta range_adr ;reset self modifying code + endif + clc + ldx #zp_bss-zero_page ;zeropage - write test area +gcs3 adc zero_page,x + bcc gcs2 + inc ram_chksm+1 ;carry to high byte + clc +gcs2 inx + bne gcs3 + ldx #hi(abs1) ;set high byte of indirect pointer + stx zpt+1 + ldy #lo(abs1) ;data after write & execute test area +gcs5 adc (zpt),y + bcc gcs4 + inc ram_chksm+1 ;carry to high byte + clc +gcs4 iny + bne gcs5 + inx ;advance RAM high address + stx zpt+1 + cpx #ram_top + bne gcs5 + sta ram_chksm ;checksum complete + endif + next_test + + if disable_selfmod = 0 +;testing relative addressing with BEQ + ldy #$fe ;testing maximum range, not -1/-2 (invalid/self adr) +range_loop + dey ;next relative address + tya + tax ;precharge count to end of loop + bpl range_fw ;calculate relative address + clc ;avoid branch self or to relative address of branch + adc #2 + nop ;offset landing zone - tolerate +/-5 offset to branch + nop + nop + nop + nop +range_fw + nop + nop + nop + nop + nop + eor #$7f ;complement except sign + sta range_adr ;load into test target + lda #0 ;should set zero flag in status register + jmp range_op + + dex ; offset landing zone - backward branch too far + dex + dex + dex + dex + ;relative address target field with branch under test in the middle + dex ;-128 - max backward + dex + dex + dex + dex + dex + dex + dex + dex ;-120 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-110 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-100 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-90 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-80 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-70 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-60 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-50 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-40 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-30 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-20 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;-10 + dex + dex + dex + dex + dex + dex + dex ;-3 +range_op ;test target with zero flag=0, z=1 if previous dex +range_adr = *+1 ;modifiable relative address + beq *+64 ;+64 if called without modification + dex ;+0 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+10 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+20 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+30 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+40 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+50 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+60 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+70 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+80 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+90 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+100 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+110 + dex + dex + dex + dex + dex + dex + dex + dex + dex + dex ;+120 + dex + dex + dex + dex + dex + dex + nop ;offset landing zone - forward branch too far + nop + nop + nop + nop + beq range_ok ;+127 - max forward + trap ; bad range + nop ;offset landing zone - tolerate +/-5 offset to branch + nop + nop + nop + nop +range_ok + nop + nop + nop + nop + nop + cpy #0 + beq range_end + jmp range_loop +range_end ;range test successful + endif + next_test + +;partial test BNE & CMP, CPX, CPY immediate + cpy #1 ;testing BNE true + bne test_bne + trap +test_bne + lda #0 + cmp #0 ;test compare immediate + trap_ne + trap_cc + trap_mi + cmp #1 + trap_eq + trap_cs + trap_pl + tax + cpx #0 ;test compare x immediate + trap_ne + trap_cc + trap_mi + cpx #1 + trap_eq + trap_cs + trap_pl + tay + cpy #0 ;test compare y immediate + trap_ne + trap_cc + trap_mi + cpy #1 + trap_eq + trap_cs + trap_pl + next_test +;testing stack operations PHA PHP PLA PLP + + ldx #$ff ;initialize stack + txs + lda #$55 + pha + lda #$aa + pha + cmp $1fe ;on stack ? + trap_ne + tsx + txa ;overwrite accu + cmp #$fd ;sp decremented? + trap_ne + pla + cmp #$aa ;successful retreived from stack? + trap_ne + pla + cmp #$55 + trap_ne + cmp $1ff ;remains on stack? + trap_ne + tsx + cpx #$ff ;sp incremented? + trap_ne + next_test + +;testing branch decisions BPL BMI BVC BVS BCC BCS BNE BEQ + set_stat $ff ;all on + bpl nbr1 ;branches should not be taken + bvc nbr2 + bcc nbr3 + bne nbr4 + bmi br1 ;branches should be taken + trap +br1 bvs br2 + trap +br2 bcs br3 + trap +br3 beq br4 + trap +nbr1 + trap ;previous bpl taken +nbr2 + trap ;previous bvc taken +nbr3 + trap ;previous bcc taken +nbr4 + trap ;previous bne taken +br4 php + tsx + cpx #$fe ;sp after php? + trap_ne + pla + cmp_flag $ff ;returned all flags on? + trap_ne + tsx + cpx #$ff ;sp after php? + trap_ne + set_stat 0 ;all off + bmi nbr11 ;branches should not be taken + bvs nbr12 + bcs nbr13 + beq nbr14 + bpl br11 ;branches should be taken + trap +br11 bvc br12 + trap +br12 bcc br13 + trap +br13 bne br14 + trap +nbr11 + trap ;previous bmi taken +nbr12 + trap ;previous bvs taken +nbr13 + trap ;previous bcs taken +nbr14 + trap ;previous beq taken +br14 php + pla + cmp_flag 0 ;flags off except break (pushed by sw) + reserved? + trap_ne + ;crosscheck flags + set_stat zero + bne brzs1 + beq brzs2 +brzs1 + trap ;branch zero/non zero +brzs2 bcs brzs3 + bcc brzs4 +brzs3 + trap ;branch carry/no carry +brzs4 bmi brzs5 + bpl brzs6 +brzs5 + trap ;branch minus/plus +brzs6 bvs brzs7 + bvc brzs8 +brzs7 + trap ;branch overflow/no overflow +brzs8 + set_stat carry + beq brcs1 + bne brcs2 +brcs1 + trap ;branch zero/non zero +brcs2 bcc brcs3 + bcs brcs4 +brcs3 + trap ;branch carry/no carry +brcs4 bmi brcs5 + bpl brcs6 +brcs5 + trap ;branch minus/plus +brcs6 bvs brcs7 + bvc brcs8 +brcs7 + trap ;branch overflow/no overflow + +brcs8 + set_stat minus + beq brmi1 + bne brmi2 +brmi1 + trap ;branch zero/non zero +brmi2 bcs brmi3 + bcc brmi4 +brmi3 + trap ;branch carry/no carry +brmi4 bpl brmi5 + bmi brmi6 +brmi5 + trap ;branch minus/plus +brmi6 bvs brmi7 + bvc brmi8 +brmi7 + trap ;branch overflow/no overflow +brmi8 + set_stat overfl + beq brvs1 + bne brvs2 +brvs1 + trap ;branch zero/non zero +brvs2 bcs brvs3 + bcc brvs4 +brvs3 + trap ;branch carry/no carry +brvs4 bmi brvs5 + bpl brvs6 +brvs5 + trap ;branch minus/plus +brvs6 bvc brvs7 + bvs brvs8 +brvs7 + trap ;branch overflow/no overflow +brvs8 + set_stat $ff-zero + beq brzc1 + bne brzc2 +brzc1 + trap ;branch zero/non zero +brzc2 bcc brzc3 + bcs brzc4 +brzc3 + trap ;branch carry/no carry +brzc4 bpl brzc5 + bmi brzc6 +brzc5 + trap ;branch minus/plus +brzc6 bvc brzc7 + bvs brzc8 +brzc7 + trap ;branch overflow/no overflow +brzc8 + set_stat $ff-carry + bne brcc1 + beq brcc2 +brcc1 + trap ;branch zero/non zero +brcc2 bcs brcc3 + bcc brcc4 +brcc3 + trap ;branch carry/no carry +brcc4 bpl brcc5 + bmi brcc6 +brcc5 + trap ;branch minus/plus +brcc6 bvc brcc7 + bvs brcc8 +brcc7 + trap ;branch overflow/no overflow +brcc8 + set_stat $ff-minus + bne brpl1 + beq brpl2 +brpl1 + trap ;branch zero/non zero +brpl2 bcc brpl3 + bcs brpl4 +brpl3 + trap ;branch carry/no carry +brpl4 bmi brpl5 + bpl brpl6 +brpl5 + trap ;branch minus/plus +brpl6 bvc brpl7 + bvs brpl8 +brpl7 + trap ;branch overflow/no overflow +brpl8 + set_stat $ff-overfl + bne brvc1 + beq brvc2 +brvc1 + trap ;branch zero/non zero +brvc2 bcc brvc3 + bcs brvc4 +brvc3 + trap ;branch carry/no carry +brvc4 bpl brvc5 + bmi brvc6 +brvc5 + trap ;branch minus/plus +brvc6 bvs brvc7 + bvc brvc8 +brvc7 + trap ;branch overflow/no overflow +brvc8 + next_test + +; test PHA does not alter flags or accumulator but PLA does + ldx #$55 ;x & y protected + ldy #$aa + set_a 1,$ff ;push + pha + tst_a 1,$ff + set_a 0,0 + pha + tst_a 0,0 + set_a $ff,$ff + pha + tst_a $ff,$ff + set_a 1,0 + pha + tst_a 1,0 + set_a 0,$ff + pha + tst_a 0,$ff + set_a $ff,0 + pha + tst_a $ff,0 + set_a 0,$ff ;pull + pla + tst_a $ff,$ff-zero + set_a $ff,0 + pla + tst_a 0,zero + set_a $fe,$ff + pla + tst_a 1,$ff-zero-minus + set_a 0,0 + pla + tst_a $ff,minus + set_a $ff,$ff + pla + tst_a 0,$ff-minus + set_a $fe,0 + pla + tst_a 1,0 + cpx #$55 ;x & y unchanged? + trap_ne + cpy #$aa + trap_ne + next_test + +; partial pretest EOR # + set_a $3c,0 + eor #$c3 + tst_a $ff,fn + set_a $c3,0 + eor #$c3 + tst_a 0,fz + next_test + +; PC modifying instructions except branches (NOP, JMP, JSR, RTS, BRK, RTI) +; testing NOP + ldx #$24 + ldy #$42 + set_a $18,0 + nop + tst_a $18,0 + cpx #$24 + trap_ne + cpy #$42 + trap_ne + ldx #$db + ldy #$bd + set_a $e7,$ff + nop + tst_a $e7,$ff + cpx #$db + trap_ne + cpy #$bd + trap_ne + next_test + +; jump absolute + set_stat $0 + lda #'F' + ldx #'A' + ldy #'R' ;N=0, V=0, Z=0, C=0 + jmp test_far + nop + nop + trap_ne ;runover protection + inx + inx +far_ret + trap_eq ;returned flags OK? + trap_pl + trap_cc + trap_vc + cmp #('F'^$aa) ;returned registers OK? + trap_ne + cpx #('A'+1) + trap_ne + cpy #('R'-3) + trap_ne + dex + iny + iny + iny + eor #$aa ;N=0, V=1, Z=0, C=1 + jmp test_near + nop + nop + trap_ne ;runover protection + inx + inx +test_near + trap_eq ;passed flags OK? + trap_mi + trap_cc + trap_vc + cmp #'F' ;passed registers OK? + trap_ne + cpx #'A' + trap_ne + cpy #'R' + trap_ne + next_test + +; jump indirect + set_stat 0 + lda #'I' + ldx #'N' + ldy #'D' ;N=0, V=0, Z=0, C=0 + jmp (ptr_tst_ind) + nop + trap_ne ;runover protection + dey + dey +ind_ret + php ;either SP or Y count will fail, if we do not hit + dey + dey + dey + plp + trap_eq ;returned flags OK? + trap_pl + trap_cc + trap_vc + cmp #('I'^$aa) ;returned registers OK? + trap_ne + cpx #('N'+1) + trap_ne + cpy #('D'-6) + trap_ne + tsx ;SP check + cpx #$ff + trap_ne + next_test + +; jump subroutine & return from subroutine + set_stat 0 + lda #'J' + ldx #'S' + ldy #'R' ;N=0, V=0, Z=0, C=0 + jsr test_jsr +jsr_ret = *-1 ;last address of jsr = return address + php ;either SP or Y count will fail, if we do not hit + dey + dey + dey + plp + trap_eq ;returned flags OK? + trap_pl + trap_cc + trap_vc + cmp #('J'^$aa) ;returned registers OK? + trap_ne + cpx #('S'+1) + trap_ne + cpy #('R'-6) + trap_ne + tsx ;sp? + cpx #$ff + trap_ne + next_test + +; break & return from interrupt + if ROM_vectors = 1 + set_stat 0 + lda #'B' + ldx #'R' + ldy #'K' ;N=0, V=0, Z=0, C=0 + brk + else + lda #hi brk_ret ;emulated break + pha + lda #lo brk_ret + pha + lda #fao ;set break & unused on stack + pha + set_stat intdis + lda #'B' + ldx #'R' + ldy #'K' ;N=0, V=0, Z=0, C=0 + jmp irq_trap + endif + dey ;should not be executed +brk_ret ;address of break return + php ;either SP or Y count will fail, if we do not hit + dey + dey + dey + cmp #('B'^$aa) ;returned registers OK? + trap_ne + cpx #('R'+1) + trap_ne + cpy #('K'-6) + trap_ne + pla ;returned flags OK (unchanged)? + cmp_flag 0 + trap_ne + tsx ;sp? + cpx #$ff + trap_ne + next_test + +; test set and clear flags CLC CLI CLD CLV SEC SEI SED + set_stat $ff + clc + tst_stat $ff-carry + sec + tst_stat $ff + if I_flag = 3 + cli + tst_stat $ff-intdis + sei + tst_stat $ff + endif + cld + tst_stat $ff-decmode + sed + tst_stat $ff + clv + tst_stat $ff-overfl + set_stat 0 + tst_stat 0 + sec + tst_stat carry + clc + tst_stat 0 + if I_flag = 3 + sei + tst_stat intdis + cli + tst_stat 0 + endif + sed + tst_stat decmode + cld + tst_stat 0 + set_stat overfl + tst_stat overfl + clv + tst_stat 0 + next_test +; testing index register increment/decrement and transfer +; INX INY DEX DEY TAX TXA TAY TYA + ldx #$fe + set_stat $ff + inx ;ff + tst_x $ff,$ff-zero + inx ;00 + tst_x 0,$ff-minus + inx ;01 + tst_x 1,$ff-minus-zero + dex ;00 + tst_x 0,$ff-minus + dex ;ff + tst_x $ff,$ff-zero + dex ;fe + set_stat 0 + inx ;ff + tst_x $ff,minus + inx ;00 + tst_x 0,zero + inx ;01 + tst_x 1,0 + dex ;00 + tst_x 0,zero + dex ;ff + tst_x $ff,minus + + ldy #$fe + set_stat $ff + iny ;ff + tst_y $ff,$ff-zero + iny ;00 + tst_y 0,$ff-minus + iny ;01 + tst_y 1,$ff-minus-zero + dey ;00 + tst_y 0,$ff-minus + dey ;ff + tst_y $ff,$ff-zero + dey ;fe + set_stat 0 + iny ;ff + tst_y $ff,0+minus + iny ;00 + tst_y 0,zero + iny ;01 + tst_y 1,0 + dey ;00 + tst_y 0,zero + dey ;ff + tst_y $ff,minus + + ldx #$ff + set_stat $ff + txa + tst_a $ff,$ff-zero + php + inx ;00 + plp + txa + tst_a 0,$ff-minus + php + inx ;01 + plp + txa + tst_a 1,$ff-minus-zero + set_stat 0 + txa + tst_a 1,0 + php + dex ;00 + plp + txa + tst_a 0,zero + php + dex ;ff + plp + txa + tst_a $ff,minus + + ldy #$ff + set_stat $ff + tya + tst_a $ff,$ff-zero + php + iny ;00 + plp + tya + tst_a 0,$ff-minus + php + iny ;01 + plp + tya + tst_a 1,$ff-minus-zero + set_stat 0 + tya + tst_a 1,0 + php + dey ;00 + plp + tya + tst_a 0,zero + php + dey ;ff + plp + tya + tst_a $ff,minus + + load_flag $ff + pha + ldx #$ff ;ff + txa + plp + tay + tst_y $ff,$ff-zero + php + inx ;00 + txa + plp + tay + tst_y 0,$ff-minus + php + inx ;01 + txa + plp + tay + tst_y 1,$ff-minus-zero + load_flag 0 + pha + lda #0 + txa + plp + tay + tst_y 1,0 + php + dex ;00 + txa + plp + tay + tst_y 0,zero + php + dex ;ff + txa + plp + tay + tst_y $ff,minus + + + load_flag $ff + pha + ldy #$ff ;ff + tya + plp + tax + tst_x $ff,$ff-zero + php + iny ;00 + tya + plp + tax + tst_x 0,$ff-minus + php + iny ;01 + tya + plp + tax + tst_x 1,$ff-minus-zero + load_flag 0 + pha + lda #0 ;preset status + tya + plp + tax + tst_x 1,0 + php + dey ;00 + tya + plp + tax + tst_x 0,zero + php + dey ;ff + tya + plp + tax + tst_x $ff,minus + next_test + +;TSX sets NZ - TXS does not +; This section also tests for proper stack wrap around. + ldx #1 ;01 + set_stat $ff + txs + php + lda $101 + cmp_flag $ff + trap_ne + set_stat 0 + txs + php + lda $101 + cmp_flag 0 + trap_ne + dex ;00 + set_stat $ff + txs + php + lda $100 + cmp_flag $ff + trap_ne + set_stat 0 + txs + php + lda $100 + cmp_flag 0 + trap_ne + dex ;ff + set_stat $ff + txs + php + lda $1ff + cmp_flag $ff + trap_ne + set_stat 0 + txs + php + lda $1ff + cmp_flag 0 + + ldx #1 + txs ;sp=01 + set_stat $ff + tsx ;clears Z, N + php ;sp=00 + cpx #1 + trap_ne + lda $101 + cmp_flag $ff-minus-zero + trap_ne + set_stat $ff + tsx ;clears N, sets Z + php ;sp=ff + cpx #0 + trap_ne + lda $100 + cmp_flag $ff-minus + trap_ne + set_stat $ff + tsx ;clears N, sets Z + php ;sp=fe + cpx #$ff + trap_ne + lda $1ff + cmp_flag $ff-zero + trap_ne + + ldx #1 + txs ;sp=01 + set_stat 0 + tsx ;clears Z, N + php ;sp=00 + cpx #1 + trap_ne + lda $101 + cmp_flag 0 + trap_ne + set_stat 0 + tsx ;clears N, sets Z + php ;sp=ff + cpx #0 + trap_ne + lda $100 + cmp_flag zero + trap_ne + set_stat 0 + tsx ;clears N, sets Z + php ;sp=fe + cpx #$ff + trap_ne + lda $1ff + cmp_flag minus + trap_ne + pla ;sp=ff + next_test + +; testing index register load & store LDY LDX STY STX all addressing modes +; LDX / STX - zp,y / abs,y + ldy #3 +tldx + set_stat 0 + ldx zp1,y + php ;test stores do not alter flags + txa + eor #$c3 + plp + sta abst,y + php ;flags after load/store sequence + eor #$c3 + cmp abs1,y ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx,y ;test flags + trap_ne + dey + bpl tldx + + ldy #3 +tldx1 + set_stat $ff + ldx zp1,y + php ;test stores do not alter flags + txa + eor #$c3 + plp + sta abst,y + php ;flags after load/store sequence + eor #$c3 + cmp abs1,y ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx,y ;test flags + trap_ne + dey + bpl tldx1 + + ldy #3 +tldx2 + set_stat 0 + ldx abs1,y + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx zpt,y + php ;flags after load/store sequence + eor #$c3 + cmp zp1,y ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx,y ;test flags + trap_ne + dey + bpl tldx2 + + ldy #3 +tldx3 + set_stat $ff + ldx abs1,y + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx zpt,y + php ;flags after load/store sequence + eor #$c3 + cmp zp1,y ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx,y ;test flags + trap_ne + dey + bpl tldx3 + + ldy #3 ;testing store result + ldx #0 +tstx lda zpt,y + eor #$c3 + cmp zp1,y + trap_ne ;store to zp data + stx zpt,y ;clear + lda abst,y + eor #$c3 + cmp abs1,y + trap_ne ;store to abs data + txa + sta abst,y ;clear + dey + bpl tstx + next_test + +; indexed wraparound test (only zp should wrap) + ldy #3+$fa +tldx4 ldx zp1-$fa&$ff,y ;wrap on indexed zp + txa + sta abst-$fa,y ;no STX abs,y! + dey + cpy #$fa + bcs tldx4 + ldy #3+$fa +tldx5 ldx abs1-$fa,y ;no wrap on indexed abs + stx zpt-$fa&$ff,y + dey + cpy #$fa + bcs tldx5 + ldy #3 ;testing wraparound result + ldx #0 +tstx1 lda zpt,y + cmp zp1,y + trap_ne ;store to zp data + stx zpt,y ;clear + lda abst,y + cmp abs1,y + trap_ne ;store to abs data + txa + sta abst,y ;clear + dey + bpl tstx1 + next_test + +; LDY / STY - zp,x / abs,x + ldx #3 +tldy + set_stat 0 + ldy zp1,x + php ;test stores do not alter flags + tya + eor #$c3 + plp + sta abst,x + php ;flags after load/store sequence + eor #$c3 + cmp abs1,x ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx,x ;test flags + trap_ne + dex + bpl tldy + + ldx #3 +tldy1 + set_stat $ff + ldy zp1,x + php ;test stores do not alter flags + tya + eor #$c3 + plp + sta abst,x + php ;flags after load/store sequence + eor #$c3 + cmp abs1,x ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx,x ;test flags + trap_ne + dex + bpl tldy1 + + ldx #3 +tldy2 + set_stat 0 + ldy abs1,x + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty zpt,x + php ;flags after load/store sequence + eor #$c3 + cmp zp1,x ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx,x ;test flags + trap_ne + dex + bpl tldy2 + + ldx #3 +tldy3 + set_stat $ff + ldy abs1,x + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty zpt,x + php ;flags after load/store sequence + eor #$c3 + cmp zp1,x ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx,x ;test flags + trap_ne + dex + bpl tldy3 + + ldx #3 ;testing store result + ldy #0 +tsty lda zpt,x + eor #$c3 + cmp zp1,x + trap_ne ;store to zp,x data + sty zpt,x ;clear + lda abst,x + eor #$c3 + cmp abs1,x + trap_ne ;store to abs,x data + txa + sta abst,x ;clear + dex + bpl tsty + next_test + +; indexed wraparound test (only zp should wrap) + ldx #3+$fa +tldy4 ldy zp1-$fa&$ff,x ;wrap on indexed zp + tya + sta abst-$fa,x ;no STX abs,x! + dex + cpx #$fa + bcs tldy4 + ldx #3+$fa +tldy5 ldy abs1-$fa,x ;no wrap on indexed abs + sty zpt-$fa&$ff,x + dex + cpx #$fa + bcs tldy5 + ldx #3 ;testing wraparound result + ldy #0 +tsty1 lda zpt,x + cmp zp1,x + trap_ne ;store to zp,x data + sty zpt,x ;clear + lda abst,x + cmp abs1,x + trap_ne ;store to abs,x data + txa + sta abst,x ;clear + dex + bpl tsty1 + next_test + +; LDX / STX - zp / abs / # + set_stat 0 + ldx zp1 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx abst + php ;flags after load/store sequence + eor #$c3 + tax + cpx #$c3 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx ;test flags + trap_ne + set_stat 0 + ldx zp1+1 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx abst+1 + php ;flags after load/store sequence + eor #$c3 + tax + cpx #$82 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+1 ;test flags + trap_ne + set_stat 0 + ldx zp1+2 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx abst+2 + php ;flags after load/store sequence + eor #$c3 + tax + cpx #$41 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+2 ;test flags + trap_ne + set_stat 0 + ldx zp1+3 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx abst+3 + php ;flags after load/store sequence + eor #$c3 + tax + cpx #0 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+3 ;test flags + trap_ne + + set_stat $ff + ldx zp1 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx abst + php ;flags after load/store sequence + eor #$c3 + tax + cpx #$c3 ;test result + trap_ne ; + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx ;test flags + trap_ne + set_stat $ff + ldx zp1+1 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx abst+1 + php ;flags after load/store sequence + eor #$c3 + tax + cpx #$82 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+1 ;test flags + trap_ne + set_stat $ff + ldx zp1+2 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx abst+2 + php ;flags after load/store sequence + eor #$c3 + tax + cpx #$41 ;test result + trap_ne ; + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+2 ;test flags + trap_ne + set_stat $ff + ldx zp1+3 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx abst+3 + php ;flags after load/store sequence + eor #$c3 + tax + cpx #0 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+3 ;test flags + trap_ne + + set_stat 0 + ldx abs1 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx zpt + php ;flags after load/store sequence + eor #$c3 + cmp zp1 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx ;test flags + trap_ne + set_stat 0 + ldx abs1+1 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx zpt+1 + php ;flags after load/store sequence + eor #$c3 + cmp zp1+1 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+1 ;test flags + trap_ne + set_stat 0 + ldx abs1+2 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx zpt+2 + php ;flags after load/store sequence + eor #$c3 + cmp zp1+2 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+2 ;test flags + trap_ne + set_stat 0 + ldx abs1+3 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx zpt+3 + php ;flags after load/store sequence + eor #$c3 + cmp zp1+3 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+3 ;test flags + trap_ne + + set_stat $ff + ldx abs1 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx zpt + php ;flags after load/store sequence + eor #$c3 + tax + cpx zp1 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx ;test flags + trap_ne + set_stat $ff + ldx abs1+1 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx zpt+1 + php ;flags after load/store sequence + eor #$c3 + tax + cpx zp1+1 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+1 ;test flags + trap_ne + set_stat $ff + ldx abs1+2 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx zpt+2 + php ;flags after load/store sequence + eor #$c3 + tax + cpx zp1+2 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+2 ;test flags + trap_ne + set_stat $ff + ldx abs1+3 + php ;test stores do not alter flags + txa + eor #$c3 + tax + plp + stx zpt+3 + php ;flags after load/store sequence + eor #$c3 + tax + cpx zp1+3 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+3 ;test flags + trap_ne + + set_stat 0 + ldx #$c3 + php + cpx abs1 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx ;test flags + trap_ne + set_stat 0 + ldx #$82 + php + cpx abs1+1 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+1 ;test flags + trap_ne + set_stat 0 + ldx #$41 + php + cpx abs1+2 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+2 ;test flags + trap_ne + set_stat 0 + ldx #0 + php + cpx abs1+3 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+3 ;test flags + trap_ne + + set_stat $ff + ldx #$c3 + php + cpx abs1 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx ;test flags + trap_ne + set_stat $ff + ldx #$82 + php + cpx abs1+1 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+1 ;test flags + trap_ne + set_stat $ff + ldx #$41 + php + cpx abs1+2 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+2 ;test flags + trap_ne + set_stat $ff + ldx #0 + php + cpx abs1+3 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+3 ;test flags + trap_ne + + ldx #0 + lda zpt + eor #$c3 + cmp zp1 + trap_ne ;store to zp data + stx zpt ;clear + lda abst + eor #$c3 + cmp abs1 + trap_ne ;store to abs data + stx abst ;clear + lda zpt+1 + eor #$c3 + cmp zp1+1 + trap_ne ;store to zp data + stx zpt+1 ;clear + lda abst+1 + eor #$c3 + cmp abs1+1 + trap_ne ;store to abs data + stx abst+1 ;clear + lda zpt+2 + eor #$c3 + cmp zp1+2 + trap_ne ;store to zp data + stx zpt+2 ;clear + lda abst+2 + eor #$c3 + cmp abs1+2 + trap_ne ;store to abs data + stx abst+2 ;clear + lda zpt+3 + eor #$c3 + cmp zp1+3 + trap_ne ;store to zp data + stx zpt+3 ;clear + lda abst+3 + eor #$c3 + cmp abs1+3 + trap_ne ;store to abs data + stx abst+3 ;clear + next_test + +; LDY / STY - zp / abs / # + set_stat 0 + ldy zp1 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty abst + php ;flags after load/store sequence + eor #$c3 + tay + cpy #$c3 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx ;test flags + trap_ne + set_stat 0 + ldy zp1+1 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty abst+1 + php ;flags after load/store sequence + eor #$c3 + tay + cpy #$82 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+1 ;test flags + trap_ne + set_stat 0 + ldy zp1+2 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty abst+2 + php ;flags after load/store sequence + eor #$c3 + tay + cpy #$41 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+2 ;test flags + trap_ne + set_stat 0 + ldy zp1+3 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty abst+3 + php ;flags after load/store sequence + eor #$c3 + tay + cpy #0 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+3 ;test flags + trap_ne + + set_stat $ff + ldy zp1 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty abst + php ;flags after load/store sequence + eor #$c3 + tay + cpy #$c3 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx ;test flags + trap_ne + set_stat $ff + ldy zp1+1 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty abst+1 + php ;flags after load/store sequence + eor #$c3 + tay + cpy #$82 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+1 ;test flags + trap_ne + set_stat $ff + ldy zp1+2 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty abst+2 + php ;flags after load/store sequence + eor #$c3 + tay + cpy #$41 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+2 ;test flags + trap_ne + set_stat $ff + ldy zp1+3 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty abst+3 + php ;flags after load/store sequence + eor #$c3 + tay + cpy #0 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+3 ;test flags + trap_ne + + set_stat 0 + ldy abs1 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty zpt + php ;flags after load/store sequence + eor #$c3 + tay + cpy zp1 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx ;test flags + trap_ne + set_stat 0 + ldy abs1+1 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty zpt+1 + php ;flags after load/store sequence + eor #$c3 + tay + cpy zp1+1 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+1 ;test flags + trap_ne + set_stat 0 + ldy abs1+2 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty zpt+2 + php ;flags after load/store sequence + eor #$c3 + tay + cpy zp1+2 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+2 ;test flags + trap_ne + set_stat 0 + ldy abs1+3 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty zpt+3 + php ;flags after load/store sequence + eor #$c3 + tay + cpy zp1+3 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+3 ;test flags + trap_ne + + set_stat $ff + ldy abs1 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty zpt + php ;flags after load/store sequence + eor #$c3 + tay + cmp zp1 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx ;test flags + trap_ne + set_stat $ff + ldy abs1+1 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty zpt+1 + php ;flags after load/store sequence + eor #$c3 + tay + cmp zp1+1 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+1 ;test flags + trap_ne + set_stat $ff + ldy abs1+2 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty zpt+2 + php ;flags after load/store sequence + eor #$c3 + tay + cmp zp1+2 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+2 ;test flags + trap_ne + set_stat $ff + ldy abs1+3 + php ;test stores do not alter flags + tya + eor #$c3 + tay + plp + sty zpt+3 + php ;flags after load/store sequence + eor #$c3 + tay + cmp zp1+3 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+3 ;test flags + trap_ne + + + set_stat 0 + ldy #$c3 + php + cpy abs1 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx ;test flags + trap_ne + set_stat 0 + ldy #$82 + php + cpy abs1+1 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+1 ;test flags + trap_ne + set_stat 0 + ldy #$41 + php + cpy abs1+2 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+2 ;test flags + trap_ne + set_stat 0 + ldy #0 + php + cpy abs1+3 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+3 ;test flags + trap_ne + + set_stat $ff + ldy #$c3 + php + cpy abs1 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx ;test flags + trap_ne + set_stat $ff + ldy #$82 + php + cpy abs1+1 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+1 ;test flags + trap_ne + set_stat $ff + ldy #$41 + php + cpy abs1+2 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+2 ;test flags + trap_ne + set_stat $ff + ldy #0 + php + cpy abs1+3 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+3 ;test flags + trap_ne + + ldy #0 + lda zpt + eor #$c3 + cmp zp1 + trap_ne ;store to zp data + sty zpt ;clear + lda abst + eor #$c3 + cmp abs1 + trap_ne ;store to abs data + sty abst ;clear + lda zpt+1 + eor #$c3 + cmp zp1+1 + trap_ne ;store to zp+1 data + sty zpt+1 ;clear + lda abst+1 + eor #$c3 + cmp abs1+1 + trap_ne ;store to abs+1 data + sty abst+1 ;clear + lda zpt+2 + eor #$c3 + cmp zp1+2 + trap_ne ;store to zp+2 data + sty zpt+2 ;clear + lda abst+2 + eor #$c3 + cmp abs1+2 + trap_ne ;store to abs+2 data + sty abst+2 ;clear + lda zpt+3 + eor #$c3 + cmp zp1+3 + trap_ne ;store to zp+3 data + sty zpt+3 ;clear + lda abst+3 + eor #$c3 + cmp abs1+3 + trap_ne ;store to abs+3 data + sty abst+3 ;clear + next_test + +; testing load / store accumulator LDA / STA all addressing modes +; LDA / STA - zp,x / abs,x + ldx #3 +tldax + set_stat 0 + lda zp1,x + php ;test stores do not alter flags + eor #$c3 + plp + sta abst,x + php ;flags after load/store sequence + eor #$c3 + cmp abs1,x ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx,x ;test flags + trap_ne + dex + bpl tldax + + ldx #3 +tldax1 + set_stat $ff + lda zp1,x + php ;test stores do not alter flags + eor #$c3 + plp + sta abst,x + php ;flags after load/store sequence + eor #$c3 + cmp abs1,x ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx,x ;test flags + trap_ne + dex + bpl tldax1 + + ldx #3 +tldax2 + set_stat 0 + lda abs1,x + php ;test stores do not alter flags + eor #$c3 + plp + sta zpt,x + php ;flags after load/store sequence + eor #$c3 + cmp zp1,x ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx,x ;test flags + trap_ne + dex + bpl tldax2 + + ldx #3 +tldax3 + set_stat $ff + lda abs1,x + php ;test stores do not alter flags + eor #$c3 + plp + sta zpt,x + php ;flags after load/store sequence + eor #$c3 + cmp zp1,x ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx,x ;test flags + trap_ne + dex + bpl tldax3 + + ldx #3 ;testing store result + ldy #0 +tstax lda zpt,x + eor #$c3 + cmp zp1,x + trap_ne ;store to zp,x data + sty zpt,x ;clear + lda abst,x + eor #$c3 + cmp abs1,x + trap_ne ;store to abs,x data + txa + sta abst,x ;clear + dex + bpl tstax + next_test + +; LDA / STA - (zp),y / abs,y / (zp,x) + ldy #3 +tlday + set_stat 0 + lda (ind1),y + php ;test stores do not alter flags + eor #$c3 + plp + sta abst,y + php ;flags after load/store sequence + eor #$c3 + cmp abs1,y ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx,y ;test flags + trap_ne + dey + bpl tlday + + ldy #3 +tlday1 + set_stat $ff + lda (ind1),y + php ;test stores do not alter flags + eor #$c3 + plp + sta abst,y + php ;flags after load/store sequence + eor #$c3 + cmp abs1,y ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx,y ;test flags + trap_ne + dey + bpl tlday1 + + ldy #3 ;testing store result + ldx #0 +tstay lda abst,y + eor #$c3 + cmp abs1,y + trap_ne ;store to abs data + txa + sta abst,y ;clear + dey + bpl tstay + + ldy #3 +tlday2 + set_stat 0 + lda abs1,y + php ;test stores do not alter flags + eor #$c3 + plp + sta (indt),y + php ;flags after load/store sequence + eor #$c3 + cmp (ind1),y ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx,y ;test flags + trap_ne + dey + bpl tlday2 + + ldy #3 +tlday3 + set_stat $ff + lda abs1,y + php ;test stores do not alter flags + eor #$c3 + plp + sta (indt),y + php ;flags after load/store sequence + eor #$c3 + cmp (ind1),y ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx,y ;test flags + trap_ne + dey + bpl tlday3 + + ldy #3 ;testing store result + ldx #0 +tstay1 lda abst,y + eor #$c3 + cmp abs1,y + trap_ne ;store to abs data + txa + sta abst,y ;clear + dey + bpl tstay1 + + ldx #6 + ldy #3 +tldax4 + set_stat 0 + lda (ind1,x) + php ;test stores do not alter flags + eor #$c3 + plp + sta (indt,x) + php ;flags after load/store sequence + eor #$c3 + cmp abs1,y ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx,y ;test flags + trap_ne + dex + dex + dey + bpl tldax4 + + ldx #6 + ldy #3 +tldax5 + set_stat $ff + lda (ind1,x) + php ;test stores do not alter flags + eor #$c3 + plp + sta (indt,x) + php ;flags after load/store sequence + eor #$c3 + cmp abs1,y ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx,y ;test flags + trap_ne + dex + dex + dey + bpl tldax5 + + ldy #3 ;testing store result + ldx #0 +tstay2 lda abst,y + eor #$c3 + cmp abs1,y + trap_ne ;store to abs data + txa + sta abst,y ;clear + dey + bpl tstay2 + next_test + +; indexed wraparound test (only zp should wrap) + ldx #3+$fa +tldax6 lda zp1-$fa&$ff,x ;wrap on indexed zp + sta abst-$fa,x ;no STX abs,x! + dex + cpx #$fa + bcs tldax6 + ldx #3+$fa +tldax7 lda abs1-$fa,x ;no wrap on indexed abs + sta zpt-$fa&$ff,x + dex + cpx #$fa + bcs tldax7 + + ldx #3 ;testing wraparound result + ldy #0 +tstax1 lda zpt,x + cmp zp1,x + trap_ne ;store to zp,x data + sty zpt,x ;clear + lda abst,x + cmp abs1,x + trap_ne ;store to abs,x data + txa + sta abst,x ;clear + dex + bpl tstax1 + + ldy #3+$f8 + ldx #6+$f8 +tlday4 lda (ind1-$f8&$ff,x) ;wrap on indexed zp indirect + sta abst-$f8,y + dex + dex + dey + cpy #$f8 + bcs tlday4 + ldy #3 ;testing wraparound result + ldx #0 +tstay4 lda abst,y + cmp abs1,y + trap_ne ;store to abs data + txa + sta abst,y ;clear + dey + bpl tstay4 + + ldy #3+$f8 +tlday5 lda abs1-$f8,y ;no wrap on indexed abs + sta (inwt),y + dey + cpy #$f8 + bcs tlday5 + ldy #3 ;testing wraparound result + ldx #0 +tstay5 lda abst,y + cmp abs1,y + trap_ne ;store to abs data + txa + sta abst,y ;clear + dey + bpl tstay5 + + ldy #3+$f8 + ldx #6+$f8 +tlday6 lda (inw1),y ;no wrap on zp indirect indexed + sta (indt-$f8&$ff,x) + dex + dex + dey + cpy #$f8 + bcs tlday6 + ldy #3 ;testing wraparound result + ldx #0 +tstay6 lda abst,y + cmp abs1,y + trap_ne ;store to abs data + txa + sta abst,y ;clear + dey + bpl tstay6 + next_test + +; LDA / STA - zp / abs / # + set_stat 0 + lda zp1 + php ;test stores do not alter flags + eor #$c3 + plp + sta abst + php ;flags after load/store sequence + eor #$c3 + cmp #$c3 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx ;test flags + trap_ne + set_stat 0 + lda zp1+1 + php ;test stores do not alter flags + eor #$c3 + plp + sta abst+1 + php ;flags after load/store sequence + eor #$c3 + cmp #$82 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+1 ;test flags + trap_ne + set_stat 0 + lda zp1+2 + php ;test stores do not alter flags + eor #$c3 + plp + sta abst+2 + php ;flags after load/store sequence + eor #$c3 + cmp #$41 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+2 ;test flags + trap_ne + set_stat 0 + lda zp1+3 + php ;test stores do not alter flags + eor #$c3 + plp + sta abst+3 + php ;flags after load/store sequence + eor #$c3 + cmp #0 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+3 ;test flags + trap_ne + set_stat $ff + lda zp1 + php ;test stores do not alter flags + eor #$c3 + plp + sta abst + php ;flags after load/store sequence + eor #$c3 + cmp #$c3 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx ;test flags + trap_ne + set_stat $ff + lda zp1+1 + php ;test stores do not alter flags + eor #$c3 + plp + sta abst+1 + php ;flags after load/store sequence + eor #$c3 + cmp #$82 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+1 ;test flags + trap_ne + set_stat $ff + lda zp1+2 + php ;test stores do not alter flags + eor #$c3 + plp + sta abst+2 + php ;flags after load/store sequence + eor #$c3 + cmp #$41 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+2 ;test flags + trap_ne + set_stat $ff + lda zp1+3 + php ;test stores do not alter flags + eor #$c3 + plp + sta abst+3 + php ;flags after load/store sequence + eor #$c3 + cmp #0 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+3 ;test flags + trap_ne + set_stat 0 + lda abs1 + php ;test stores do not alter flags + eor #$c3 + plp + sta zpt + php ;flags after load/store sequence + eor #$c3 + cmp zp1 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx ;test flags + trap_ne + set_stat 0 + lda abs1+1 + php ;test stores do not alter flags + eor #$c3 + plp + sta zpt+1 + php ;flags after load/store sequence + eor #$c3 + cmp zp1+1 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+1 ;test flags + trap_ne + set_stat 0 + lda abs1+2 + php ;test stores do not alter flags + eor #$c3 + plp + sta zpt+2 + php ;flags after load/store sequence + eor #$c3 + cmp zp1+2 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+2 ;test flags + trap_ne + set_stat 0 + lda abs1+3 + php ;test stores do not alter flags + eor #$c3 + plp + sta zpt+3 + php ;flags after load/store sequence + eor #$c3 + cmp zp1+3 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+3 ;test flags + trap_ne + set_stat $ff + lda abs1 + php ;test stores do not alter flags + eor #$c3 + plp + sta zpt + php ;flags after load/store sequence + eor #$c3 + cmp zp1 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx ;test flags + trap_ne + set_stat $ff + lda abs1+1 + php ;test stores do not alter flags + eor #$c3 + plp + sta zpt+1 + php ;flags after load/store sequence + eor #$c3 + cmp zp1+1 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+1 ;test flags + trap_ne + set_stat $ff + lda abs1+2 + php ;test stores do not alter flags + eor #$c3 + plp + sta zpt+2 + php ;flags after load/store sequence + eor #$c3 + cmp zp1+2 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+2 ;test flags + trap_ne + set_stat $ff + lda abs1+3 + php ;test stores do not alter flags + eor #$c3 + plp + sta zpt+3 + php ;flags after load/store sequence + eor #$c3 + cmp zp1+3 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+3 ;test flags + trap_ne + set_stat 0 + lda #$c3 + php + cmp abs1 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx ;test flags + trap_ne + set_stat 0 + lda #$82 + php + cmp abs1+1 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+1 ;test flags + trap_ne + set_stat 0 + lda #$41 + php + cmp abs1+2 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+2 ;test flags + trap_ne + set_stat 0 + lda #0 + php + cmp abs1+3 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+3 ;test flags + trap_ne + + set_stat $ff + lda #$c3 + php + cmp abs1 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx ;test flags + trap_ne + set_stat $ff + lda #$82 + php + cmp abs1+1 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+1 ;test flags + trap_ne + set_stat $ff + lda #$41 + php + cmp abs1+2 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+2 ;test flags + trap_ne + set_stat $ff + lda #0 + php + cmp abs1+3 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+3 ;test flags + trap_ne + + ldx #0 + lda zpt + eor #$c3 + cmp zp1 + trap_ne ;store to zp data + stx zpt ;clear + lda abst + eor #$c3 + cmp abs1 + trap_ne ;store to abs data + stx abst ;clear + lda zpt+1 + eor #$c3 + cmp zp1+1 + trap_ne ;store to zp data + stx zpt+1 ;clear + lda abst+1 + eor #$c3 + cmp abs1+1 + trap_ne ;store to abs data + stx abst+1 ;clear + lda zpt+2 + eor #$c3 + cmp zp1+2 + trap_ne ;store to zp data + stx zpt+2 ;clear + lda abst+2 + eor #$c3 + cmp abs1+2 + trap_ne ;store to abs data + stx abst+2 ;clear + lda zpt+3 + eor #$c3 + cmp zp1+3 + trap_ne ;store to zp data + stx zpt+3 ;clear + lda abst+3 + eor #$c3 + cmp abs1+3 + trap_ne ;store to abs data + stx abst+3 ;clear + next_test + +; testing bit test & compares BIT CPX CPY CMP all addressing modes +; BIT - zp / abs + set_a $ff,0 + bit zp1+3 ;00 - should set Z / clear NV + tst_a $ff,fz + set_a 1,0 + bit zp1+2 ;41 - should set V (M6) / clear NZ + tst_a 1,fv + set_a 1,0 + bit zp1+1 ;82 - should set N (M7) & Z / clear V + tst_a 1,fnz + set_a 1,0 + bit zp1 ;c3 - should set N (M7) & V (M6) / clear Z + tst_a 1,fnv + + set_a $ff,$ff + bit zp1+3 ;00 - should set Z / clear NV + tst_a $ff,~fnv + set_a 1,$ff + bit zp1+2 ;41 - should set V (M6) / clear NZ + tst_a 1,~fnz + set_a 1,$ff + bit zp1+1 ;82 - should set N (M7) & Z / clear V + tst_a 1,~fv + set_a 1,$ff + bit zp1 ;c3 - should set N (M7) & V (M6) / clear Z + tst_a 1,~fz + + set_a $ff,0 + bit abs1+3 ;00 - should set Z / clear NV + tst_a $ff,fz + set_a 1,0 + bit abs1+2 ;41 - should set V (M6) / clear NZ + tst_a 1,fv + set_a 1,0 + bit abs1+1 ;82 - should set N (M7) & Z / clear V + tst_a 1,fnz + set_a 1,0 + bit abs1 ;c3 - should set N (M7) & V (M6) / clear Z + tst_a 1,fnv + + set_a $ff,$ff + bit abs1+3 ;00 - should set Z / clear NV + tst_a $ff,~fnv + set_a 1,$ff + bit abs1+2 ;41 - should set V (M6) / clear NZ + tst_a 1,~fnz + set_a 1,$ff + bit abs1+1 ;82 - should set N (M7) & Z / clear V + tst_a 1,~fv + set_a 1,$ff + bit abs1 ;c3 - should set N (M7) & V (M6) / clear Z + tst_a 1,~fz + next_test + +; CPX - zp / abs / # + set_x $80,0 + cpx zp7f + tst_stat fc + dex + cpx zp7f + tst_stat fzc + dex + cpx zp7f + tst_x $7e,fn + set_x $80,$ff + cpx zp7f + tst_stat ~fnz + dex + cpx zp7f + tst_stat ~fn + dex + cpx zp7f + tst_x $7e,~fzc + + set_x $80,0 + cpx abs7f + tst_stat fc + dex + cpx abs7f + tst_stat fzc + dex + cpx abs7f + tst_x $7e,fn + set_x $80,$ff + cpx abs7f + tst_stat ~fnz + dex + cpx abs7f + tst_stat ~fn + dex + cpx abs7f + tst_x $7e,~fzc + + set_x $80,0 + cpx #$7f + tst_stat fc + dex + cpx #$7f + tst_stat fzc + dex + cpx #$7f + tst_x $7e,fn + set_x $80,$ff + cpx #$7f + tst_stat ~fnz + dex + cpx #$7f + tst_stat ~fn + dex + cpx #$7f + tst_x $7e,~fzc + next_test + +; CPY - zp / abs / # + set_y $80,0 + cpy zp7f + tst_stat fc + dey + cpy zp7f + tst_stat fzc + dey + cpy zp7f + tst_y $7e,fn + set_y $80,$ff + cpy zp7f + tst_stat ~fnz + dey + cpy zp7f + tst_stat ~fn + dey + cpy zp7f + tst_y $7e,~fzc + + set_y $80,0 + cpy abs7f + tst_stat fc + dey + cpy abs7f + tst_stat fzc + dey + cpy abs7f + tst_y $7e,fn + set_y $80,$ff + cpy abs7f + tst_stat ~fnz + dey + cpy abs7f + tst_stat ~fn + dey + cpy abs7f + tst_y $7e,~fzc + + set_y $80,0 + cpy #$7f + tst_stat fc + dey + cpy #$7f + tst_stat fzc + dey + cpy #$7f + tst_y $7e,fn + set_y $80,$ff + cpy #$7f + tst_stat ~fnz + dey + cpy #$7f + tst_stat ~fn + dey + cpy #$7f + tst_y $7e,~fzc + next_test + +; CMP - zp / abs / # + set_a $80,0 + cmp zp7f + tst_a $80,fc + set_a $7f,0 + cmp zp7f + tst_a $7f,fzc + set_a $7e,0 + cmp zp7f + tst_a $7e,fn + set_a $80,$ff + cmp zp7f + tst_a $80,~fnz + set_a $7f,$ff + cmp zp7f + tst_a $7f,~fn + set_a $7e,$ff + cmp zp7f + tst_a $7e,~fzc + + set_a $80,0 + cmp abs7f + tst_a $80,fc + set_a $7f,0 + cmp abs7f + tst_a $7f,fzc + set_a $7e,0 + cmp abs7f + tst_a $7e,fn + set_a $80,$ff + cmp abs7f + tst_a $80,~fnz + set_a $7f,$ff + cmp abs7f + tst_a $7f,~fn + set_a $7e,$ff + cmp abs7f + tst_a $7e,~fzc + + set_a $80,0 + cmp #$7f + tst_a $80,fc + set_a $7f,0 + cmp #$7f + tst_a $7f,fzc + set_a $7e,0 + cmp #$7f + tst_a $7e,fn + set_a $80,$ff + cmp #$7f + tst_a $80,~fnz + set_a $7f,$ff + cmp #$7f + tst_a $7f,~fn + set_a $7e,$ff + cmp #$7f + tst_a $7e,~fzc + + ldx #4 ;with indexing by X + set_a $80,0 + cmp zp1,x + tst_a $80,fc + set_a $7f,0 + cmp zp1,x + tst_a $7f,fzc + set_a $7e,0 + cmp zp1,x + tst_a $7e,fn + set_a $80,$ff + cmp zp1,x + tst_a $80,~fnz + set_a $7f,$ff + cmp zp1,x + tst_a $7f,~fn + set_a $7e,$ff + cmp zp1,x + tst_a $7e,~fzc + + set_a $80,0 + cmp abs1,x + tst_a $80,fc + set_a $7f,0 + cmp abs1,x + tst_a $7f,fzc + set_a $7e,0 + cmp abs1,x + tst_a $7e,fn + set_a $80,$ff + cmp abs1,x + tst_a $80,~fnz + set_a $7f,$ff + cmp abs1,x + tst_a $7f,~fn + set_a $7e,$ff + cmp abs1,x + tst_a $7e,~fzc + + ldy #4 ;with indexing by Y + ldx #8 ;with indexed indirect + set_a $80,0 + cmp abs1,y + tst_a $80,fc + set_a $7f,0 + cmp abs1,y + tst_a $7f,fzc + set_a $7e,0 + cmp abs1,y + tst_a $7e,fn + set_a $80,$ff + cmp abs1,y + tst_a $80,~fnz + set_a $7f,$ff + cmp abs1,y + tst_a $7f,~fn + set_a $7e,$ff + cmp abs1,y + tst_a $7e,~fzc + + set_a $80,0 + cmp (ind1,x) + tst_a $80,fc + set_a $7f,0 + cmp (ind1,x) + tst_a $7f,fzc + set_a $7e,0 + cmp (ind1,x) + tst_a $7e,fn + set_a $80,$ff + cmp (ind1,x) + tst_a $80,~fnz + set_a $7f,$ff + cmp (ind1,x) + tst_a $7f,~fn + set_a $7e,$ff + cmp (ind1,x) + tst_a $7e,~fzc + + set_a $80,0 + cmp (ind1),y + tst_a $80,fc + set_a $7f,0 + cmp (ind1),y + tst_a $7f,fzc + set_a $7e,0 + cmp (ind1),y + tst_a $7e,fn + set_a $80,$ff + cmp (ind1),y + tst_a $80,~fnz + set_a $7f,$ff + cmp (ind1),y + tst_a $7f,~fn + set_a $7e,$ff + cmp (ind1),y + tst_a $7e,~fzc + next_test + +; testing shifts - ASL LSR ROL ROR all addressing modes +; shifts - accumulator + ldx #3 +tasl + set_ax zp1,0 + asl a + tst_ax rASL,fASL,0 + dex + bpl tasl + ldx #3 +tasl1 + set_ax zp1,$ff + asl a + tst_ax rASL,fASL,$ff-fnzc + dex + bpl tasl1 + + ldx #3 +tlsr + set_ax zp1,0 + lsr a + tst_ax rLSR,fLSR,0 + dex + bpl tlsr + ldx #3 +tlsr1 + set_ax zp1,$ff + lsr a + tst_ax rLSR,fLSR,$ff-fnzc + dex + bpl tlsr1 + + ldx #3 +trol + set_ax zp1,0 + rol a + tst_ax rROL,fROL,0 + dex + bpl trol + ldx #3 +trol1 + set_ax zp1,$ff-fc + rol a + tst_ax rROL,fROL,$ff-fnzc + dex + bpl trol1 + + ldx #3 +trolc + set_ax zp1,fc + rol a + tst_ax rROLc,fROLc,0 + dex + bpl trolc + ldx #3 +trolc1 + set_ax zp1,$ff + rol a + tst_ax rROLc,fROLc,$ff-fnzc + dex + bpl trolc1 + + ldx #3 +tror + set_ax zp1,0 + ror a + tst_ax rROR,fROR,0 + dex + bpl tror + ldx #3 +tror1 + set_ax zp1,$ff-fc + ror a + tst_ax rROR,fROR,$ff-fnzc + dex + bpl tror1 + + ldx #3 +trorc + set_ax zp1,fc + ror a + tst_ax rRORc,fRORc,0 + dex + bpl trorc + ldx #3 +trorc1 + set_ax zp1,$ff + ror a + tst_ax rRORc,fRORc,$ff-fnzc + dex + bpl trorc1 + next_test + +; shifts - zeropage + ldx #3 +tasl2 + set_z zp1,0 + asl zpt + tst_z rASL,fASL,0 + dex + bpl tasl2 + ldx #3 +tasl3 + set_z zp1,$ff + asl zpt + tst_z rASL,fASL,$ff-fnzc + dex + bpl tasl3 + + ldx #3 +tlsr2 + set_z zp1,0 + lsr zpt + tst_z rLSR,fLSR,0 + dex + bpl tlsr2 + ldx #3 +tlsr3 + set_z zp1,$ff + lsr zpt + tst_z rLSR,fLSR,$ff-fnzc + dex + bpl tlsr3 + + ldx #3 +trol2 + set_z zp1,0 + rol zpt + tst_z rROL,fROL,0 + dex + bpl trol2 + ldx #3 +trol3 + set_z zp1,$ff-fc + rol zpt + tst_z rROL,fROL,$ff-fnzc + dex + bpl trol3 + + ldx #3 +trolc2 + set_z zp1,fc + rol zpt + tst_z rROLc,fROLc,0 + dex + bpl trolc2 + ldx #3 +trolc3 + set_z zp1,$ff + rol zpt + tst_z rROLc,fROLc,$ff-fnzc + dex + bpl trolc3 + + ldx #3 +tror2 + set_z zp1,0 + ror zpt + tst_z rROR,fROR,0 + dex + bpl tror2 + ldx #3 +tror3 + set_z zp1,$ff-fc + ror zpt + tst_z rROR,fROR,$ff-fnzc + dex + bpl tror3 + + ldx #3 +trorc2 + set_z zp1,fc + ror zpt + tst_z rRORc,fRORc,0 + dex + bpl trorc2 + ldx #3 +trorc3 + set_z zp1,$ff + ror zpt + tst_z rRORc,fRORc,$ff-fnzc + dex + bpl trorc3 + next_test + +; shifts - absolute + ldx #3 +tasl4 + set_abs zp1,0 + asl abst + tst_abs rASL,fASL,0 + dex + bpl tasl4 + ldx #3 +tasl5 + set_abs zp1,$ff + asl abst + tst_abs rASL,fASL,$ff-fnzc + dex + bpl tasl5 + + ldx #3 +tlsr4 + set_abs zp1,0 + lsr abst + tst_abs rLSR,fLSR,0 + dex + bpl tlsr4 + ldx #3 +tlsr5 + set_abs zp1,$ff + lsr abst + tst_abs rLSR,fLSR,$ff-fnzc + dex + bpl tlsr5 + + ldx #3 +trol4 + set_abs zp1,0 + rol abst + tst_abs rROL,fROL,0 + dex + bpl trol4 + ldx #3 +trol5 + set_abs zp1,$ff-fc + rol abst + tst_abs rROL,fROL,$ff-fnzc + dex + bpl trol5 + + ldx #3 +trolc4 + set_abs zp1,fc + rol abst + tst_abs rROLc,fROLc,0 + dex + bpl trolc4 + ldx #3 +trolc5 + set_abs zp1,$ff + rol abst + tst_abs rROLc,fROLc,$ff-fnzc + dex + bpl trolc5 + + ldx #3 +tror4 + set_abs zp1,0 + ror abst + tst_abs rROR,fROR,0 + dex + bpl tror4 + ldx #3 +tror5 + set_abs zp1,$ff-fc + ror abst + tst_abs rROR,fROR,$ff-fnzc + dex + bpl tror5 + + ldx #3 +trorc4 + set_abs zp1,fc + ror abst + tst_abs rRORc,fRORc,0 + dex + bpl trorc4 + ldx #3 +trorc5 + set_abs zp1,$ff + ror abst + tst_abs rRORc,fRORc,$ff-fnzc + dex + bpl trorc5 + next_test + +; shifts - zp indexed + ldx #3 +tasl6 + set_zx zp1,0 + asl zpt,x + tst_zx rASL,fASL,0 + dex + bpl tasl6 + ldx #3 +tasl7 + set_zx zp1,$ff + asl zpt,x + tst_zx rASL,fASL,$ff-fnzc + dex + bpl tasl7 + + ldx #3 +tlsr6 + set_zx zp1,0 + lsr zpt,x + tst_zx rLSR,fLSR,0 + dex + bpl tlsr6 + ldx #3 +tlsr7 + set_zx zp1,$ff + lsr zpt,x + tst_zx rLSR,fLSR,$ff-fnzc + dex + bpl tlsr7 + + ldx #3 +trol6 + set_zx zp1,0 + rol zpt,x + tst_zx rROL,fROL,0 + dex + bpl trol6 + ldx #3 +trol7 + set_zx zp1,$ff-fc + rol zpt,x + tst_zx rROL,fROL,$ff-fnzc + dex + bpl trol7 + + ldx #3 +trolc6 + set_zx zp1,fc + rol zpt,x + tst_zx rROLc,fROLc,0 + dex + bpl trolc6 + ldx #3 +trolc7 + set_zx zp1,$ff + rol zpt,x + tst_zx rROLc,fROLc,$ff-fnzc + dex + bpl trolc7 + + ldx #3 +tror6 + set_zx zp1,0 + ror zpt,x + tst_zx rROR,fROR,0 + dex + bpl tror6 + ldx #3 +tror7 + set_zx zp1,$ff-fc + ror zpt,x + tst_zx rROR,fROR,$ff-fnzc + dex + bpl tror7 + + ldx #3 +trorc6 + set_zx zp1,fc + ror zpt,x + tst_zx rRORc,fRORc,0 + dex + bpl trorc6 + ldx #3 +trorc7 + set_zx zp1,$ff + ror zpt,x + tst_zx rRORc,fRORc,$ff-fnzc + dex + bpl trorc7 + next_test + +; shifts - abs indexed + ldx #3 +tasl8 + set_absx zp1,0 + asl abst,x + tst_absx rASL,fASL,0 + dex + bpl tasl8 + ldx #3 +tasl9 + set_absx zp1,$ff + asl abst,x + tst_absx rASL,fASL,$ff-fnzc + dex + bpl tasl9 + + ldx #3 +tlsr8 + set_absx zp1,0 + lsr abst,x + tst_absx rLSR,fLSR,0 + dex + bpl tlsr8 + ldx #3 +tlsr9 + set_absx zp1,$ff + lsr abst,x + tst_absx rLSR,fLSR,$ff-fnzc + dex + bpl tlsr9 + + ldx #3 +trol8 + set_absx zp1,0 + rol abst,x + tst_absx rROL,fROL,0 + dex + bpl trol8 + ldx #3 +trol9 + set_absx zp1,$ff-fc + rol abst,x + tst_absx rROL,fROL,$ff-fnzc + dex + bpl trol9 + + ldx #3 +trolc8 + set_absx zp1,fc + rol abst,x + tst_absx rROLc,fROLc,0 + dex + bpl trolc8 + ldx #3 +trolc9 + set_absx zp1,$ff + rol abst,x + tst_absx rROLc,fROLc,$ff-fnzc + dex + bpl trolc9 + + ldx #3 +tror8 + set_absx zp1,0 + ror abst,x + tst_absx rROR,fROR,0 + dex + bpl tror8 + ldx #3 +tror9 + set_absx zp1,$ff-fc + ror abst,x + tst_absx rROR,fROR,$ff-fnzc + dex + bpl tror9 + + ldx #3 +trorc8 + set_absx zp1,fc + ror abst,x + tst_absx rRORc,fRORc,0 + dex + bpl trorc8 + ldx #3 +trorc9 + set_absx zp1,$ff + ror abst,x + tst_absx rRORc,fRORc,$ff-fnzc + dex + bpl trorc9 + next_test + +; testing memory increment/decrement - INC DEC all addressing modes +; zeropage + ldx #0 + lda #$7e + sta zpt +tinc + set_stat 0 + inc zpt + tst_z rINC,fINC,0 + inx + cpx #2 + bne tinc1 + lda #$fe + sta zpt +tinc1 cpx #5 + bne tinc + dex + inc zpt +tdec + set_stat 0 + dec zpt + tst_z rINC,fINC,0 + dex + bmi tdec1 + cpx #1 + bne tdec + lda #$81 + sta zpt + bne tdec +tdec1 + ldx #0 + lda #$7e + sta zpt +tinc10 + set_stat $ff + inc zpt + tst_z rINC,fINC,$ff-fnz + inx + cpx #2 + bne tinc11 + lda #$fe + sta zpt +tinc11 cpx #5 + bne tinc10 + dex + inc zpt +tdec10 + set_stat $ff + dec zpt + tst_z rINC,fINC,$ff-fnz + dex + bmi tdec11 + cpx #1 + bne tdec10 + lda #$81 + sta zpt + bne tdec10 +tdec11 + next_test + +; absolute memory + ldx #0 + lda #$7e + sta abst +tinc2 + set_stat 0 + inc abst + tst_abs rINC,fINC,0 + inx + cpx #2 + bne tinc3 + lda #$fe + sta abst +tinc3 cpx #5 + bne tinc2 + dex + inc abst +tdec2 + set_stat 0 + dec abst + tst_abs rINC,fINC,0 + dex + bmi tdec3 + cpx #1 + bne tdec2 + lda #$81 + sta abst + bne tdec2 +tdec3 + ldx #0 + lda #$7e + sta abst +tinc12 + set_stat $ff + inc abst + tst_abs rINC,fINC,$ff-fnz + inx + cpx #2 + bne tinc13 + lda #$fe + sta abst +tinc13 cpx #5 + bne tinc12 + dex + inc abst +tdec12 + set_stat $ff + dec abst + tst_abs rINC,fINC,$ff-fnz + dex + bmi tdec13 + cpx #1 + bne tdec12 + lda #$81 + sta abst + bne tdec12 +tdec13 + next_test + +; zeropage indexed + ldx #0 + lda #$7e +tinc4 sta zpt,x + set_stat 0 + inc zpt,x + tst_zx rINC,fINC,0 + lda zpt,x + inx + cpx #2 + bne tinc5 + lda #$fe +tinc5 cpx #5 + bne tinc4 + dex + lda #2 +tdec4 sta zpt,x + set_stat 0 + dec zpt,x + tst_zx rINC,fINC,0 + lda zpt,x + dex + bmi tdec5 + cpx #1 + bne tdec4 + lda #$81 + bne tdec4 +tdec5 + ldx #0 + lda #$7e +tinc14 sta zpt,x + set_stat $ff + inc zpt,x + tst_zx rINC,fINC,$ff-fnz + lda zpt,x + inx + cpx #2 + bne tinc15 + lda #$fe +tinc15 cpx #5 + bne tinc14 + dex + lda #2 +tdec14 sta zpt,x + set_stat $ff + dec zpt,x + tst_zx rINC,fINC,$ff-fnz + lda zpt,x + dex + bmi tdec15 + cpx #1 + bne tdec14 + lda #$81 + bne tdec14 +tdec15 + next_test + +; memory indexed + ldx #0 + lda #$7e +tinc6 sta abst,x + set_stat 0 + inc abst,x + tst_absx rINC,fINC,0 + lda abst,x + inx + cpx #2 + bne tinc7 + lda #$fe +tinc7 cpx #5 + bne tinc6 + dex + lda #2 +tdec6 sta abst,x + set_stat 0 + dec abst,x + tst_absx rINC,fINC,0 + lda abst,x + dex + bmi tdec7 + cpx #1 + bne tdec6 + lda #$81 + bne tdec6 +tdec7 + ldx #0 + lda #$7e +tinc16 sta abst,x + set_stat $ff + inc abst,x + tst_absx rINC,fINC,$ff-fnz + lda abst,x + inx + cpx #2 + bne tinc17 + lda #$fe +tinc17 cpx #5 + bne tinc16 + dex + lda #2 +tdec16 sta abst,x + set_stat $ff + dec abst,x + tst_absx rINC,fINC,$ff-fnz + lda abst,x + dex + bmi tdec17 + cpx #1 + bne tdec16 + lda #$81 + bne tdec16 +tdec17 + next_test + +; testing logical instructions - AND EOR ORA all addressing modes +; AND + ldx #3 ;immediate +tand lda zpAN,x + sta ex_andi+1 ;set AND # operand + set_ax absANa,0 + jsr ex_andi ;execute AND # in RAM + tst_ax absrlo,absflo,0 + dex + bpl tand + ldx #3 +tand1 lda zpAN,x + sta ex_andi+1 ;set AND # operand + set_ax absANa,$ff + jsr ex_andi ;execute AND # in RAM + tst_ax absrlo,absflo,$ff-fnz + dex + bpl tand1 + + ldx #3 ;zp +tand2 lda zpAN,x + sta zpt + set_ax absANa,0 + and zpt + tst_ax absrlo,absflo,0 + dex + bpl tand2 + ldx #3 +tand3 lda zpAN,x + sta zpt + set_ax absANa,$ff + and zpt + tst_ax absrlo,absflo,$ff-fnz + dex + bpl tand3 + + ldx #3 ;abs +tand4 lda zpAN,x + sta abst + set_ax absANa,0 + and abst + tst_ax absrlo,absflo,0 + dex + bpl tand4 + ldx #3 +tand5 lda zpAN,x + sta abst + set_ax absANa,$ff + and abst + tst_ax absrlo,absflo,$ff-fnz + dex + bpl tand6 + + ldx #3 ;zp,x +tand6 + set_ax absANa,0 + and zpAN,x + tst_ax absrlo,absflo,0 + dex + bpl tand6 + ldx #3 +tand7 + set_ax absANa,$ff + and zpAN,x + tst_ax absrlo,absflo,$ff-fnz + dex + bpl tand7 + + ldx #3 ;abs,x +tand8 + set_ax absANa,0 + and absAN,x + tst_ax absrlo,absflo,0 + dex + bpl tand8 + ldx #3 +tand9 + set_ax absANa,$ff + and absAN,x + tst_ax absrlo,absflo,$ff-fnz + dex + bpl tand9 + + ldy #3 ;abs,y +tand10 + set_ay absANa,0 + and absAN,y + tst_ay absrlo,absflo,0 + dey + bpl tand10 + ldy #3 +tand11 + set_ay absANa,$ff + and absAN,y + tst_ay absrlo,absflo,$ff-fnz + dey + bpl tand11 + + ldx #6 ;(zp,x) + ldy #3 +tand12 + set_ay absANa,0 + and (indAN,x) + tst_ay absrlo,absflo,0 + dex + dex + dey + bpl tand12 + ldx #6 + ldy #3 +tand13 + set_ay absANa,$ff + and (indAN,x) + tst_ay absrlo,absflo,$ff-fnz + dex + dex + dey + bpl tand13 + + ldy #3 ;(zp),y +tand14 + set_ay absANa,0 + and (indAN),y + tst_ay absrlo,absflo,0 + dey + bpl tand14 + ldy #3 +tand15 + set_ay absANa,$ff + and (indAN),y + tst_ay absrlo,absflo,$ff-fnz + dey + bpl tand15 + next_test + +; EOR + ldx #3 ;immediate - self modifying code +teor lda zpEO,x + sta ex_eori+1 ;set EOR # operand + set_ax absEOa,0 + jsr ex_eori ;execute EOR # in RAM + tst_ax absrlo,absflo,0 + dex + bpl teor + ldx #3 +teor1 lda zpEO,x + sta ex_eori+1 ;set EOR # operand + set_ax absEOa,$ff + jsr ex_eori ;execute EOR # in RAM + tst_ax absrlo,absflo,$ff-fnz + dex + bpl teor1 + + ldx #3 ;zp +teor2 lda zpEO,x + sta zpt + set_ax absEOa,0 + eor zpt + tst_ax absrlo,absflo,0 + dex + bpl teor2 + ldx #3 +teor3 lda zpEO,x + sta zpt + set_ax absEOa,$ff + eor zpt + tst_ax absrlo,absflo,$ff-fnz + dex + bpl teor3 + + ldx #3 ;abs +teor4 lda zpEO,x + sta abst + set_ax absEOa,0 + eor abst + tst_ax absrlo,absflo,0 + dex + bpl teor4 + ldx #3 +teor5 lda zpEO,x + sta abst + set_ax absEOa,$ff + eor abst + tst_ax absrlo,absflo,$ff-fnz + dex + bpl teor6 + + ldx #3 ;zp,x +teor6 + set_ax absEOa,0 + eor zpEO,x + tst_ax absrlo,absflo,0 + dex + bpl teor6 + ldx #3 +teor7 + set_ax absEOa,$ff + eor zpEO,x + tst_ax absrlo,absflo,$ff-fnz + dex + bpl teor7 + + ldx #3 ;abs,x +teor8 + set_ax absEOa,0 + eor absEO,x + tst_ax absrlo,absflo,0 + dex + bpl teor8 + ldx #3 +teor9 + set_ax absEOa,$ff + eor absEO,x + tst_ax absrlo,absflo,$ff-fnz + dex + bpl teor9 + + ldy #3 ;abs,y +teor10 + set_ay absEOa,0 + eor absEO,y + tst_ay absrlo,absflo,0 + dey + bpl teor10 + ldy #3 +teor11 + set_ay absEOa,$ff + eor absEO,y + tst_ay absrlo,absflo,$ff-fnz + dey + bpl teor11 + + ldx #6 ;(zp,x) + ldy #3 +teor12 + set_ay absEOa,0 + eor (indEO,x) + tst_ay absrlo,absflo,0 + dex + dex + dey + bpl teor12 + ldx #6 + ldy #3 +teor13 + set_ay absEOa,$ff + eor (indEO,x) + tst_ay absrlo,absflo,$ff-fnz + dex + dex + dey + bpl teor13 + + ldy #3 ;(zp),y +teor14 + set_ay absEOa,0 + eor (indEO),y + tst_ay absrlo,absflo,0 + dey + bpl teor14 + ldy #3 +teor15 + set_ay absEOa,$ff + eor (indEO),y + tst_ay absrlo,absflo,$ff-fnz + dey + bpl teor15 + next_test + +; OR + ldx #3 ;immediate - self modifying code +tora lda zpOR,x + sta ex_orai+1 ;set ORA # operand + set_ax absORa,0 + jsr ex_orai ;execute ORA # in RAM + tst_ax absrlo,absflo,0 + dex + bpl tora + ldx #3 +tora1 lda zpOR,x + sta ex_orai+1 ;set ORA # operand + set_ax absORa,$ff + jsr ex_orai ;execute ORA # in RAM + tst_ax absrlo,absflo,$ff-fnz + dex + bpl tora1 + + ldx #3 ;zp +tora2 lda zpOR,x + sta zpt + set_ax absORa,0 + ora zpt + tst_ax absrlo,absflo,0 + dex + bpl tora2 + ldx #3 +tora3 lda zpOR,x + sta zpt + set_ax absORa,$ff + ora zpt + tst_ax absrlo,absflo,$ff-fnz + dex + bpl tora3 + + ldx #3 ;abs +tora4 lda zpOR,x + sta abst + set_ax absORa,0 + ora abst + tst_ax absrlo,absflo,0 + dex + bpl tora4 + ldx #3 +tora5 lda zpOR,x + sta abst + set_ax absORa,$ff + ora abst + tst_ax absrlo,absflo,$ff-fnz + dex + bpl tora6 + + ldx #3 ;zp,x +tora6 + set_ax absORa,0 + ora zpOR,x + tst_ax absrlo,absflo,0 + dex + bpl tora6 + ldx #3 +tora7 + set_ax absORa,$ff + ora zpOR,x + tst_ax absrlo,absflo,$ff-fnz + dex + bpl tora7 + + ldx #3 ;abs,x +tora8 + set_ax absORa,0 + ora absOR,x + tst_ax absrlo,absflo,0 + dex + bpl tora8 + ldx #3 +tora9 + set_ax absORa,$ff + ora absOR,x + tst_ax absrlo,absflo,$ff-fnz + dex + bpl tora9 + + ldy #3 ;abs,y +tora10 + set_ay absORa,0 + ora absOR,y + tst_ay absrlo,absflo,0 + dey + bpl tora10 + ldy #3 +tora11 + set_ay absORa,$ff + ora absOR,y + tst_ay absrlo,absflo,$ff-fnz + dey + bpl tora11 + + ldx #6 ;(zp,x) + ldy #3 +tora12 + set_ay absORa,0 + ora (indOR,x) + tst_ay absrlo,absflo,0 + dex + dex + dey + bpl tora12 + ldx #6 + ldy #3 +tora13 + set_ay absORa,$ff + ora (indOR,x) + tst_ay absrlo,absflo,$ff-fnz + dex + dex + dey + bpl tora13 + + ldy #3 ;(zp),y +tora14 + set_ay absORa,0 + ora (indOR),y + tst_ay absrlo,absflo,0 + dey + bpl tora14 + ldy #3 +tora15 + set_ay absORa,$ff + ora (indOR),y + tst_ay absrlo,absflo,$ff-fnz + dey + bpl tora15 + if I_flag = 3 + cli + endif + next_test + +; full binary add/subtract test +; iterates through all combinations of operands and carry input +; uses increments/decrements to predict result & result flags + cld + ldx #ad2 ;for indexed test + ldy #$ff ;max range + lda #0 ;start with adding zeroes & no carry + sta adfc ;carry in - for diag + sta ad1 ;operand 1 - accumulator + sta ad2 ;operand 2 - memory or immediate + sta ada2 ;non zp + sta adrl ;expected result bits 0-7 + sta adrh ;expected result bit 8 (carry out) + lda #$ff ;complemented operand 2 for subtract + sta sb2 + sta sba2 ;non zp + lda #2 ;expected Z-flag + sta adrf +tadd clc ;test with carry clear + jsr chkadd + inc adfc ;now with carry + inc adrl ;result +1 + php ;save N & Z from low result + php + pla ;accu holds expected flags + and #$82 ;mask N & Z + plp + bne tadd1 + inc adrh ;result bit 8 - carry +tadd1 ora adrh ;merge C to expected flags + sta adrf ;save expected flags except overflow + sec ;test with carry set + jsr chkadd + dec adfc ;same for operand +1 but no carry + inc ad1 + bne tadd ;iterate op1 + lda #0 ;preset result to op2 when op1 = 0 + sta adrh + inc ada2 + inc ad2 + php ;save NZ as operand 2 becomes the new result + pla + and #$82 ;mask N00000Z0 + sta adrf ;no need to check carry as we are adding to 0 + dec sb2 ;complement subtract operand 2 + dec sba2 + lda ad2 + sta adrl + bne tadd ;iterate op2 + if disable_decimal < 1 + next_test + +; decimal add/subtract test +; *** WARNING - tests documented behavior only! *** +; only valid BCD operands are tested, N V Z flags are ignored +; iterates through all valid combinations of operands and carry input +; uses increments/decrements to predict result & carry flag + sed + ldx #ad2 ;for indexed test + ldy #$ff ;max range + lda #$99 ;start with adding 99 to 99 with carry + sta ad1 ;operand 1 - accumulator + sta ad2 ;operand 2 - memory or immediate + sta ada2 ;non zp + sta adrl ;expected result bits 0-7 + lda #1 ;set carry in & out + sta adfc ;carry in - for diag + sta adrh ;expected result bit 8 (carry out) + lda #0 ;complemented operand 2 for subtract + sta sb2 + sta sba2 ;non zp +tdad sec ;test with carry set + jsr chkdad + dec adfc ;now with carry clear + lda adrl ;decimal adjust result + bne tdad1 ;skip clear carry & preset result 99 (9A-1) + dec adrh + lda #$99 + sta adrl + bne tdad3 +tdad1 and #$f ;lower nibble mask + bne tdad2 ;no decimal adjust needed + dec adrl ;decimal adjust (?0-6) + dec adrl + dec adrl + dec adrl + dec adrl + dec adrl +tdad2 dec adrl ;result -1 +tdad3 clc ;test with carry clear + jsr chkdad + inc adfc ;same for operand -1 but with carry + lda ad1 ;decimal adjust operand 1 + beq tdad5 ;iterate operand 2 + and #$f ;lower nibble mask + bne tdad4 ;skip decimal adjust + dec ad1 ;decimal adjust (?0-6) + dec ad1 + dec ad1 + dec ad1 + dec ad1 + dec ad1 +tdad4 dec ad1 ;operand 1 -1 + jmp tdad ;iterate op1 + +tdad5 lda #$99 ;precharge op1 max + sta ad1 + lda ad2 ;decimal adjust operand 2 + beq tdad7 ;end of iteration + and #$f ;lower nibble mask + bne tdad6 ;skip decimal adjust + dec ad2 ;decimal adjust (?0-6) + dec ad2 + dec ad2 + dec ad2 + dec ad2 + dec ad2 + inc sb2 ;complemented decimal adjust for subtract (?9+6) + inc sb2 + inc sb2 + inc sb2 + inc sb2 + inc sb2 +tdad6 dec ad2 ;operand 2 -1 + inc sb2 ;complemented operand for subtract + lda sb2 + sta sba2 ;copy as non zp operand + lda ad2 + sta ada2 ;copy as non zp operand + sta adrl ;new result since op1+carry=00+carry +op2=op2 + inc adrh ;result carry + bne tdad ;iterate op2 +tdad7 + next_test + +; decimal/binary switch test +; tests CLD, SED, PLP, RTI to properly switch between decimal & binary opcode +; tables + clc + cld + php + lda #$55 + adc #$55 + cmp #$aa + trap_ne ;expected binary result after cld + clc + sed + php + lda #$55 + adc #$55 + cmp #$10 + trap_ne ;expected decimal result after sed + cld + plp + lda #$55 + adc #$55 + cmp #$10 + trap_ne ;expected decimal result after plp D=1 + plp + lda #$55 + adc #$55 + cmp #$aa + trap_ne ;expected binary result after plp D=0 + clc + lda #hi bin_rti_ret ;emulated interrupt for rti + pha + lda #lo bin_rti_ret + pha + php + sed + lda #hi dec_rti_ret ;emulated interrupt for rti + pha + lda #lo dec_rti_ret + pha + php + cld + rti +dec_rti_ret + lda #$55 + adc #$55 + cmp #$10 + trap_ne ;expected decimal result after rti D=1 + rti +bin_rti_ret + lda #$55 + adc #$55 + cmp #$aa + trap_ne ;expected binary result after rti D=0 + endif + + lda test_case + cmp #test_num + trap_ne ;previous test is out of sequence + lda #$f0 ;mark opcode testing complete + sta test_case + +; final RAM integrity test +; verifies that none of the previous tests has altered RAM outside of the +; designated write areas. + check_ram +; *** DEBUG INFO *** +; to debug checksum errors uncomment check_ram in the next_test macro to +; narrow down the responsible opcode. +; may give false errors when monitor, OS or other background activity is +; allowed during previous tests. + + +; S U C C E S S ************************************************ +; ------------- + success ;if you get here everything went well +; ------------- +; S U C C E S S ************************************************ + jmp start ;run again + + if disable_decimal < 1 +; core subroutine of the decimal add/subtract test +; *** WARNING - tests documented behavior only! *** +; only valid BCD operands are tested, N V Z flags are ignored +; iterates through all valid combinations of operands and carry input +; uses increments/decrements to predict result & carry flag +chkdad +; decimal ADC / SBC zp + php ;save carry for subtract + lda ad1 + adc ad2 ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp + php ;save carry for next add + lda ad1 + sbc sb2 ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad flags + plp +; decimal ADC / SBC abs + php ;save carry for subtract + lda ad1 + adc ada2 ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp + php ;save carry for next add + lda ad1 + sbc sba2 ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp +; decimal ADC / SBC # + php ;save carry for subtract + lda ad2 + sta ex_adci+1 ;set ADC # operand + lda ad1 + jsr ex_adci ;execute ADC # in RAM + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp + php ;save carry for next add + lda sb2 + sta ex_sbci+1 ;set SBC # operand + lda ad1 + jsr ex_sbci ;execute SBC # in RAM + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp +; decimal ADC / SBC zp,x + php ;save carry for subtract + lda ad1 + adc 0,x ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp + php ;save carry for next add + lda ad1 + sbc sb2-ad2,x ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp +; decimal ADC / SBC abs,x + php ;save carry for subtract + lda ad1 + adc ada2-ad2,x ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp + php ;save carry for next add + lda ad1 + sbc sba2-ad2,x ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp +; decimal ADC / SBC abs,y + php ;save carry for subtract + lda ad1 + adc ada2-$ff,y ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp + php ;save carry for next add + lda ad1 + sbc sba2-$ff,y ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp +; decimal ADC / SBC (zp,x) + php ;save carry for subtract + lda ad1 + adc (lo adi2-ad2,x) ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp + php ;save carry for next add + lda ad1 + sbc (lo sbi2-ad2,x) ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp +; decimal ADC / SBC (abs),y + php ;save carry for subtract + lda ad1 + adc (adiy2),y ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp + php ;save carry for next add + lda ad1 + sbc (sbiy2),y ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #1 ;mask carry + cmp adrh + trap_ne ;bad carry + plp + rts + endif + +; core subroutine of the full binary add/subtract test +; iterates through all combinations of operands and carry input +; uses increments/decrements to predict result & result flags +chkadd lda adrf ;add V-flag if overflow + and #$83 ;keep N-----ZC / clear V + pha + lda ad1 ;test sign unequal between operands + eor ad2 + bmi ckad1 ;no overflow possible - operands have different sign + lda ad1 ;test sign equal between operands and result + eor adrl + bpl ckad1 ;no overflow occured - operand and result have same sign + pla + ora #$40 ;set V + pha +ckad1 pla + sta adrf ;save expected flags +; binary ADC / SBC zp + php ;save carry for subtract + lda ad1 + adc ad2 ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda ad1 + sbc sb2 ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp +; binary ADC / SBC abs + php ;save carry for subtract + lda ad1 + adc ada2 ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda ad1 + sbc sba2 ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp +; binary ADC / SBC # + php ;save carry for subtract + lda ad2 + sta ex_adci+1 ;set ADC # operand + lda ad1 + jsr ex_adci ;execute ADC # in RAM + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda sb2 + sta ex_sbci+1 ;set SBC # operand + lda ad1 + jsr ex_sbci ;execute SBC # in RAM + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp +; binary ADC / SBC zp,x + php ;save carry for subtract + lda ad1 + adc 0,x ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda ad1 + sbc sb2-ad2,x ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp +; binary ADC / SBC abs,x + php ;save carry for subtract + lda ad1 + adc ada2-ad2,x ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda ad1 + sbc sba2-ad2,x ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp +; binary ADC / SBC abs,y + php ;save carry for subtract + lda ad1 + adc ada2-$ff,y ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda ad1 + sbc sba2-$ff,y ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp +; binary ADC / SBC (zp,x) + php ;save carry for subtract + lda ad1 + adc (lo adi2-ad2,x) ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda ad1 + sbc (lo sbi2-ad2,x) ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp +; binary ADC / SBC (abs),y + php ;save carry for subtract + lda ad1 + adc (adiy2),y ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda ad1 + sbc (sbiy2),y ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp + rts + +; target for the jump absolute test + dey + dey +test_far + php ;either SP or Y count will fail, if we do not hit + dey + dey + dey + plp + trap_cs ;flags loaded? + trap_vs + trap_mi + trap_eq + cmp #'F' ;registers loaded? + trap_ne + cpx #'A' + trap_ne + cpy #('R'-3) + trap_ne + pha ;save a,x + txa + pha + tsx + cpx #$fd ;check SP + trap_ne + pla ;restore x + tax + set_stat $ff + pla ;restore a + inx ;return registers with modifications + eor #$aa ;N=1, V=1, Z=0, C=1 + jmp far_ret + +; target for the jump indirect test + align +ptr_tst_ind dw test_ind +ptr_ind_ret dw ind_ret + trap ;runover protection + dey + dey +test_ind + php ;either SP or Y count will fail, if we do not hit + dey + dey + dey + plp + trap_cs ;flags loaded? + trap_vs + trap_mi + trap_eq + cmp #'I' ;registers loaded? + trap_ne + cpx #'N' + trap_ne + cpy #('D'-3) + trap_ne + pha ;save a,x + txa + pha + tsx + cpx #$fd ;check SP + trap_ne + pla ;restore x + tax + set_stat $ff + pla ;restore a + inx ;return registers with modifications + eor #$aa ;N=1, V=1, Z=0, C=1 + jmp (ptr_ind_ret) + trap ;runover protection + +; target for the jump subroutine test + dey + dey +test_jsr + php ;either SP or Y count will fail, if we do not hit + dey + dey + dey + plp + trap_cs ;flags loaded? + trap_vs + trap_mi + trap_eq + cmp #'J' ;registers loaded? + trap_ne + cpx #'S' + trap_ne + cpy #('R'-3) + trap_ne + pha ;save a,x + txa + pha + tsx ;sp -4? (return addr,a,x) + cpx #$fb + trap_ne + lda $1ff ;propper return on stack + cmp #hi(jsr_ret) + trap_ne + lda $1fe + cmp #lo(jsr_ret) + trap_ne + set_stat $ff + pla ;pull x,a + tax + pla + inx ;return registers with modifications + eor #$aa ;N=1, V=1, Z=0, C=1 + rts + trap ;runover protection + +;trap in case of unexpected IRQ, NMI, BRK, RESET - BRK test target +nmi_trap + trap ;check stack for conditions at NMI +res_trap + trap ;unexpected RESET + + dey + dey +irq_trap ;BRK test or unextpected BRK or IRQ + php ;either SP or Y count will fail, if we do not hit + dey + dey + dey + ;next 4 traps could be caused by unexpected BRK or IRQ + ;check stack for BREAK and originating location + ;possible jump/branch into weeds (uninitialized space) + cmp #'B' ;registers loaded? + trap_ne + cpx #'R' + trap_ne + cpy #('K'-3) + trap_ne + sta irq_a ;save registers during break test + stx irq_x + tsx ;test break on stack + lda $102,x + cmp_flag 0 ;break test should have B=1 + trap_ne ; - no break flag on stack + pla + cmp #fai ;should have added interrupt disable + trap_ne + tsx + cpx #$fc ;sp -3? (return addr, flags) + trap_ne + lda $1ff ;propper return on stack + cmp #hi(brk_ret) + trap_ne + lda $1fe + cmp #lo(brk_ret) + trap_ne + set_stat $ff + ldx irq_x + inx ;return registers with modifications + lda irq_a + eor #$aa ;N=1, V=1, Z=0, C=1 but original flags should be restored + rti + trap ;runover protection + + if report = 1 + include "report.i65" + endif + +;copy of data to initialize BSS segment + if load_data_direct != 1 +zp_init +zp1_ db $c3,$82,$41,0 ;test patterns for LDx BIT ROL ROR ASL LSR +zp7f_ db $7f ;test pattern for compare +;logical zeropage operands +zpOR_ db 0,$1f,$71,$80 ;test pattern for OR +zpAN_ db $0f,$ff,$7f,$80 ;test pattern for AND +zpEO_ db $ff,$0f,$8f,$8f ;test pattern for EOR +;indirect addressing pointers +ind1_ dw abs1 ;indirect pointer to pattern in absolute memory + dw abs1+1 + dw abs1+2 + dw abs1+3 + dw abs7f +inw1_ dw abs1-$f8 ;indirect pointer for wrap-test pattern +indt_ dw abst ;indirect pointer to store area in absolute memory + dw abst+1 + dw abst+2 + dw abst+3 +inwt_ dw abst-$f8 ;indirect pointer for wrap-test store +indAN_ dw absAN ;indirect pointer to AND pattern in absolute memory + dw absAN+1 + dw absAN+2 + dw absAN+3 +indEO_ dw absEO ;indirect pointer to EOR pattern in absolute memory + dw absEO+1 + dw absEO+2 + dw absEO+3 +indOR_ dw absOR ;indirect pointer to OR pattern in absolute memory + dw absOR+1 + dw absOR+2 + dw absOR+3 +;add/subtract indirect pointers +adi2_ dw ada2 ;indirect pointer to operand 2 in absolute memory +sbi2_ dw sba2 ;indirect pointer to complemented operand 2 (SBC) +adiy2_ dw ada2-$ff ;with offset for indirect indexed +sbiy2_ dw sba2-$ff +zp_end + if (zp_end - zp_init) != (zp_bss_end - zp_bss) + ;force assembler error if size is different + ERROR ERROR ERROR ;mismatch between bss and zeropage data + endif +data_init +ex_and_ and #0 ;execute immediate opcodes + rts +ex_eor_ eor #0 ;execute immediate opcodes + rts +ex_ora_ ora #0 ;execute immediate opcodes + rts +ex_adc_ adc #0 ;execute immediate opcodes + rts +ex_sbc_ sbc #0 ;execute immediate opcodes + rts +abs1_ db $c3,$82,$41,0 ;test patterns for LDx BIT ROL ROR ASL LSR +abs7f_ db $7f ;test pattern for compare +;loads +fLDx_ db fn,fn,0,fz ;expected flags for load +;shifts +rASL_ ;expected result ASL & ROL -carry +rROL_ db $86,$04,$82,0 ; " +rROLc_ db $87,$05,$83,1 ;expected result ROL +carry +rLSR_ ;expected result LSR & ROR -carry +rROR_ db $61,$41,$20,0 ; " +rRORc_ db $e1,$c1,$a0,$80 ;expected result ROR +carry +fASL_ ;expected flags for shifts +fROL_ db fnc,fc,fn,fz ;no carry in +fROLc_ db fnc,fc,fn,0 ;carry in +fLSR_ +fROR_ db fc,0,fc,fz ;no carry in +fRORc_ db fnc,fn,fnc,fn ;carry in +;increments (decrements) +rINC_ db $7f,$80,$ff,0,1 ;expected result for INC/DEC +fINC_ db 0,fn,fn,fz,0 ;expected flags for INC/DEC +;logical memory operand +absOR_ db 0,$1f,$71,$80 ;test pattern for OR +absAN_ db $0f,$ff,$7f,$80 ;test pattern for AND +absEO_ db $ff,$0f,$8f,$8f ;test pattern for EOR +;logical accu operand +absORa_ db 0,$f1,$1f,0 ;test pattern for OR +absANa_ db $f0,$ff,$ff,$ff ;test pattern for AND +absEOa_ db $ff,$f0,$f0,$0f ;test pattern for EOR +;logical results +absrlo_ db 0,$ff,$7f,$80 +absflo_ db fz,fn,0,fn +data_end + if (data_end - data_init) != (data_bss_end - data_bss) + ;force assembler error if size is different + ERROR ERROR ERROR ;mismatch between bss and data + endif + +vec_init + dw nmi_trap + dw res_trap + dw irq_trap +vec_bss equ $fffa + endif ;end of RAM init data + + if (load_data_direct = 1) & (ROM_vectors = 1) + org $fffa ;vectors + dw nmi_trap + dw res_trap + dw irq_trap + endif + + end start + diff --git a/M6502/test_M6502/roms/6502_interrupt_test.a65 b/M6502/test_M6502/roms/6502_interrupt_test.a65 new file mode 100644 index 0000000..bfc3754 --- /dev/null +++ b/M6502/test_M6502/roms/6502_interrupt_test.a65 @@ -0,0 +1,1026 @@ +; +; 6 5 0 2 I N T E R R U P T T E S T +; +; Copyright (C) 2013 Klaus Dormann +; +; This program is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . + + +; This program is designed to test IRQ and NMI of a 6502 emulator. It requires +; an internal or external feedback register to the IRQ & NMI inputs +; +; version 15-aug-2014 +; contact info at http://2m5.de or email K@2m5.de +; +; assembled with AS65 from http://www.kingswood-consulting.co.uk/assemblers/ +; command line switches: -l -m -s2 -w -h0 +; | | | | no page headers in listing +; | | | wide listing (133 char/col) +; | | write intel hex file instead of binary +; | expand macros in listing +; generate pass2 listing +; +; No IO - should be run from a monitor with access to registers. +; To run load intel hex image with a load command, than alter PC to 400 hex and +; enter a go command. +; Loop on program counter determines error or successful completion of test. +; Check listing for relevant traps (jump/branch *). +; +; Debugging hints: +; Most of the code is written sequentially. if you hit a trap, check the +; immediately preceeding code for the instruction to be tested. Results are +; tested first, flags are checked second by pushing them onto the stack and +; pulling them to the accumulator after the result was checked. The "real" +; flags are no longer valid for the tested instruction at this time! +; If the tested instruction was indexed, the relevant index (X or Y) must +; also be checked. Opposed to the flags, X and Y registers are still valid. +; +; versions: +; 19-jul-2013 1st version distributed for testing +; 16-aug-2013 added error report to standard output option +; 15-aug-2014 added filter to feedback (bit 7 will cause diag stop in emu) + + +; C O N F I G U R A T I O N +; +;ROM_vectors MUST be writable & the I_flag MUST be alterable + +;load_data_direct (0=move from code segment, 1=load directly) +;loading directly is preferred but may not be supported by your platform +;0 produces only consecutive object code, 1 is not suitable for a binary image +load_data_direct = 1 + +;NMI & IRQ are tested with a feedback register +;emulators diag register - set i_drive = 0 for a latch (74HC573) +I_port = $bffc ;feedback port address +I_ddr = 0 ;feedback DDR address, 0 = no DDR +I_drive = 1 ;0 = totem pole, 1 = open collector +IRQ_bit = 0 ;bit number of feedback to IRQ +NMI_bit = 1 ;bit number of feedback to NMI, -1 if not available +I_filter = $7f ;filtering bit 7 = diag stop + +;typical IO chip port B - set i_drive = 0 to avoid pullup resistors +;I_port = $bfb2 ;feedback port address +;I_ddr = $bfb3 ;feedback DDR address, 0 = no DDR +;I_drive = 1 ;0 = totem pole, 1 = open collector +;IRQ_bit = 0 ;bit number of feedback to IRQ +;NMI_bit = 1 ;bit number of feedback to NMI, -1 if not available +;I_filter = $ff ;no bits filtered + +;decimal mode flag during IRQ, NMI & BRK +D_clear = 0 ;0 = not cleared (NMOS), 1 = cleared (CMOS) + +;configure memory - try to stay away from memory used by the system +;zero_page memory start address, 6 consecutive Bytes required +zero_page = $a + +;data_segment memory start address, 4 consecutive Bytes required +data_segment = $200 + +;code_segment memory start address +code_segment = $400 + +;report errors through I/O channel (0=use standard self trap loops, 1=include +;report.i65 as I/O channel) +report = 0 + + noopt ;do not take shortcuts + +;macros for error & success traps to allow user modification +;example: +;trap macro +; jsr my_error_handler +; endm +;trap_eq macro +; bne skip\? +; trap ;failed equal (zero) +;skip\? +; endm +; +; my_error_handler should pop the calling address from the stack and report it. +; putting larger portions of code (more than 3 bytes) inside the trap macro +; may lead to branch range problems for some tests. + if report = 0 +trap macro + jmp * ;failed anyway + endm +trap_eq macro + beq * ;failed equal (zero) + endm +trap_ne macro + bne * ;failed not equal (non zero) + endm +; please observe that during the test the stack gets invalidated +; therefore a RTS inside the success macro is not possible +success macro + jmp * ;test passed, no errors + endm + endif + if report = 1 +trap macro + jsr report_error + endm +trap_eq macro + bne skip\? + trap ;failed equal (zero) +skip\? + endm +trap_ne macro + beq skip\? + trap ;failed not equal (non zero) +skip\? + endm +; please observe that during the test the stack gets invalidated +; therefore a RTS inside the success macro is not possible +success macro + jsr report_success + endm + endif + + +carry equ %00000001 ;flag bits in status +zero equ %00000010 +intdis equ %00000100 +decmode equ %00001000 +break equ %00010000 +reserv equ %00100000 +overfl equ %01000000 +minus equ %10000000 + +fc equ carry +fz equ zero +fzc equ carry+zero +fv equ overfl +fvz equ overfl+zero +fn equ minus +fnc equ minus+carry +fnz equ minus+zero +fnzc equ minus+zero+carry +fnv equ minus+overfl + +fao equ break+reserv ;bits always on after PHP, BRK +fai equ fao+intdis ;+ forced interrupt disable +m8 equ $ff ;8 bit mask +m8i equ $ff&~intdis ;8 bit mask - interrupt disable + +;macros to set status +push_stat macro ;setting flags in the processor status register + lda #\1 + pha ;use stack to load status + endm + +set_stat macro ;setting flags in the processor status register + lda #\1 + pha ;use stack to load status + plp + endm + + if load_data_direct = 1 + data + else + bss ;uninitialized segment, copy of data at end of code! + endif + org zero_page +;BRK, IRQ, NMI test interrupt save +zpt +irq_a ds 1 ;a register +irq_x ds 1 ;x register +irq_f ds 1 ;flags +nmi_a ds 1 ;a register +nmi_x ds 1 ;x register +nmi_f ds 1 ;flags +zp_bss + +;fixed stack locations +lst_f equ $1fe ;last flags before interrupt +lst_a equ $1ff ;last accumulator before interrupt + + org data_segment +;concurrent NMI, IRQ & BRK test result +nmi_count ds 1 ;lowest number handled first, $ff = never +irq_count ds 1 ;separation-1 = instructions between interrupts +brk_count ds 1 +;expected interrupt mask +I_src ds 1 ;bit: 0=BRK, 1=IRQ, 2=NMI +data_bss + + code + org code_segment +start cld + lda #0 ;clear expected interrupts for 2nd run + sta I_src + ldx #$ff + txs + +;initialize I/O for report channel + if report = 1 + jsr report_init + endif + +; load system vectors + if load_data_direct != 1 + ldx #5 +ld_vect lda vec_init,x + sta vec_bss,x + dex + bpl ld_vect + endif + +; IRQ & NMI test - requires a feedback register + if I_drive > 1 + ERROR ;invalid interrupt drive! + endif + if NMI_bit < 0 + if I_drive = 0 ;totem pole (push/pull, 0 -> I_port to force interrupt) +I_set macro ibit ;ibit = interrupt bit + lda I_port ;turn on interrupt by bit + and #I_filter-(1<<\1) + plp ;set flags + pha ;save to verify + php + sta I_port ;interrupt next instruction plus outbound delay + endm +I_clr macro ibit ;ibit = interrupt bit + lda I_port ;turn off interrupt by bit + and #I_filter + ora #(1< I_DDR or I_port to force interrupt + if I_ddr != 0 ;with DDR +I_set macro ibit ;ibit = interrupt bit + lda I_ddr ;turn on interrupt by bit + and #I_filter + ora #(1<<\1) + plp ;set flags + pha ;save to verify + php + sta I_ddr ;interrupt next instruction plus outbound delay + endm +I_clr macro ibit ;ibit = interrupt bit + lda I_ddr ;turn off interrupt by bit + and #I_filter-(1< I_port to force interrupt) +I_set macro ibit ;ibit = interrupt bit + lda I_port ;turn on interrupt by bit + if ibit > 7 ;set both NMI & IRQ + and #I_filter-(1< I_DDR or I_port to force interrupt + if I_ddr != 0 ;with DDR +I_set macro ibit ;ibit = interrupt bit + lda I_ddr ;turn on interrupt by bit + and #I_filter + if ibit > 7 ;set both NMI & IRQ + ora #(1< 7 ;set both NMI & IRQ + ora #(1<. + + +; This program is designed to test all additional 65C02 opcodes, addressing +; modes and functionality not available in the NMOS version of the 6502. +; The 6502_functional_test is a prerequisite to this test. +; NMI, IRQ, BRK, STP & WAI are covered in the 6502_interrupt_test. +; +; version 09-feb-2017 +; contact info at http://2m5.de or email K@2m5.de +; +; assembled with AS65 from http://www.kingswood-consulting.co.uk/assemblers/ +; command line switches: -l -m -s2 -w -x -h0 +; | | | | | no page headers in listing +; | | | | 65C02 extensions +; | | | wide listing (133 char/col) +; | | write intel hex file instead of binary +; | expand macros in listing +; generate pass2 listing +; +; No IO - should be run from a monitor with access to registers. +; To run load intel hex image with a load command, than alter PC to 400 hex +; (code_segment) and enter a go command. +; Loop on program counter determines error or successful completion of test. +; Check listing for relevant traps (jump/branch *). +; Please note that in early tests some instructions will have to be used before +; they are actually tested! +; +; RESET, NMI or IRQ should not occur and will be trapped if vectors are enabled. +; Tests documented behavior of the original 65C02 only! +; Decimal ops will only be tested with valid BCD operands and the V flag will +; be ignored as it is absolutely useless in decimal mode. +; +; Debugging hints: +; Most of the code is written sequentially. if you hit a trap, check the +; immediately preceeding code for the instruction to be tested. Results are +; tested first, flags are checked second by pushing them onto the stack and +; pulling them to the accumulator after the result was checked. The "real" +; flags are no longer valid for the tested instruction at this time! +; If the tested instruction was indexed, the relevant index (X or Y) must +; also be checked. Opposed to the flags, X and Y registers are still valid. +; +; versions: +; 19-jul-2013 1st version distributed for testing +; 23-jul-2013 fixed BRA out of range due to larger trap macros +; added RAM integrity check +; 16-aug-2013 added error report to standard output option +; 23-aug-2015 change revoked +; 24-aug-2015 all self modifying immediate opcodes now execute in data RAM +; 28-aug-2015 fixed decimal adc/sbc immediate only testing carry +; 09-feb-2017 fixed RMB/SMB tested when they shouldn't be tested + + +; C O N F I G U R A T I O N + +;ROM_vectors writable (0=no, 1=yes) +;if ROM vectors can not be used interrupts will not be trapped +;as a consequence BRK can not be tested but will be emulated to test RTI +ROM_vectors = 1 + +;load_data_direct (0=move from code segment, 1=load directly) +;loading directly is preferred but may not be supported by your platform +;0 produces only consecutive object code, 1 is not suitable for a binary image +load_data_direct = 1 + +;I_flag behavior (0=force enabled, 1=force disabled, 2=prohibit change, 3=allow +;change) 2 requires extra code and is not recommended. +I_flag = 3 + +;configure memory - try to stay away from memory used by the system +;zero_page memory start address, $4e (78) consecutive Bytes required +; add 2 if I_flag = 2 +zero_page = $a + +;data_segment memory start address, $63 (99) consecutive Bytes required +; + 12 Bytes at data_segment + $f9 (JMP indirect page cross test) +data_segment = $200 + if (data_segment & $ff) != 0 + ERROR ERROR ERROR low byte of data_segment MUST be $00 !! + endif + +;code_segment memory start address, 10kB of consecutive space required +; add 1 kB if I_flag = 2 +code_segment = $400 + +;added WDC only opcodes WAI & STP (0=test as NOPs, >0=no test) +wdc_op = 1 + +;added Rockwell & WDC opcodes BBR, BBS, RMB & SMB +;(0=test as NOPs, 1=full test, >1=no test) +rkwl_wdc_op = 1 + +;report errors through I/O channel (0=use standard self trap loops, 1=include +;report.i65 as I/O channel, add 3 kB) +report = 0 + +;RAM integrity test option. Checks for undesired RAM writes. +;set lowest non RAM or RAM mirror address page (-1=disable, 0=64k, $40=16k) +;leave disabled if a monitor, OS or background interrupt is allowed to alter RAM +ram_top = -1 + + noopt ;do not take shortcuts + +;macros for error & success traps to allow user modification +;example: +;trap macro +; jsr my_error_handler +; endm +;trap_eq macro +; bne skip\? +; trap ;failed equal (zero) +;skip\? +; endm +; +; my_error_handler should pop the calling address from the stack and report it. +; putting larger portions of code (more than 3 bytes) inside the trap macro +; may lead to branch range problems for some tests. + if report = 0 +trap macro + jmp * ;failed anyway + endm +trap_eq macro + beq * ;failed equal (zero) + endm +trap_ne macro + bne * ;failed not equal (non zero) + endm +trap_cs macro + bcs * ;failed carry set + endm +trap_cc macro + bcc * ;failed carry clear + endm +trap_mi macro + bmi * ;failed minus (bit 7 set) + endm +trap_pl macro + bpl * ;failed plus (bit 7 clear) + endm +trap_vs macro + bvs * ;failed overflow set + endm +trap_vc macro + bvc * ;failed overflow clear + endm +; please observe that during the test the stack gets invalidated +; therefore a RTS inside the success macro is not possible +success macro + jmp * ;test passed, no errors + endm + endif + if report = 1 +trap macro + jsr report_error + endm +trap_eq macro + bne skip\? + trap ;failed equal (zero) +skip\? + endm +trap_ne macro + beq skip\? + trap ;failed not equal (non zero) +skip\? + endm +trap_cs macro + bcc skip\? + trap ;failed carry set +skip\? + endm +trap_cc macro + bcs skip\? + trap ;failed carry clear +skip\? + endm +trap_mi macro + bpl skip\? + trap ;failed minus (bit 7 set) +skip\? + endm +trap_pl macro + bmi skip\? + trap ;failed plus (bit 7 clear) +skip\? + endm +trap_vs macro + bvc skip\? + trap ;failed overflow set +skip\? + endm +trap_vc macro + bvs skip\? + trap ;failed overflow clear +skip\? + endm +; please observe that during the test the stack gets invalidated +; therefore a RTS inside the success macro is not possible +success macro + jsr report_success + endm + endif + + +carry equ %00000001 ;flag bits in status +zero equ %00000010 +intdis equ %00000100 +decmode equ %00001000 +break equ %00010000 +reserv equ %00100000 +overfl equ %01000000 +minus equ %10000000 + +fc equ carry +fz equ zero +fzc equ carry+zero +fv equ overfl +fvz equ overfl+zero +fn equ minus +fnc equ minus+carry +fnz equ minus+zero +fnzc equ minus+zero+carry +fnv equ minus+overfl + +fao equ break+reserv ;bits always on after PHP, BRK +fai equ fao+intdis ;+ forced interrupt disable +m8 equ $ff ;8 bit mask +m8i equ $ff&~intdis ;8 bit mask - interrupt disable + +;macros to allow masking of status bits. +;masking of interrupt enable/disable on load and compare +;masking of always on bits after PHP or BRK (unused & break) on compare + if I_flag = 0 +load_flag macro + lda #\1&m8i ;force enable interrupts (mask I) + endm +cmp_flag macro + cmp #(\1|fao)&m8i ;I_flag is always enabled + always on bits + endm +eor_flag macro + eor #(\1&m8i|fao) ;mask I, invert expected flags + always on bits + endm + endif + if I_flag = 1 +load_flag macro + lda #\1|intdis ;force disable interrupts + endm +cmp_flag macro + cmp #(\1|fai)&m8 ;I_flag is always disabled + always on bits + endm +eor_flag macro + eor #(\1|fai) ;invert expected flags + always on bits + I + endm + endif + if I_flag = 2 +load_flag macro + lda #\1 + ora flag_I_on ;restore I-flag + and flag_I_off + endm +cmp_flag macro + eor flag_I_on ;I_flag is never changed + cmp #(\1|fao)&m8i ;expected flags + always on bits, mask I + endm +eor_flag macro + eor flag_I_on ;I_flag is never changed + eor #(\1&m8i|fao) ;mask I, invert expected flags + always on bits + endm + endif + if I_flag = 3 +load_flag macro + lda #\1 ;allow test to change I-flag (no mask) + endm +cmp_flag macro + cmp #(\1|fao)&m8 ;expected flags + always on bits + endm +eor_flag macro + eor #\1|fao ;invert expected flags + always on bits + endm + endif + +;macros to set (register|memory|zeropage) & status +set_stat macro ;setting flags in the processor status register + load_flag \1 + pha ;use stack to load status + plp + endm + +set_a macro ;precharging accu & status + load_flag \2 + pha ;use stack to load status + lda #\1 ;precharge accu + plp + endm + +set_x macro ;precharging index & status + load_flag \2 + pha ;use stack to load status + ldx #\1 ;precharge index x + plp + endm + +set_y macro ;precharging index & status + load_flag \2 + pha ;use stack to load status + ldy #\1 ;precharge index y + plp + endm + +set_ax macro ;precharging indexed accu & immediate status + load_flag \2 + pha ;use stack to load status + lda \1,x ;precharge accu + plp + endm + +set_ay macro ;precharging indexed accu & immediate status + load_flag \2 + pha ;use stack to load status + lda \1,y ;precharge accu + plp + endm + +set_z macro ;precharging indexed zp & immediate status + load_flag \2 + pha ;use stack to load status + lda \1,x ;load to zeropage + sta zpt + plp + endm + +set_zx macro ;precharging zp,x & immediate status + load_flag \2 + pha ;use stack to load status + lda \1,x ;load to indexed zeropage + sta zpt,x + plp + endm + +set_abs macro ;precharging indexed memory & immediate status + load_flag \2 + pha ;use stack to load status + lda \1,x ;load to memory + sta abst + plp + endm + +set_absx macro ;precharging abs,x & immediate status + load_flag \2 + pha ;use stack to load status + lda \1,x ;load to indexed memory + sta abst,x + plp + endm + +;macros to test (register|memory|zeropage) & status & (mask) +tst_stat macro ;testing flags in the processor status register + php ;save status + pla ;use stack to retrieve status + pha + cmp_flag \1 + trap_ne + plp ;restore status + endm + +tst_a macro ;testing result in accu & flags + php ;save flags + cmp #\1 ;test result + trap_ne + pla ;load status + pha + cmp_flag \2 + trap_ne + plp ;restore status + endm + +tst_as macro ;testing result in accu & flags, save accu + pha + php ;save flags + cmp #\1 ;test result + trap_ne + pla ;load status + pha + cmp_flag \2 + trap_ne + plp ;restore status + pla + endm + +tst_x macro ;testing result in x index & flags + php ;save flags + cpx #\1 ;test result + trap_ne + pla ;load status + pha + cmp_flag \2 + trap_ne + plp ;restore status + endm + +tst_y macro ;testing result in y index & flags + php ;save flags + cpy #\1 ;test result + trap_ne + pla ;load status + pha + cmp_flag \2 + trap_ne + plp ;restore status + endm + +tst_ax macro ;indexed testing result in accu & flags + php ;save flags + cmp \1,x ;test result + trap_ne + pla ;load status + eor_flag \3 + cmp \2,x ;test flags + trap_ne ; + endm + +tst_ay macro ;indexed testing result in accu & flags + php ;save flags + cmp \1,y ;test result + trap_ne ; + pla ;load status + eor_flag \3 + cmp \2,y ;test flags + trap_ne + endm + +tst_z macro ;indexed testing result in zp & flags + php ;save flags + lda zpt + cmp \1,x ;test result + trap_ne + pla ;load status + eor_flag \3 + cmp \2,x ;test flags + trap_ne + endm + +tst_zx macro ;testing result in zp,x & flags + php ;save flags + lda zpt,x + cmp \1,x ;test result + trap_ne + pla ;load status + eor_flag \3 + cmp \2,x ;test flags + trap_ne + endm + +tst_abs macro ;indexed testing result in memory & flags + php ;save flags + lda abst + cmp \1,x ;test result + trap_ne + pla ;load status + eor_flag \3 + cmp \2,x ;test flags + trap_ne + endm + +tst_absx macro ;testing result in abs,x & flags + php ;save flags + lda abst,x + cmp \1,x ;test result + trap_ne + pla ;load status + eor_flag \3 + cmp \2,x ;test flags + trap_ne + endm + +; RAM integrity test +; verifies that none of the previous tests has altered RAM outside of the +; designated write areas. +; uses zpt word as indirect pointer, zpt+2 word as checksum + if ram_top > -1 +check_ram macro + cld + lda #0 + sta zpt ;set low byte of indirect pointer + sta zpt+3 ;checksum high byte + ldx #11 ;reset modifiable RAM +ccs1\? sta jxi_tab,x ;JMP indirect page cross area + dex + bpl ccs1\? + clc + ldx #zp_bss-zero_page ;zeropage - write test area +ccs3\? adc zero_page,x + bcc ccs2\? + inc zpt+3 ;carry to high byte + clc +ccs2\? inx + bne ccs3\? + ldx #hi(abs1) ;set high byte of indirect pointer + stx zpt+1 + ldy #lo(abs1) ;data after write & execute test area +ccs5\? adc (zpt),y + bcc ccs4\? + inc zpt+3 ;carry to high byte + clc +ccs4\? iny + bne ccs5\? + inx ;advance RAM high address + stx zpt+1 + cpx #ram_top + bne ccs5\? + sta zpt+2 ;checksum low is + cmp ram_chksm ;checksum low expected + trap_ne ;checksum mismatch + lda zpt+3 ;checksum high is + cmp ram_chksm+1 ;checksum high expected + trap_ne ;checksum mismatch + endm + else +check_ram macro + ;RAM check disabled - RAM size not set + endm + endif + +next_test macro ;make sure, tests don't jump the fence + lda test_case ;previous test + cmp #test_num + trap_ne ;test is out of sequence +test_num = test_num + 1 + lda #test_num ;*** next tests' number + sta test_case + ;check_ram ;uncomment to find altered RAM after each test + endm + + if load_data_direct = 1 + data + else + bss ;uninitialized segment, copy of data at end of code! + endif + org zero_page + if I_flag = 2 +;masking for I bit in status +flag_I_on ds 1 ;or mask to load flags +flag_I_off ds 1 ;and mask to load flags + endif +zpt ;5 bytes store/modify test area +;add/subtract operand generation and result/flag prediction +adfc ds 1 ;carry flag before op +ad1 ds 1 ;operand 1 - accumulator +ad2 ds 1 ;operand 2 - memory / immediate +adrl ds 1 ;expected result bits 0-7 +adrh ds 1 ;expected result bit 8 (carry) +adrf ds 1 ;expected flags NV0000ZC (-V in decimal mode) +sb2 ds 1 ;operand 2 complemented for subtract +zp_bss +zp1 db $c3,$82,$41,0 ;test patterns for LDx BIT ROL ROR ASL LSR +zp7f db $7f ;test pattern for compare +;logical zeropage operands +zpOR db 0,$1f,$71,$80 ;test pattern for OR +zpAN db $0f,$ff,$7f,$80 ;test pattern for AND +zpEO db $ff,$0f,$8f,$8f ;test pattern for EOR +;indirect addressing pointers +ind1 dw abs1 ;indirect pointer to pattern in absolute memory + dw abs1+1 + dw abs1+2 + dw abs1+3 + dw abs7f +inw1 dw abs1-$f8 ;indirect pointer for wrap-test pattern +indt dw abst ;indirect pointer to store area in absolute memory + dw abst+1 + dw abst+2 + dw abst+3 +inwt dw abst-$f8 ;indirect pointer for wrap-test store +indAN dw absAN ;indirect pointer to AND pattern in absolute memory + dw absAN+1 + dw absAN+2 + dw absAN+3 +indEO dw absEO ;indirect pointer to EOR pattern in absolute memory + dw absEO+1 + dw absEO+2 + dw absEO+3 +indOR dw absOR ;indirect pointer to OR pattern in absolute memory + dw absOR+1 + dw absOR+2 + dw absOR+3 +;add/subtract indirect pointers +adi2 dw ada2 ;indirect pointer to operand 2 in absolute memory +sbi2 dw sba2 ;indirect pointer to complemented operand 2 (SBC) +adiy2 dw ada2-$ff ;with offset for indirect indexed +sbiy2 dw sba2-$ff +zp_bss_end + + org data_segment +pg_x ds 2 ;high JMP indirect address for page cross bug +test_case ds 1 ;current test number +ram_chksm ds 2 ;checksum for RAM integrity test +;add/subtract operand copy - abs tests write area +abst ;5 bytes store/modify test area +ada2 ds 1 ;operand 2 +sba2 ds 1 ;operand 2 complemented for subtract + ds 3 ;fill remaining bytes +data_bss + if load_data_direct = 1 +ex_adci adc #0 ;execute immediate opcodes + rts +ex_sbci sbc #0 ;execute immediate opcodes + rts + else +ex_adci ds 3 +ex_sbci ds 3 + endif +abs1 db $c3,$82,$41,0 ;test patterns for LDx BIT ROL ROR ASL LSR +abs7f db $7f ;test pattern for compare +;loads +fLDx db fn,fn,0,fz ;expected flags for load +;shifts +rASL ;expected result ASL & ROL -carry +rROL db $86,$04,$82,0 ; " +rROLc db $87,$05,$83,1 ;expected result ROL +carry +rLSR ;expected result LSR & ROR -carry +rROR db $61,$41,$20,0 ; " +rRORc db $e1,$c1,$a0,$80 ;expected result ROR +carry +fASL ;expected flags for shifts +fROL db fnc,fc,fn,fz ;no carry in +fROLc db fnc,fc,fn,0 ;carry in +fLSR +fROR db fc,0,fc,fz ;no carry in +fRORc db fnc,fn,fnc,fn ;carry in +;increments (decrements) +rINC db $7f,$80,$ff,0,1 ;expected result for INC/DEC +fINC db 0,fn,fn,fz,0 ;expected flags for INC/DEC +;logical memory operand +absOR db 0,$1f,$71,$80 ;test pattern for OR +absAN db $0f,$ff,$7f,$80 ;test pattern for AND +absEO db $ff,$0f,$8f,$8f ;test pattern for EOR +;logical accu operand +absORa db 0,$f1,$1f,0 ;test pattern for OR +absANa db $f0,$ff,$ff,$ff ;test pattern for AND +absEOa db $ff,$f0,$f0,$0f ;test pattern for EOR +;logical results +absrlo db 0,$ff,$7f,$80 +absflo db fz,fn,0,fn +data_bss_end +;define area for page crossing JMP (abs) & JMP (abs,x) test +jxi_tab equ data_segment + $100 - 7 ;JMP (jxi_tab,x) x=6 +ji_tab equ data_segment + $100 - 3 ;JMP (ji_tab+2) +jxp_tab equ data_segment + $100 ;JMP (jxp_tab-255) x=255 + + + code + org code_segment +start cld + ldx #$ff + txs + lda #0 ;*** test 0 = initialize + sta test_case +test_num = 0 + +;stop interrupts before initializing BSS + if I_flag = 1 + sei + endif + +;initialize I/O for report channel + if report = 1 + jsr report_init + endif + +;initialize BSS segment + if load_data_direct != 1 + ldx #zp_end-zp_init-1 +ld_zp lda zp_init,x + sta zp_bss,x + dex + bpl ld_zp + ldx #data_end-data_init-1 +ld_data lda data_init,x + sta data_bss,x + dex + bpl ld_data + if ROM_vectors = 1 + ldx #5 +ld_vect lda vec_init,x + sta vec_bss,x + dex + bpl ld_vect + endif + endif + +;retain status of interrupt flag + if I_flag = 2 + php + pla + and #4 ;isolate flag + sta flag_I_on ;or mask + eor #lo(~4) ;reverse + sta flag_I_off ;and mask + endif + +;generate checksum for RAM integrity test + if ram_top > -1 + lda #0 + sta zpt ;set low byte of indirect pointer + sta ram_chksm+1 ;checksum high byte + ldx #11 ;reset modifiable RAM +gcs1 sta jxi_tab,x ;JMP indirect page cross area + dex + bpl gcs1 + clc + ldx #zp_bss-zero_page ;zeropage - write test area +gcs3 adc zero_page,x + bcc gcs2 + inc ram_chksm+1 ;carry to high byte + clc +gcs2 inx + bne gcs3 + ldx #hi(abs1) ;set high byte of indirect pointer + stx zpt+1 + ldy #lo(abs1) ;data after write & execute test area +gcs5 adc (zpt),y + bcc gcs4 + inc ram_chksm+1 ;carry to high byte + clc +gcs4 iny + bne gcs5 + inx ;advance RAM high address + stx zpt+1 + cpx #ram_top + bne gcs5 + sta ram_chksm ;checksum complete + endif + next_test + +;testing stack operations PHX PHY PLX PLY + lda #$99 ;protect a + ldx #$ff ;initialize stack + txs + ldx #$55 + phx + ldx #$aa + phx + cpx $1fe ;on stack ? + trap_ne + tsx + cpx #$fd ;sp decremented? + trap_ne + ply + cpy #$aa ;successful retreived from stack? + trap_ne + ply + cpy #$55 + trap_ne + cpy $1ff ;remains on stack? + trap_ne + tsx + cpx #$ff ;sp incremented? + trap_ne + + ldy #$a5 + phy + ldy #$5a + phy + cpy $1fe ;on stack ? + trap_ne + tsx + cpx #$fd ;sp decremented? + trap_ne + plx + cpx #$5a ;successful retreived from stack? + trap_ne + plx + cpx #$a5 + trap_ne + cpx $1ff ;remains on stack? + trap_ne + tsx + cpx #$ff ;sp incremented? + trap_ne + cmp #$99 ;unchanged? + trap_ne + next_test + +; test PHX does not alter flags or X but PLX does + ldy #$aa ;protect y + set_x 1,$ff ;push + phx + tst_x 1,$ff + set_x 0,0 + phx + tst_x 0,0 + set_x $ff,$ff + phx + tst_x $ff,$ff + set_x 1,0 + phx + tst_x 1,0 + set_x 0,$ff + phx + tst_x 0,$ff + set_x $ff,0 + phx + tst_x $ff,0 + set_x 0,$ff ;pull + plx + tst_x $ff,$ff-zero + set_x $ff,0 + plx + tst_x 0,zero + set_x $fe,$ff + plx + tst_x 1,$ff-zero-minus + set_x 0,0 + plx + tst_x $ff,minus + set_x $ff,$ff + plx + tst_x 0,$ff-minus + set_x $fe,0 + plx + tst_x 1,0 + cpy #$aa ;Y unchanged + trap_ne + next_test + +; test PHY does not alter flags or Y but PLY does + ldx #$55 ;x & a protected + set_y 1,$ff ;push + phy + tst_y 1,$ff + set_y 0,0 + phy + tst_y 0,0 + set_y $ff,$ff + phy + tst_y $ff,$ff + set_y 1,0 + phy + tst_y 1,0 + set_y 0,$ff + phy + tst_y 0,$ff + set_y $ff,0 + phy + tst_y $ff,0 + set_y 0,$ff ;pull + ply + tst_y $ff,$ff-zero + set_y $ff,0 + ply + tst_y 0,zero + set_y $fe,$ff + ply + tst_y 1,$ff-zero-minus + set_y 0,0 + ply + tst_y $ff,minus + set_y $ff,$ff + ply + tst_y 0,$ff-minus + set_y $fe,0 + ply + tst_y 1,0 + cpx #$55 ;x unchanged? + trap_ne + next_test + +; PC modifying instructions (BRA, BBR, BBS, 1, 2, 3 byte NOPs, JMP(abs,x)) +; testing unconditional branch BRA + + ldx #$81 ;protect unused registers + ldy #$7e + set_a 0,$ff + bra br1 ;branch should always be taken + trap +br1 + tst_a 0,$ff + set_a $ff,0 + bra br2 ;branch should always be taken + trap +br2 + tst_a $ff,0 + cpx #$81 + trap_ne + cpy #$7e + trap_ne + next_test + + ldy #0 ;branch range test + bra bra0 + +bra1 cpy #1 + trap_ne ;long range backward + iny + bra bra2 + +bra3 cpy #3 + trap_ne ;long range backward + iny + bra bra4 + +bra5 cpy #5 + trap_ne ;long range backward + iny + ldy #0 + bra brf0 + + iny + iny + iny + iny +brf0 bra brf1 + + iny + iny + iny +brf1 iny + bra brf2 + + iny + iny +brf2 iny + iny + bra brf3 + + iny +brf3 iny + iny + iny + bra brf4 + +brf4 iny + iny + iny + iny + cpy #10 + trap_ne ;short range forward + bra brb0 + +brb4 dey + dey + dey + dey + bra brb5 + +brb3 dey + dey + dey + bra brb4 + +brb2 dey + dey + bra brb3 + +brb1 dey + bra brb2 + +brb0 bra brb1 + +brb5 cpy #0 + trap_ne ;short range backward + bra bra6 + +bra4 cpy #4 + trap_ne ;long range forward + iny + bra bra5 + +bra2 cpy #2 + trap_ne ;long range forward + iny + bra bra3 + +bra0 cpy #0 + trap_ne ;long range forward + iny + bra bra1 + +bra6 + next_test + + if rkwl_wdc_op = 1 +; testing BBR & BBS + +bbt macro ;\1 = bitnum + lda #(1<<\1) ;testing 1 bit on + sta zpt + set_a $33,0 ;with flags off + bbr \1,zpt,fail1\? + bbs \1,zpt,ok1\? + trap ;bbs branch not taken +fail1\? + trap ;bbr branch taken +ok1\? + tst_a $33,0 + set_a $cc,$ff ;with flags on + bbr \1,zpt,fail2\? + bbs \1,zpt,ok2\? + trap ;bbs branch not taken +fail2\? + trap ;bbr branch taken +ok2\? + tst_a $cc,$ff + lda zpt + cmp #(1<<\1) + trap_ne ;zp altered + lda #$ff-(1<<\1) ;testing 1 bit off + sta zpt + set_a $33,0 ;with flags off + bbs \1,zpt,fail3\? + bbr \1,zpt,ok3\? + trap ;bbr branch not taken +fail3\? + trap ;bbs branch taken +ok3\? + tst_a $33,0 + set_a $cc,$ff ;with flags on + bbs \1,zpt,fail4\? + bbr \1,zpt,ok4\? + trap ;bbr branch not taken +fail4\? + trap ;bbs branch taken +ok4\? + tst_a $cc,$ff + lda zpt + cmp #$ff-(1<<\1) + trap_ne ;zp altered + endm + + ldx #$11 ;test bbr/bbs integrity + ldy #$22 + bbt 0 + bbt 1 + bbt 2 + bbt 3 + bbt 4 + bbt 5 + bbt 6 + bbt 7 + cpx #$11 + trap_ne ;x overwritten + cpy #$22 + trap_ne ;y overwritten + next_test + +bbrc macro ;\1 = bitnum + bbr \1,zpt,skip\? + eor #(1<<\1) +skip\? + endm +bbsc macro ;\1 = bitnum + bbs \1,zpt,skip\? + eor #(1<<\1) +skip\? + endm + + lda #0 ;combined bit test + sta zpt +bbcl lda #0 + bbrc 0 + bbrc 1 + bbrc 2 + bbrc 3 + bbrc 4 + bbrc 5 + bbrc 6 + bbrc 7 + eor zpt + trap_ne ;failed bbr bitnum in accu + lda #$ff + bbsc 0 + bbsc 1 + bbsc 2 + bbsc 3 + bbsc 4 + bbsc 5 + bbsc 6 + bbsc 7 + eor zpt + trap_ne ;failed bbs bitnum in accu + inc zpt + bne bbcl + next_test + endif + +; testing NOP + +nop_test macro ;\1 = opcode, \2 = # of bytes + ldy #$42 + ldx #4-\2 + db \1 ;test nop length + if \2 = 1 + dex + dex + endif + if \2 = 2 + iny + dex + endif + if \2 = 3 + iny + iny + endif + dex + trap_ne ;wrong number of bytes + set_a $ff-\1,0 + db \1 ;test nop integrity - flags off + nop + nop + tst_a $ff-\1,0 + set_a $aa-\1,$ff + db \1 ;test nop integrity - flags on + nop + nop + tst_a $aa-\1,$ff + cpy #$42 + trap_ne ;y changed + cpx #0 + trap_ne ;x changed + endm + + nop_test $02,2 + nop_test $22,2 + nop_test $42,2 + nop_test $62,2 + nop_test $82,2 + nop_test $c2,2 + nop_test $e2,2 + nop_test $44,2 + nop_test $54,2 + nop_test $d4,2 + nop_test $f4,2 + nop_test $5c,3 + nop_test $dc,3 + nop_test $fc,3 + nop_test $03,1 + nop_test $13,1 + nop_test $23,1 + nop_test $33,1 + nop_test $43,1 + nop_test $53,1 + nop_test $63,1 + nop_test $73,1 + nop_test $83,1 + nop_test $93,1 + nop_test $a3,1 + nop_test $b3,1 + nop_test $c3,1 + nop_test $d3,1 + nop_test $e3,1 + nop_test $f3,1 + nop_test $0b,1 + nop_test $1b,1 + nop_test $2b,1 + nop_test $3b,1 + nop_test $4b,1 + nop_test $5b,1 + nop_test $6b,1 + nop_test $7b,1 + nop_test $8b,1 + nop_test $9b,1 + nop_test $ab,1 + nop_test $bb,1 + nop_test $eb,1 + nop_test $fb,1 + if rkwl_wdc_op = 0 ;NOPs not available on Rockwell & WDC 65C02 + nop_test $07,1 + nop_test $17,1 + nop_test $27,1 + nop_test $37,1 + nop_test $47,1 + nop_test $57,1 + nop_test $67,1 + nop_test $77,1 + nop_test $87,1 + nop_test $97,1 + nop_test $a7,1 + nop_test $b7,1 + nop_test $c7,1 + nop_test $d7,1 + nop_test $e7,1 + nop_test $f7,1 + nop_test $0f,1 + nop_test $1f,1 + nop_test $2f,1 + nop_test $3f,1 + nop_test $4f,1 + nop_test $5f,1 + nop_test $6f,1 + nop_test $7f,1 + nop_test $8f,1 + nop_test $9f,1 + nop_test $af,1 + nop_test $bf,1 + nop_test $cf,1 + nop_test $df,1 + nop_test $ef,1 + nop_test $ff,1 + endif + if wdc_op = 0 ;NOPs not available on WDC 65C02 (WAI, STP) + nop_test $cb,1 + nop_test $db,1 + endif + next_test + +; jump indirect (test page cross bug is fixed) + ldx #3 ;prepare table +ji1 lda ji_adr,x + sta ji_tab,x + dex + bpl ji1 + lda #hi(ji_px) ;high address if page cross bug + sta pg_x + set_stat 0 + lda #'I' + ldx #'N' + ldy #'D' ;N=0, V=0, Z=0, C=0 + jmp (ji_tab) + nop + trap_ne ;runover protection + + dey + dey +ji_ret php ;either SP or Y count will fail, if we do not hit + dey + dey + dey + plp + trap_eq ;returned flags OK? + trap_pl + trap_cc + trap_vc + cmp #('I'^$aa) ;returned registers OK? + trap_ne + cpx #('N'+1) + trap_ne + cpy #('D'-6) + trap_ne + tsx ;SP check + cpx #$ff + trap_ne + next_test + +; jump indexed indirect + ldx #11 ;prepare table +jxi1 lda jxi_adr,x + sta jxi_tab,x + dex + bpl jxi1 + lda #hi(jxi_px) ;high address if page cross bug + sta pg_x + set_stat 0 + lda #'X' + ldx #4 + ldy #'I' ;N=0, V=0, Z=0, C=0 + jmp (jxi_tab,x) + nop + trap_ne ;runover protection + + dey + dey +jxi_ret php ;either SP or Y count will fail, if we do not hit + dey + dey + dey + plp + trap_eq ;returned flags OK? + trap_pl + trap_cc + trap_vc + cmp #('X'^$aa) ;returned registers OK? + trap_ne + cpx #6 + trap_ne + cpy #('I'-6) + trap_ne + tsx ;SP check + cpx #$ff + trap_ne + + lda #lo(jxp_ok) ;test with index causing a page cross + sta jxp_tab + lda #hi(jxp_ok) + sta jxp_tab+1 + lda #lo(jxp_px) + sta pg_x + lda #hi(jxp_px) + sta pg_x+1 + ldx #$ff + jmp (jxp_tab-$ff,x) + +jxp_px + trap ;page cross by index to wrong page + +jxp_ok + next_test + + if ROM_vectors = 1 +; test BRK clears decimal mode + sed + brk + nop +brk_ret + next_test + endif + +; testing accumulator increment/decrement INC A & DEC A + ldx #$ac ;protect x & y + ldy #$dc + set_a $fe,$ff + inc a ;ff + tst_as $ff,$ff-zero + inc a ;00 + tst_as 0,$ff-minus + inc a ;01 + tst_as 1,$ff-minus-zero + dec a ;00 + tst_as 0,$ff-minus + dec a ;ff + tst_as $ff,$ff-zero + dec a ;fe + set_a $fe,0 + inc a ;ff + tst_as $ff,minus + inc a ;00 + tst_as 0,zero + inc a ;01 + tst_as 1,0 + dec a ;00 + tst_as 0,zero + dec a ;ff + tst_as $ff,minus + cpx #$ac + trap_ne ;x altered during test + cpy #$dc + trap_ne ;y altered during test + tsx + cpx #$ff + trap_ne ;sp push/pop mismatch + next_test + +; testing load / store accumulator LDA / STA (zp) + ldx #$99 ;protect x & y + ldy #$66 + set_stat 0 + lda (ind1) + php ;test stores do not alter flags + eor #$c3 + plp + sta (indt) + php ;flags after load/store sequence + eor #$c3 + cmp #$c3 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx ;test flags + trap_ne + set_stat 0 + lda (ind1+2) + php ;test stores do not alter flags + eor #$c3 + plp + sta (indt+2) + php ;flags after load/store sequence + eor #$c3 + cmp #$82 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+1 ;test flags + trap_ne + set_stat 0 + lda (ind1+4) + php ;test stores do not alter flags + eor #$c3 + plp + sta (indt+4) + php ;flags after load/store sequence + eor #$c3 + cmp #$41 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+2 ;test flags + trap_ne + set_stat 0 + lda (ind1+6) + php ;test stores do not alter flags + eor #$c3 + plp + sta (indt+6) + php ;flags after load/store sequence + eor #$c3 + cmp #0 ;test result + trap_ne + pla ;load status + eor_flag 0 + cmp fLDx+3 ;test flags + trap_ne + cpx #$99 + trap_ne ;x altered during test + cpy #$66 + trap_ne ;y altered during test + + ldy #3 ;testing store result + ldx #0 +tstai1 lda abst,y + eor #$c3 + cmp abs1,y + trap_ne ;store to indirect data + txa + sta abst,y ;clear + dey + bpl tstai1 + + ldx #$99 ;protect x & y + ldy #$66 + set_stat $ff + lda (ind1) + php ;test stores do not alter flags + eor #$c3 + plp + sta (indt) + php ;flags after load/store sequence + eor #$c3 + cmp #$c3 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx ;test flags + trap_ne + set_stat $ff + lda (ind1+2) + php ;test stores do not alter flags + eor #$c3 + plp + sta (indt+2) + php ;flags after load/store sequence + eor #$c3 + cmp #$82 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+1 ;test flags + trap_ne + set_stat $ff + lda (ind1+4) + php ;test stores do not alter flags + eor #$c3 + plp + sta (indt+4) + php ;flags after load/store sequence + eor #$c3 + cmp #$41 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+2 ;test flags + trap_ne + set_stat $ff + lda (ind1+6) + php ;test stores do not alter flags + eor #$c3 + plp + sta (indt+6) + php ;flags after load/store sequence + eor #$c3 + cmp #0 ;test result + trap_ne + pla ;load status + eor_flag lo~fnz ;mask bits not altered + cmp fLDx+3 ;test flags + trap_ne + cpx #$99 + trap_ne ;x altered during test + cpy #$66 + trap_ne ;y altered during test + + ldy #3 ;testing store result + ldx #0 +tstai2 lda abst,y + eor #$c3 + cmp abs1,y + trap_ne ;store to indirect data + txa + sta abst,y ;clear + dey + bpl tstai2 + tsx + cpx #$ff + trap_ne ;sp push/pop mismatch + next_test + +; testing STZ - zp / abs / zp,x / abs,x + ldy #123 ;protect y + ldx #4 ;precharge test area + lda #7 +tstz1 sta zpt,x + asl a + dex + bpl tstz1 + ldx #4 + set_a $55,$ff + stz zpt + stz zpt+1 + stz zpt+2 + stz zpt+3 + stz zpt+4 + tst_a $55,$ff +tstz2 lda zpt,x ;verify zeros stored + trap_ne ;non zero after STZ zp + dex + bpl tstz2 + ldx #4 ;precharge test area + lda #7 +tstz3 sta zpt,x + asl a + dex + bpl tstz3 + ldx #4 + set_a $aa,0 + stz zpt + stz zpt+1 + stz zpt+2 + stz zpt+3 + stz zpt+4 + tst_a $aa,0 +tstz4 lda zpt,x ;verify zeros stored + trap_ne ;non zero after STZ zp + dex + bpl tstz4 + + ldx #4 ;precharge test area + lda #7 +tstz5 sta abst,x + asl a + dex + bpl tstz5 + ldx #4 + set_a $55,$ff + stz abst + stz abst+1 + stz abst+2 + stz abst+3 + stz abst+4 + tst_a $55,$ff +tstz6 lda abst,x ;verify zeros stored + trap_ne ;non zero after STZ abs + dex + bpl tstz6 + ldx #4 ;precharge test area + lda #7 +tstz7 sta abst,x + asl a + dex + bpl tstz7 + ldx #4 + set_a $aa,0 + stz abst + stz abst+1 + stz abst+2 + stz abst+3 + stz abst+4 + tst_a $aa,0 +tstz8 lda abst,x ;verify zeros stored + trap_ne ;non zero after STZ abs + dex + bpl tstz8 + + ldx #4 ;precharge test area + lda #7 +tstz11 sta zpt,x + asl a + dex + bpl tstz11 + ldx #4 +tstz15 + set_a $55,$ff + stz zpt,x + tst_a $55,$ff + dex + bpl tstz15 + ldx #4 +tstz12 lda zpt,x ;verify zeros stored + trap_ne ;non zero after STZ zp + dex + bpl tstz12 + ldx #4 ;precharge test area + lda #7 +tstz13 sta zpt,x + asl a + dex + bpl tstz13 + ldx #4 +tstz16 + set_a $aa,0 + stz zpt,x + tst_a $aa,0 + dex + bpl tstz16 + ldx #4 +tstz14 lda zpt,x ;verify zeros stored + trap_ne ;non zero after STZ zp + dex + bpl tstz14 + + ldx #4 ;precharge test area + lda #7 +tstz21 sta abst,x + asl a + dex + bpl tstz21 + ldx #4 +tstz25 + set_a $55,$ff + stz abst,x + tst_a $55,$ff + dex + bpl tstz25 + ldx #4 +tstz22 lda abst,x ;verify zeros stored + trap_ne ;non zero after STZ zp + dex + bpl tstz22 + ldx #4 ;precharge test area + lda #7 +tstz23 sta abst,x + asl a + dex + bpl tstz23 + ldx #4 +tstz26 + set_a $aa,0 + stz abst,x + tst_a $aa,0 + dex + bpl tstz26 + ldx #4 +tstz24 lda abst,x ;verify zeros stored + trap_ne ;non zero after STZ zp + dex + bpl tstz24 + + cpy #123 + trap_ne ;y altered during test + tsx + cpx #$ff + trap_ne ;sp push/pop mismatch + next_test + +; testing BIT - zp,x / abs,x / # + ldy #$42 + ldx #3 + set_a $ff,0 + bit zp1,x ;00 - should set Z / clear NV + tst_a $ff,fz + dex + set_a 1,0 + bit zp1,x ;41 - should set V (M6) / clear NZ + tst_a 1,fv + dex + set_a 1,0 + bit zp1,x ;82 - should set N (M7) & Z / clear V + tst_a 1,fnz + dex + set_a 1,0 + bit zp1,x ;c3 - should set N (M7) & V (M6) / clear Z + tst_a 1,fnv + + set_a 1,$ff + bit zp1,x ;c3 - should set N (M7) & V (M6) / clear Z + tst_a 1,~fz + inx + set_a 1,$ff + bit zp1,x ;82 - should set N (M7) & Z / clear V + tst_a 1,~fv + inx + set_a 1,$ff + bit zp1,x ;41 - should set V (M6) / clear NZ + tst_a 1,~fnz + inx + set_a $ff,$ff + bit zp1,x ;00 - should set Z / clear NV + tst_a $ff,~fnv + + set_a $ff,0 + bit abs1,x ;00 - should set Z / clear NV + tst_a $ff,fz + dex + set_a 1,0 + bit abs1,x ;41 - should set V (M6) / clear NZ + tst_a 1,fv + dex + set_a 1,0 + bit abs1,x ;82 - should set N (M7) & Z / clear V + tst_a 1,fnz + dex + set_a 1,0 + bit abs1,x ;c3 - should set N (M7) & V (M6) / clear Z + tst_a 1,fnv + + set_a 1,$ff + bit abs1,x ;c3 - should set N (M7) & V (M6) / clear Z + tst_a 1,~fz + inx + set_a 1,$ff + bit abs1,x ;82 - should set N (M7) & Z / clear V + tst_a 1,~fv + inx + set_a 1,$ff + bit abs1,x ;41 - should set V (M6) / clear NZ + tst_a 1,~fnz + inx + set_a $ff,$ff + bit abs1,x ;00 - should set Z / clear NV + tst_a $ff,~fnv + + set_a $ff,0 + bit #$00 ;00 - should set Z + tst_a $ff,fz + dex + set_a 1,0 + bit #$41 ;41 - should clear Z + tst_a 1,0 +; *** DEBUG INFO *** +; if it fails the previous test and your BIT # has set the V flag +; see http://forum.6502.org/viewtopic.php?f=2&t=2241&p=27243#p27239 +; why it shouldn't alter N or V flags on a BIT # + dex + set_a 1,0 + bit #$82 ;82 - should set Z + tst_a 1,fz + dex + set_a 1,0 + bit #$c3 ;c3 - should clear Z + tst_a 1,0 + + set_a 1,$ff + bit #$c3 ;c3 - clear Z + tst_a 1,~fz + inx + set_a 1,$ff + bit #$82 ;82 - should set Z + tst_a 1,$ff + inx + set_a 1,$ff + bit #$41 ;41 - should clear Z + tst_a 1,~fz + inx + set_a $ff,$ff + bit #$00 ;00 - should set Z + tst_a $ff,$ff + + cpx #3 + trap_ne ;x altered during test + cpy #$42 + trap_ne ;y altered during test + tsx + cpx #$ff + trap_ne ;sp push/pop mismatch + next_test + +; testing TRB, TSB - zp / abs + +trbt macro ;\1 = memory, \2 = flags + sty \1 + load_flag \2 + pha + lda zpt+1 + plp + trb \1 + php + cmp zpt+1 + trap_ne ;accu was changed + pla + pha + ora #fz ;mask Z + cmp_flag \2|fz + trap_ne ;flags changed except Z + pla + and #fz + cmp zpt+2 + trap_ne ;Z flag invalid + lda zpt+3 + cmp zpt + trap_ne ;altered bits in memory wrong + endm + +tsbt macro ;\1 = memory, \2 = flags + sty \1 + load_flag \2 + pha + lda zpt+1 + plp + tsb \1 + php + cmp zpt+1 + trap_ne ;accu was changed + pla + pha + ora #fz ;mask Z + cmp_flag \2|fz + trap_ne ;flags changed except Z + pla + and #fz + cmp zpt+2 + trap_ne ;Z flag invalid + lda zpt+4 + cmp zpt + trap_ne ;altered bits in memory wrong + endm + + ldx #$c0 + ldy #0 ;op1 - memory save + ; zpt ;op1 - memory modifiable + stz zpt+1 ;op2 - accu + ; zpt+2 ;and flags + ; zpt+3 ;memory after reset + ; zpt+4 ;memory after set + +tbt1 tya + and zpt+1 ;set Z by anding the 2 operands + php + pla + and #fz ;mask Z + sta zpt+2 + tya ;reset op1 bits by op2 + eor #$ff + ora zpt+1 + eor #$ff + sta zpt+3 + tya ;set op1 bits by op2 + ora zpt+1 + sta zpt+4 + + trbt zpt,$ff + trbt abst,$ff + trbt zpt,0 + trbt abst,0 + tsbt zpt,$ff + tsbt abst,$ff + tsbt zpt,0 + tsbt abst,0 + + iny ;iterate op1 + bne tbt3 + inc zpt+1 ;iterate op2 + beq tbt2 +tbt3 jmp tbt1 +tbt2 + cpx #$c0 + trap_ne ;x altered during test + tsx + cpx #$ff + trap_ne ;sp push/pop mismatch + next_test + + if rkwl_wdc_op = 1 +; testing RMB, SMB - zp +rmbt macro ;\1 = bitnum + lda #$ff + sta zpt + set_a $a5,0 + rmb \1,zpt + tst_a $a5,0 + lda zpt + cmp #$ff-(1<<\1) + trap_ne ;wrong bits set or cleared + lda #1<<\1 + sta zpt + set_a $5a,$ff + rmb \1,zpt + tst_a $5a,$ff + lda zpt + trap_ne ;wrong bits set or cleared + endm +smbt macro ;\1 = bitnum + lda #$ff-(1<<\1) + sta zpt + set_a $a5,0 + smb \1,zpt + tst_a $a5,0 + lda zpt + cmp #$ff + trap_ne ;wrong bits set or cleared + lda #0 + sta zpt + set_a $5a,$ff + smb \1,zpt + tst_a $5a,$ff + lda zpt + cmp #1<<\1 + trap_ne ;wrong bits set or cleared + endm + + ldx #$ba ;protect x & y + ldy #$d0 + rmbt 0 + rmbt 1 + rmbt 2 + rmbt 3 + rmbt 4 + rmbt 5 + rmbt 6 + rmbt 7 + smbt 0 + smbt 1 + smbt 2 + smbt 3 + smbt 4 + smbt 5 + smbt 6 + smbt 7 + cpx #$ba + trap_ne ;x altered during test + cpy #$d0 + trap_ne ;y altered during test + tsx + cpx #$ff + trap_ne ;sp push/pop mismatch + next_test + endif + +; testing CMP - (zp) + ldx #$de ;protect x & y + ldy #$ad + set_a $80,0 + cmp (ind1+8) + tst_a $80,fc + set_a $7f,0 + cmp (ind1+8) + tst_a $7f,fzc + set_a $7e,0 + cmp (ind1+8) + tst_a $7e,fn + set_a $80,$ff + cmp (ind1+8) + tst_a $80,~fnz + set_a $7f,$ff + cmp (ind1+8) + tst_a $7f,~fn + set_a $7e,$ff + cmp (ind1+8) + tst_a $7e,~fzc + cpx #$de + trap_ne ;x altered during test + cpy #$ad + trap_ne ;y altered during test + tsx + cpx #$ff + trap_ne ;sp push/pop mismatch + next_test + +; testing logical instructions - AND EOR ORA (zp) + ldx #$42 ;protect x & y + + ldy #0 ;AND + lda indAN ;set indirect address + sta zpt + lda indAN+1 + sta zpt+1 +tand1 + set_ay absANa,0 + and (zpt) + tst_ay absrlo,absflo,0 + inc zpt + iny + cpy #4 + bne tand1 + dey + dec zpt +tand2 + set_ay absANa,$ff + and (zpt) + tst_ay absrlo,absflo,$ff-fnz + dec zpt + dey + bpl tand2 + + ldy #0 ;EOR + lda indEO ;set indirect address + sta zpt + lda indEO+1 + sta zpt+1 +teor1 + set_ay absEOa,0 + eor (zpt) + tst_ay absrlo,absflo,0 + inc zpt + iny + cpy #4 + bne teor1 + dey + dec zpt +teor2 + set_ay absEOa,$ff + eor (zpt) + tst_ay absrlo,absflo,$ff-fnz + dec zpt + dey + bpl teor2 + + ldy #0 ;ORA + lda indOR ;set indirect address + sta zpt + lda indOR+1 + sta zpt+1 +tora1 + set_ay absORa,0 + ora (zpt) + tst_ay absrlo,absflo,0 + inc zpt + iny + cpy #4 + bne tora1 + dey + dec zpt +tora2 + set_ay absORa,$ff + ora (zpt) + tst_ay absrlo,absflo,$ff-fnz + dec zpt + dey + bpl tora2 + + cpx #$42 + trap_ne ;x altered during test + tsx + cpx #$ff + trap_ne ;sp push/pop mismatch + next_test + + if I_flag = 3 + cli + endif + +; full binary add/subtract test - (zp) only +; iterates through all combinations of operands and carry input +; uses increments/decrements to predict result & result flags + cld + ldx #ad2 ;for indexed test + ldy #$ff ;max range + lda #0 ;start with adding zeroes & no carry + sta adfc ;carry in - for diag + sta ad1 ;operand 1 - accumulator + sta ad2 ;operand 2 - memory or immediate + sta ada2 ;non zp + sta adrl ;expected result bits 0-7 + sta adrh ;expected result bit 8 (carry out) + lda #$ff ;complemented operand 2 for subtract + sta sb2 + sta sba2 ;non zp + lda #2 ;expected Z-flag + sta adrf +tadd clc ;test with carry clear + jsr chkadd + inc adfc ;now with carry + inc adrl ;result +1 + php ;save N & Z from low result + php + pla ;accu holds expected flags + and #$82 ;mask N & Z + plp + bne tadd1 + inc adrh ;result bit 8 - carry +tadd1 ora adrh ;merge C to expected flags + sta adrf ;save expected flags except overflow + sec ;test with carry set + jsr chkadd + dec adfc ;same for operand +1 but no carry + inc ad1 + bne tadd ;iterate op1 + lda #0 ;preset result to op2 when op1 = 0 + sta adrh + inc ada2 + inc ad2 + php ;save NZ as operand 2 becomes the new result + pla + and #$82 ;mask N00000Z0 + sta adrf ;no need to check carry as we are adding to 0 + dec sb2 ;complement subtract operand 2 + dec sba2 + lda ad2 + sta adrl + bne tadd ;iterate op2 + + cpx #ad2 + trap_ne ;x altered during test + cpy #$ff + trap_ne ;y altered during test + tsx + cpx #$ff + trap_ne ;sp push/pop mismatch + next_test + +; decimal add/subtract test +; *** WARNING - tests documented behavior only! *** +; only valid BCD operands are tested, the V flag is ignored +; although V is declared as beeing valid on the 65C02 it has absolutely +; no use in BCD math. No sign = no overflow! +; iterates through all valid combinations of operands and carry input +; uses increments/decrements to predict result & carry flag + sed + ldx #ad2 ;for indexed test + ldy #$ff ;max range + lda #$99 ;start with adding 99 to 99 with carry + sta ad1 ;operand 1 - accumulator + sta ad2 ;operand 2 - memory or immediate + sta ada2 ;non zp + sta adrl ;expected result bits 0-7 + lda #1 ;set carry in & out + sta adfc ;carry in - for diag + sta adrh ;expected result bit 8 (carry out) + lda #$81 ;set N & C (99 + 99 + C = 99 + C) + sta adrf + lda #0 ;complemented operand 2 for subtract + sta sb2 + sta sba2 ;non zp +tdad sec ;test with carry set + jsr chkdad + dec adfc ;now with carry clear + lda adrl ;decimal adjust result + bne tdad1 ;skip clear carry & preset result 99 (9A-1) + dec adrh + lda #$99 + sta adrl + bne tdad3 +tdad1 and #$f ;lower nibble mask + bne tdad2 ;no decimal adjust needed + dec adrl ;decimal adjust (?0-6) + dec adrl + dec adrl + dec adrl + dec adrl + dec adrl +tdad2 dec adrl ;result -1 +tdad3 php ;save valid flags + pla + and #$82 ;N-----Z- + ora adrh ;N-----ZC + sta adrf + clc ;test with carry clear + jsr chkdad + inc adfc ;same for operand -1 but with carry + lda ad1 ;decimal adjust operand 1 + beq tdad5 ;iterate operand 2 + and #$f ;lower nibble mask + bne tdad4 ;skip decimal adjust + dec ad1 ;decimal adjust (?0-6) + dec ad1 + dec ad1 + dec ad1 + dec ad1 + dec ad1 +tdad4 dec ad1 ;operand 1 -1 + jmp tdad ;iterate op1 + +tdad5 lda #$99 ;precharge op1 max + sta ad1 + lda ad2 ;decimal adjust operand 2 + beq tdad7 ;end of iteration + and #$f ;lower nibble mask + bne tdad6 ;skip decimal adjust + dec ad2 ;decimal adjust (?0-6) + dec ad2 + dec ad2 + dec ad2 + dec ad2 + dec ad2 + inc sb2 ;complemented decimal adjust for subtract (?9+6) + inc sb2 + inc sb2 + inc sb2 + inc sb2 + inc sb2 +tdad6 dec ad2 ;operand 2 -1 + inc sb2 ;complemented operand for subtract + lda sb2 + sta sba2 ;copy as non zp operand + lda ad2 + sta ada2 ;copy as non zp operand + sta adrl ;new result since op1+carry=00+carry +op2=op2 + php ;save flags + pla + and #$82 ;N-----Z- + ora #1 ;N-----ZC + sta adrf + inc adrh ;result carry + jmp tdad ;iterate op2 + +tdad7 cpx #ad2 + trap_ne ;x altered during test + cpy #$ff + trap_ne ;y altered during test + tsx + cpx #$ff + trap_ne ;sp push/pop mismatch + cld + + lda test_case + cmp #test_num + trap_ne ;previous test is out of sequence + lda #$f0 ;mark opcode testing complete + sta test_case + +; final RAM integrity test +; verifies that none of the previous tests has altered RAM outside of the +; designated write areas. + check_ram +; *** DEBUG INFO *** +; to debug checksum errors uncomment check_ram in the next_test macro to +; narrow down the responsible opcode. +; may give false errors when monitor, OS or other background activity is +; allowed during previous tests. + + +; S U C C E S S ************************************************ +; ------------- + success ;if you get here everything went well +; ------------- +; S U C C E S S ************************************************ + jmp start ;run again + +; core subroutine of the decimal add/subtract test +; *** WARNING - tests documented behavior only! *** +; only valid BCD operands are tested, V flag is ignored +; iterates through all valid combinations of operands and carry input +; uses increments/decrements to predict result & carry flag +chkdad +; decimal ADC / SBC zp + php ;save carry for subtract + lda ad1 + adc ad2 ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda ad1 + sbc sb2 ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + plp +; decimal ADC / SBC abs + php ;save carry for subtract + lda ad1 + adc ada2 ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda ad1 + sbc sba2 ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + plp +; decimal ADC / SBC # + php ;save carry for subtract + lda ad2 + sta ex_adci+1 ;set ADC # operand + lda ad1 + jsr ex_adci ;execute ADC # in RAM + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda sb2 + sta ex_sbci+1 ;set SBC # operand + lda ad1 + jsr ex_sbci ;execute SBC # in RAM + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + plp +; decimal ADC / SBC zp,x + php ;save carry for subtract + lda ad1 + adc 0,x ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda ad1 + sbc sb2-ad2,x ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + plp +; decimal ADC / SBC abs,x + php ;save carry for subtract + lda ad1 + adc ada2-ad2,x ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda ad1 + sbc sba2-ad2,x ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + plp +; decimal ADC / SBC abs,y + php ;save carry for subtract + lda ad1 + adc ada2-$ff,y ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda ad1 + sbc sba2-$ff,y ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + plp +; decimal ADC / SBC (zp,x) + php ;save carry for subtract + lda ad1 + adc (lo adi2-ad2,x) ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda ad1 + sbc (lo sbi2-ad2,x) ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + plp +; decimal ADC / SBC (abs),y + php ;save carry for subtract + lda ad1 + adc (adiy2),y ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda ad1 + sbc (sbiy2),y ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + plp +; decimal ADC / SBC (zp) + php ;save carry for subtract + lda ad1 + adc (adi2) ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda ad1 + sbc (sbi2) ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$83 ;mask N-----ZC + cmp adrf + trap_ne ;bad flags + plp + rts + +; core subroutine of the full binary add/subtract test +; iterates through all combinations of operands and carry input +; uses increments/decrements to predict result & result flags +chkadd lda adrf ;add V-flag if overflow + and #$83 ;keep N-----ZC / clear V + pha + lda ad1 ;test sign unequal between operands + eor ad2 + bmi ckad1 ;no overflow possible - operands have different sign + lda ad1 ;test sign equal between operands and result + eor adrl + bpl ckad1 ;no overflow occured - operand and result have same sign + pla + ora #$40 ;set V + pha +ckad1 pla + sta adrf ;save expected flags +; binary ADC / SBC (zp) + php ;save carry for subtract + lda ad1 + adc (adi2) ;perform add + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp + php ;save carry for next add + lda ad1 + sbc (sbi2) ;perform subtract + php + cmp adrl ;check result + trap_ne ;bad result + pla ;check flags + and #$c3 ;mask NV----ZC + cmp adrf + trap_ne ;bad flags + plp + rts + +; target for the jump indirect test +ji_adr dw test_ji + dw ji_ret + + dey + dey +test_ji + php ;either SP or Y count will fail, if we do not hit + dey + dey + dey + plp + trap_cs ;flags loaded? + trap_vs + trap_mi + trap_eq + cmp #'I' ;registers loaded? + trap_ne + cpx #'N' + trap_ne + cpy #('D'-3) + trap_ne + pha ;save a,x + txa + pha + tsx + cpx #$fd ;check SP + trap_ne + pla ;restore x + tax + set_stat $ff + pla ;restore a + inx ;return registers with modifications + eor #$aa ;N=1, V=1, Z=0, C=1 + jmp (ji_tab+2) + nop + nop + trap ;runover protection + +; target for the jump indirect test +jxi_adr dw trap_ind + dw trap_ind + dw test_jxi ;+4 + dw jxi_ret ;+6 + dw trap_ind + dw trap_ind + + dey + dey +test_jxi + php ;either SP or Y count will fail, if we do not hit + dey + dey + dey + plp + trap_cs ;flags loaded? + trap_vs + trap_mi + trap_eq + cmp #'X' ;registers loaded? + trap_ne + cpx #4 + trap_ne + cpy #('I'-3) + trap_ne + pha ;save a,x + txa + pha + tsx + cpx #$fd ;check SP + trap_ne + pla ;restore x + tax + set_stat $ff + pla ;restore a + inx ;return registers with modifications + inx + eor #$aa ;N=1, V=1, Z=0, C=1 + jmp (jxi_tab,x) + nop + nop + trap ;runover protection + +; JMP (abs,x) with bad x + nop + nop +trap_ind + nop + nop + trap ;near miss indexed indirect jump + +;trap in case of unexpected IRQ, NMI, BRK, RESET +nmi_trap + trap ;check stack for conditions at NMI +res_trap + trap ;unexpected RESET +irq_trap + php ;save decimal flag + tsx ;test break on stack + lda $102,x + and #break + trap_eq ;check stack for conditions at IRQ + if ROM_vectors = 1 + pla ;test decimal mode cleared + and #decmode + trap_ne ;decimal mode not cleared after BRK + plp ;pop saved flags + pla ;return address low + cmp #lo(brk_ret) + trap_ne ;unexpected BRK + pla ;return address high + cmp #hi(brk_ret) + trap_ne ;unexpected BRK + jmp brk_ret + else + trap_ne ;check stack for conditions at BRK + endif + + if report = 1 + include "report.i65" + endif + +;copy of data to initialize BSS segment + if load_data_direct != 1 +zp_init +zp1_ db $c3,$82,$41,0 ;test patterns for LDx BIT ROL ROR ASL LSR +zp7f_ db $7f ;test pattern for compare +;logical zeropage operands +zpOR_ db 0,$1f,$71,$80 ;test pattern for OR +zpAN_ db $0f,$ff,$7f,$80 ;test pattern for AND +zpEO_ db $ff,$0f,$8f,$8f ;test pattern for EOR +;indirect addressing pointers +ind1_ dw abs1 ;indirect pointer to pattern in absolute memory + dw abs1+1 + dw abs1+2 + dw abs1+3 + dw abs7f +inw1_ dw abs1-$f8 ;indirect pointer for wrap-test pattern +indt_ dw abst ;indirect pointer to store area in absolute memory + dw abst+1 + dw abst+2 + dw abst+3 +inwt_ dw abst-$f8 ;indirect pointer for wrap-test store +indAN_ dw absAN ;indirect pointer to AND pattern in absolute memory + dw absAN+1 + dw absAN+2 + dw absAN+3 +indEO_ dw absEO ;indirect pointer to EOR pattern in absolute memory + dw absEO+1 + dw absEO+2 + dw absEO+3 +indOR_ dw absOR ;indirect pointer to OR pattern in absolute memory + dw absOR+1 + dw absOR+2 + dw absOR+3 +;add/subtract indirect pointers +adi2_ dw ada2 ;indirect pointer to operand 2 in absolute memory +sbi2_ dw sba2 ;indirect pointer to complemented operand 2 (SBC) +adiy2_ dw ada2-$ff ;with offset for indirect indexed +sbiy2_ dw sba2-$ff +zp_end + if (zp_end - zp_init) != (zp_bss_end - zp_bss) + ;force assembler error if size is different + ERROR ERROR ERROR ;mismatch between bss and zeropage data + endif +data_init +ex_adc_ adc #0 ;execute immediate opcodes + rts +ex_sbc_ sbc #0 ;execute immediate opcodes + rts +abs1_ db $c3,$82,$41,0 ;test patterns for LDx BIT ROL ROR ASL LSR +abs7f_ db $7f ;test pattern for compare +;loads +fLDx_ db fn,fn,0,fz ;expected flags for load +;shifts +rASL_ ;expected result ASL & ROL -carry +rROL_ db $86,$04,$82,0 ; " +rROLc_ db $87,$05,$83,1 ;expected result ROL +carry +rLSR_ ;expected result LSR & ROR -carry +rROR_ db $61,$41,$20,0 ; " +rRORc_ db $e1,$c1,$a0,$80 ;expected result ROR +carry +fASL_ ;expected flags for shifts +fROL_ db fnc,fc,fn,fz ;no carry in +fROLc_ db fnc,fc,fn,0 ;carry in +fLSR_ +fROR_ db fc,0,fc,fz ;no carry in +fRORc_ db fnc,fn,fnc,fn ;carry in +;increments (decrements) +rINC_ db $7f,$80,$ff,0,1 ;expected result for INC/DEC +fINC_ db 0,fn,fn,fz,0 ;expected flags for INC/DEC +;logical memory operand +absOR_ db 0,$1f,$71,$80 ;test pattern for OR +absAN_ db $0f,$ff,$7f,$80 ;test pattern for AND +absEO_ db $ff,$0f,$8f,$8f ;test pattern for EOR +;logical accu operand +absORa_ db 0,$f1,$1f,0 ;test pattern for OR +absANa_ db $f0,$ff,$ff,$ff ;test pattern for AND +absEOa_ db $ff,$f0,$f0,$0f ;test pattern for EOR +;logical results +absrlo_ db 0,$ff,$7f,$80 +absflo_ db fz,fn,0,fn +data_end + if (data_end - data_init) != (data_bss_end - data_bss) + ;force assembler error if size is different + ERROR ERROR ERROR ;mismatch between bss and data + endif + +vec_init + dw nmi_trap + dw res_trap + dw irq_trap +vec_bss equ $fffa + endif ;end of RAM init data + +; code at end of image due to the need to add blank space as required + if ($ff & (ji_ret - * - 2)) < ($ff & (jxi_ret - * - 2)) +; JMP (abs) when $xxff and $xx00 are from same page + ds lo(ji_ret - * - 2) + nop + nop +ji_px nop ;low address byte matched with ji_ret + nop + trap ;jmp indirect page cross bug + +; JMP (abs,x) when $xxff and $xx00 are from same page + ds lo(jxi_ret - * - 2) + nop + nop +jxi_px nop ;low address byte matched with jxi_ret + nop + trap ;jmp indexed indirect page cross bug + else +; JMP (abs,x) when $xxff and $xx00 are from same page + ds lo(jxi_ret - * - 2) + nop + nop +jxi_px nop ;low address byte matched with jxi_ret + nop + trap ;jmp indexed indirect page cross bug + +; JMP (abs) when $xxff and $xx00 are from same page + ds lo(ji_ret - * - 2) + nop + nop +ji_px nop ;low address byte matched with ji_ret + nop + trap ;jmp indirect page cross bug + endif + + if (load_data_direct = 1) & (ROM_vectors = 1) + org $fffa ;vectors + dw nmi_trap + dw res_trap + dw irq_trap + endif + + end start diff --git a/M6502/test_M6502/roms/license.txt b/M6502/test_M6502/roms/license.txt new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/M6502/test_M6502/roms/license.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/M6502/test_M6502/roms/readme.txt b/M6502/test_M6502/roms/readme.txt new file mode 100644 index 0000000..a3e30cf --- /dev/null +++ b/M6502/test_M6502/roms/readme.txt @@ -0,0 +1,25 @@ +This is a set of functional tests for the 6502/65C02 type processors. + +The 6502_functional_test.a65 is an assembler sourcecode to test all valid +opcodes and addressing modes of the original NMOS 6502 cpu. + +The 65C02_extended_opcodes_test.a65c tests all additional opcodes of the +65C02 processor including undefined opcodes. + +The 6502_interrupt_test.a65 is a simple test to check the interrupt system +of both processors. A feedback register is required to inject IRQ and NMI +requests. + +Detailed information about how to configure, assemble and run the tests is +included in each source file. + +The tests have primarily been written to test my own ATMega16 6502 emulator +project. You can find it here: http://2m5.de/6502_Emu/index.htm + +A discussion about the tests can be found here: +http://forum.6502.org/viewtopic.php?f=2&t=2241 + +Good luck debugging your emulator, simulator, fpga core, discrete +logic implementation or whatever you have! + + diff --git a/M6502/test_M6502/roms/report.i65 b/M6502/test_M6502/roms/report.i65 new file mode 100644 index 0000000..06f8849 --- /dev/null +++ b/M6502/test_M6502/roms/report.i65 @@ -0,0 +1,179 @@ +;**** report 6502 funtional test errors to standard I/O **** +; +;this include file is part of the 6502 functional tests +;it is used when you configure report = 1 in the tests +; +;to adopt the standard output vectors of your test environment +;you must modify the rchar and rget subroutines in this include +; +;I/O hardware may have to be initialized in report_init + +;print message macro - \1 = message location +rprt macro + ldx #0 + lda \1 +loop\? + jsr rchar + inx + lda \1,x + bne loop\? + endm + +;initialize I/O as required (example: configure & enable ACIA) +report_init + ;nothing to initialize + rprt rmsg_start + rts + +;show stack (with saved registers), zeropage and absolute memory workspace +;after an error was trapped in the test program +report_error +;save registers + php + pha + txa + pha + tya + pha + cld +;show stack with index to registers at error + rprt rmsg_stack + tsx + inx + lda #1 ;address high + jsr rhex + txa ;address low + jsr rhex +rstack jsr rspace + lda $100,x ;stack data + jsr rhex + inx + bne rstack + jsr rcrlf ;new line +;show zero page workspace + lda #0 + jsr rhex + lda #zpt + tax + jsr rhex +rzp jsr rspace + lda 0,x + jsr rhex + inx + cpx #zp_bss + bne rzp + jsr rcrlf +;show absolute workspace + lda #hi(data_segment) + jsr rhex + lda #lo(data_segment) + jsr rhex + ldx #0 +rabs jsr rspace + lda data_segment,x + jsr rhex + inx + cpx #(data_bss-data_segment) + bne rabs +;ask to continue + rprt rmsg_cont +rerr1 jsr rget + cmp #'C' + bne rerr1 +;restore registers + pla + tay + pla + tax + pla + plp + rts + +;show test has ended, ask to repeat +report_success + if rep_int = 1 + rprt rmsg_priority + lda data_segment ;show interrupt sequence + jsr rhex + jsr rspace + lda data_segment+1 + jsr rhex + jsr rspace + lda data_segment+2 + jsr rhex + endif + rprt rmsg_success +rsuc1 jsr rget + cmp #'R' + bne rsuc1 + rts + +;input subroutine +;get a character from standard input +;adjust according to the needs in your test environment +rget ;get character in A +;rget1 +; lda $bff1 ;wait RDRF +; and #8 +; beq rget1 +;not a real ACIA - so RDRF is not checked + lda $bff0 ;read acia rx reg +;the load can be replaced by a call to a kernal routine +; jsr $ffcf ;example: CHRIN for a C64 + cmp #'a' ;lower case + bcc rget1 + and #$5f ;convert to upper case +rget1 rts + +;output subroutines +rcrlf lda #10 + jsr rchar + lda #13 + bne rchar + +rspace lda #' ' + bne rchar + +rhex pha ;report hex byte in A + lsr a ;high nibble first + lsr a + lsr a + lsr a + jsr rnib + pla ;now low nibble + and #$f + +rnib clc ;report nibble in A + adc #'0' ;make printable 0-9 + cmp #'9'+1 + bcc rchar + adc #6 ;make printable A-F + +;send a character to standard output +;adjust according to the needs in your test environment +;register X needs to be preserved! +rchar ;report character in A +; pha ;wait TDRF +;rchar1 lda $bff1 +; and #$10 +; beq rchar1 +; pla +;not a real ACIA - so TDRF is not checked + sta $bff0 ;write acia tx reg +;the store can be replaced by a call to a kernal routine +; jsr $ffd2 ;example: CHROUT for a C64 + rts + +rmsg_start + db 10,13,"Started testing",10,13,0 +rmsg_stack + db 10,13,"regs Y X A PS PCLPCH",10,13,0 +rmsg_cont + db 10,13,"press C to continue",10,13,0 +rmsg_success + db 10,13,"All tests completed, press R to repeat",10,13,0 + if rep_int = 1 +rmsg_priority + db 10,13,"interrupt sequence (NMI IRQ BRK) ",0 + endif + \ No newline at end of file diff --git a/M6502/test_M6502/stdafx.cpp b/M6502/test_M6502/stdafx.cpp new file mode 100644 index 0000000..9e1cf10 --- /dev/null +++ b/M6502/test_M6502/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// test_M6502.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/M6502/test_M6502/stdafx.h b/M6502/test_M6502/stdafx.h new file mode 100644 index 0000000..2fcbda5 --- /dev/null +++ b/M6502/test_M6502/stdafx.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +#include +#include +#include + +#include + +// Windows!! +#include diff --git a/M6502/test_M6502/test.cpp b/M6502/test_M6502/test.cpp new file mode 100644 index 0000000..64f68e6 --- /dev/null +++ b/M6502/test_M6502/test.cpp @@ -0,0 +1,22 @@ +#include "stdafx.h" +#include "TestHarness.h" +#include "Configuration.h" +#include "Board.h" + +#include + +int main(int argc, char* argv[]) { + + Configuration configuration; + +#ifdef _DEBUG + configuration.setDebugMode(true); + configuration.setProfileMode(true); +#endif + + EightBit::TestHarness harness(configuration); + harness.initialise(); + harness.runLoop(); + + return 0; +} \ No newline at end of file diff --git a/M6502/test_M6502/test_M6502.vcxproj b/M6502/test_M6502/test_M6502.vcxproj new file mode 100644 index 0000000..25f43c2 --- /dev/null +++ b/M6502/test_M6502/test_M6502.vcxproj @@ -0,0 +1,177 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {EF6F5840-328E-465A-A78A-E807BC9AEB4C} + Win32Proj + test_M6502 + + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + + + ..\inc;..\..\inc;C:\local\boost_1_64_0;$(IncludePath) + C:\local\boost_1_64_0\lib64-msvc-14.0;$(LibraryPath) + false + + + true + ..\inc;..\..\inc;C:\local\boost_1_64_0;$(IncludePath) + C:\local\boost_1_64_0\lib32-msvc-14.0;$(LibraryPath) + + + true + ..\inc;..\..\inc;C:\local\boost_1_64_0;$(IncludePath) + C:\local\boost_1_64_0\lib64-msvc-14.0;$(LibraryPath) + + + false + ..\inc;..\..\inc;C:\local\boost_1_64_0;$(IncludePath) + C:\local\boost_1_64_0\lib32-msvc-14.0;$(LibraryPath) + + + + Level3 + Use + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + false + AdvancedVectorExtensions + + + Console + true + true + 10000000 + true + + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + + + + + Use + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + 10000000 + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + + + + + Create + Create + Create + Create + + + + + + {a9c24bd9-0cb4-4c84-b09b-46b815f9da47} + + + {d8726a1b-bbfe-47ef-9860-26b90140ba66} + + + + + + \ No newline at end of file diff --git a/M6502/test_M6502/test_M6502.vcxproj.filters b/M6502/test_M6502/test_M6502.vcxproj.filters new file mode 100644 index 0000000..ee00c3a --- /dev/null +++ b/M6502/test_M6502/test_M6502.vcxproj.filters @@ -0,0 +1,38 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/README.md b/README.md index 6f74131..609e261 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,6 @@ Assortment of eight-bit processor emulators: * Intel 8080 +* Zilog Z80 +* Sharp LR35902 (Not working) +* Mos Technology 6502 diff --git a/Z80/fusetest_Z80/FuseTestRunner.cpp b/Z80/fusetest_Z80/FuseTestRunner.cpp index d64a3e9..1520e88 100644 --- a/Z80/fusetest_Z80/FuseTestRunner.cpp +++ b/Z80/fusetest_Z80/FuseTestRunner.cpp @@ -263,7 +263,7 @@ void Fuse::TestRunner::checkregisters() { << "**** R, Expected: " << EightBit::Disassembler::hex((uint8_t)expectedState.r) << ", Got: " - << EightBit::Disassembler::hex(m_cpu.REFRESH()) + << EightBit::Disassembler::hex((uint8_t)m_cpu.REFRESH()) << std::endl; } diff --git a/Z80/fusetest_Z80/fusetest_Z80.vcxproj b/Z80/fusetest_Z80/fusetest_Z80.vcxproj index 880f56e..d3f5c7c 100644 --- a/Z80/fusetest_Z80/fusetest_Z80.vcxproj +++ b/Z80/fusetest_Z80/fusetest_Z80.vcxproj @@ -24,7 +24,6 @@ Win32Proj fuse_test_Z80 fusetest_Z80 - 8.1 diff --git a/Z80/inc/Z80.h b/Z80/inc/Z80.h index 545c0f8..f0fcd52 100644 --- a/Z80/inc/Z80.h +++ b/Z80/inc/Z80.h @@ -9,6 +9,47 @@ namespace EightBit { class Z80 : public IntelProcessor { public: + struct refresh_t { + + bool high : 1; + uint8_t variable : 7; + + refresh_t(uint8_t value) + : high((value & Bit7) != 0), + variable(value & Mask7) + { } + + operator uint8_t() const { + return (high << 7) | variable; + } + + refresh_t& operator++() { + ++variable; + return *this; + } + }; + + struct opcode_decoded_t { + + int x; + int y; + int z; + int p; + int q; + + opcode_decoded_t() { + x = y = z = p = q = 0; + } + + opcode_decoded_t(uint8_t opcode) { + x = (opcode & 0b11000000) >> 6; // 0 - 3 + y = (opcode & 0b00111000) >> 3; // 0 - 7 + z = (opcode & 0b00000111); // 0 - 7 + p = (y & 0b110) >> 1; // 0 - 3 + q = (y & 1); // 0 - 1 + } + }; + enum StatusBits { SF = Bit7, ZF = Bit6, @@ -63,7 +104,7 @@ namespace EightBit { uint8_t& IYH() { return IY().high; } uint8_t& IYL() { return IY().low; } - uint8_t& REFRESH() { return m_refresh; } + refresh_t& REFRESH() { return m_refresh; } uint8_t& IV() { return iv; } int& IM() { return m_interruptMode; } bool& IFF1() { return m_iff1; } @@ -96,7 +137,8 @@ namespace EightBit { register16_t m_ix; register16_t m_iy; - uint8_t m_refresh; + refresh_t m_refresh; + uint8_t iv; int m_interruptMode; bool m_iff1; @@ -112,21 +154,19 @@ namespace EightBit { int8_t m_displacement; bool m_displaced; + std::array m_decodedOpcodes; + int fetchExecute() { M1() = true; return execute(fetchByte()); } - void incrementRefresh() { - REFRESH() = (REFRESH() & Bit7) | (REFRESH() + 1) & Mask7; - } - uint8_t& DISPLACED() { m_memory.ADDRESS().word = MEMPTR().word = (m_prefixDD ? IX() : IY()).word + m_displacement; return m_memory.reference(); } - uint8_t& R(int r) { + uint8_t& R(int r, uint8_t& a) { switch (r) { case 0: return B(); @@ -141,19 +181,21 @@ namespace EightBit { case 5: return HL2().low; case 6: - if (m_displaced) { - m_displacement = fetchByte(); - return DISPLACED(); + if (!m_displaced) { + m_memory.ADDRESS() = HL(); + return m_memory.reference(); } - m_memory.ADDRESS() = HL(); - return m_memory.reference(); + m_displacement = fetchByte(); + return DISPLACED(); case 7: - return A(); + return a; + default: + __assume(0); } throw std::logic_error("Unhandled registry mechanism"); } - uint8_t& R2(int r) { + uint8_t& R2(int r, uint8_t& a) { switch (r) { case 0: return B(); @@ -171,12 +213,16 @@ namespace EightBit { m_memory.ADDRESS() = HL(); return m_memory.reference(); case 7: - return A(); + return a; + default: + __assume(0); } throw std::logic_error("Unhandled registry mechanism"); } register16_t& RP(int rp) { + __assume(rp < 4); + __assume(rp >= 0); switch (rp) { case 3: return SP(); @@ -188,16 +234,17 @@ namespace EightBit { } register16_t& HL2() { - if (m_displaced) { - if (m_prefixDD) - return IX(); - else if (m_prefixFD) - return IY(); - } - return HL(); + if (!m_displaced) + return HL(); + if (m_prefixDD) + return IX(); + // Must be FD prefix + return IY(); } register16_t& RP2(int rp) { + __assume(rp < 4); + __assume(rp >= 0); switch (rp) { case 3: return AF(); @@ -208,21 +255,6 @@ namespace EightBit { } } - void addViaMemptr(register16_t& hl, register16_t operand) { - MEMPTR().word = hl.word + 1; - add(hl, operand); - } - - void sbcViaMemptr(register16_t& hl, register16_t operand) { - MEMPTR().word = hl.word + 1; - sbc(hl, operand); - } - - void adcViaMemptr(register16_t& hl, register16_t operand) { - MEMPTR().word = hl.word + 1; - adc(hl, operand); - } - static void adjustHalfCarryAdd(uint8_t& f, uint8_t before, uint8_t value, int calculation) { setFlag(f, HC, calculateHalfCarryAdd(before, value, calculation)); } @@ -249,98 +281,96 @@ namespace EightBit { setFlag(f, VF, overflow); } - void executeCB(int x, int y, int z, int p, int q); + static void subtract(uint8_t& f, uint8_t& operand, uint8_t value, int carry = 0); + + void executeCB(int x, int y, int z); void executeED(int x, int y, int z, int p, int q); void executeOther(int x, int y, int z, int p, int q); - void postIncrement(uint8_t& f, uint8_t value); - void postDecrement(uint8_t& f, uint8_t value); + static void postIncrement(uint8_t& f, uint8_t value); + static void postDecrement(uint8_t& f, uint8_t value); void retn(); void reti(); - bool jrConditionalFlag(int flag); - bool returnConditionalFlag(int flag); - bool jumpConditionalFlag(int flag); - bool callConditionalFlag(int flag); + bool jrConditionalFlag(uint8_t& f, int flag); + bool returnConditionalFlag(uint8_t& f, int flag); + bool jumpConditionalFlag(uint8_t& f, int flag); + bool callConditionalFlag(uint8_t& f, int flag); - void sbc(register16_t& operand, register16_t value); - void adc(register16_t& operand, register16_t value); + void sbc(uint8_t& f, register16_t& operand, register16_t value); + void adc(uint8_t& f, register16_t& operand, register16_t value); + void add(uint8_t& f, register16_t& operand, register16_t value); - void add(register16_t& operand, register16_t value); + static void add(uint8_t& f, uint8_t& operand, uint8_t value, int carry = 0); + static void adc(uint8_t& f, uint8_t& operand, uint8_t value); + static void sub(uint8_t& f, uint8_t& operand, uint8_t value, int carry = 0); + static void sbc(uint8_t& f, uint8_t& operand, uint8_t value); + static void andr(uint8_t& f, uint8_t& operand, uint8_t value); + static void xorr(uint8_t& f, uint8_t& operand, uint8_t value); + static void orr(uint8_t& f, uint8_t& operand, uint8_t value); + static void compare(uint8_t& f, uint8_t check, uint8_t value); - void add(uint8_t& operand, uint8_t value, int carry = 0); - void adc(uint8_t& operand, uint8_t value); - void sub(uint8_t& operand, uint8_t value, int carry = 0); - void sbc(uint8_t& operand, uint8_t value); - void andr(uint8_t& operand, uint8_t value); - void xorr(uint8_t& operand, uint8_t value); - void orr(uint8_t& operand, uint8_t value); - void compare(uint8_t value); + static uint8_t& rlc(uint8_t& f, uint8_t& operand); + static uint8_t& rrc(uint8_t& f, uint8_t& operand); + static uint8_t& rl(uint8_t& f, uint8_t& operand); + static uint8_t& rr(uint8_t& f, uint8_t& operand); + static uint8_t& sla(uint8_t& f, uint8_t& operand); + static uint8_t& sra(uint8_t& f, uint8_t& operand); + static uint8_t& sll(uint8_t& f, uint8_t& operand); + static uint8_t& srl(uint8_t& f, uint8_t& operand); - uint8_t& rlc(uint8_t& operand); - uint8_t& rrc(uint8_t& operand); - uint8_t& rl(uint8_t& operand); - uint8_t& rr(uint8_t& operand); - uint8_t& sla(uint8_t& operand); - uint8_t& sra(uint8_t& operand); - uint8_t& sll(uint8_t& operand); - uint8_t& srl(uint8_t& operand); + static uint8_t& bit(uint8_t& f, int n, uint8_t& operand); + static uint8_t& res(int n, uint8_t& operand); + static uint8_t& set(int nit, uint8_t& operand); - uint8_t& bit(int n, uint8_t& operand); - uint8_t& res(int n, uint8_t& operand); - uint8_t& set(int nit, uint8_t& operand); + static void daa(uint8_t& a, uint8_t& f); - void daa(); - - void scf(); - void ccf(); - void cpl(); + static void scf(uint8_t a, uint8_t& f); + static void ccf(uint8_t a, uint8_t& f); + static void cpl(uint8_t& a, uint8_t& f); void xhtl(register16_t& operand); - void xhtl(); - void blockCompare(); + void blockCompare(uint8_t a, uint8_t& f); - void cpi(); - bool cpir(); + void cpi(uint8_t a, uint8_t& f); + bool cpir(uint8_t a, uint8_t& f); - void cpd(); - bool cpdr(); + void cpd(uint8_t a, uint8_t& f); + bool cpdr(uint8_t a, uint8_t& f); - void blockLoad(register16_t source, register16_t destination); + void blockLoad(uint8_t a, uint8_t& f, register16_t source, register16_t destination); - void ldi(); - bool ldir(); + void ldi(uint8_t a, uint8_t& f); + bool ldir(uint8_t a, uint8_t& f); - void ldd(); - bool lddr(); + void ldd(uint8_t a, uint8_t& f); + bool lddr(uint8_t a, uint8_t& f); - void ini(); - bool inir(); + void ini(uint8_t& f); + bool inir(uint8_t& f); - void ind(); - bool indr(); + void ind(uint8_t& f); + bool indr(uint8_t& f); - void blockOut(); + void blockOut(uint8_t& f); - void outi(); - bool otir(); + void outi(uint8_t& f); + bool otir(uint8_t& f); - void outd(); - bool otdr(); + void outd(uint8_t& f); + bool otdr(uint8_t& f); - void neg(); + static void neg(uint8_t& a, uint8_t& f); - void rrd(); - void rld(); + void rrd(uint8_t& a, uint8_t& f); + void rld(uint8_t& a, uint8_t& f); - void writePort() { - m_ports.write(m_memory.ADDRESS().low, m_memory.DATA()); - } + void writePort(uint8_t port, uint8_t a); + void writePort(); - void readPort() { - m_memory.placeDATA(m_ports.read(m_memory.ADDRESS().low)); - } + void readPort(uint8_t port, uint8_t& a); + void readPort(); }; } \ No newline at end of file diff --git a/Z80/src/Disassembler.cpp b/Z80/src/Disassembler.cpp index 6524d02..d738a75 100644 --- a/Z80/src/Disassembler.cpp +++ b/Z80/src/Disassembler.cpp @@ -31,7 +31,7 @@ std::string EightBit::Disassembler::state(Z80& cpu) { auto l = cpu.L(); auto i = cpu.IV(); - auto r = cpu.REFRESH(); + uint8_t r = cpu.REFRESH(); auto im = cpu.IM(); diff --git a/Z80/src/Z80.cpp b/Z80/src/Z80.cpp index 6502a15..5d037cd 100644 --- a/Z80/src/Z80.cpp +++ b/Z80/src/Z80.cpp @@ -34,6 +34,10 @@ void EightBit::Z80::initialise() { IntelProcessor::initialise(); + for (int i = 0; i < 0x100; ++i) { + m_decodedOpcodes[i] = i; + } + IM() = 0; AF().word = 0xffff; @@ -126,46 +130,50 @@ void EightBit::Z80::postDecrement(uint8_t& f, uint8_t value) { #pragma region PC manipulation: call/ret/jp/jr -bool EightBit::Z80::jrConditionalFlag(int flag) { +bool EightBit::Z80::jrConditionalFlag(uint8_t& f, int flag) { switch (flag) { case 0: // NZ - return jrConditional(!(F() & ZF)); + return jrConditional(!(f & ZF)); case 1: // Z - return jrConditional(F() & ZF); + return jrConditional(f & ZF); case 2: // NC - return jrConditional(!(F() & CF)); + return jrConditional(!(f & CF)); case 3: // C - return jrConditional(F() & CF); + return jrConditional(f & CF); case 4: // PO - return jrConditional(!(F() & PF)); + return jrConditional(!(f & PF)); case 5: // PE - return jrConditional(F() & PF); + return jrConditional(f & PF); case 6: // P - return jrConditional(!(F() & SF)); + return jrConditional(!(f & SF)); case 7: // M - return jrConditional(F() & SF); + return jrConditional(f & SF); + default: + __assume(0); } throw std::logic_error("Unhandled JR conditional"); } -bool EightBit::Z80::jumpConditionalFlag(int flag) { +bool EightBit::Z80::jumpConditionalFlag(uint8_t& f, int flag) { switch (flag) { case 0: // NZ - return jumpConditional(!(F() & ZF)); + return jumpConditional(!(f & ZF)); case 1: // Z - return jumpConditional(F() & ZF); + return jumpConditional(f & ZF); case 2: // NC - return jumpConditional(!(F() & CF)); + return jumpConditional(!(f & CF)); case 3: // C - return jumpConditional(F() & CF); + return jumpConditional(f & CF); case 4: // PO - return jumpConditional(!(F() & PF)); + return jumpConditional(!(f & PF)); case 5: // PE - return jumpConditional(F() & PF); + return jumpConditional(f & PF); case 6: // P - return jumpConditional(!(F() & SF)); + return jumpConditional(!(f & SF)); case 7: // M - return jumpConditional(F() & SF); + return jumpConditional(f & SF); + default: + __assume(0); } throw std::logic_error("Unhandled JP conditional"); } @@ -179,46 +187,50 @@ void EightBit::Z80::reti() { retn(); } -bool EightBit::Z80::returnConditionalFlag(int flag) { +bool EightBit::Z80::returnConditionalFlag(uint8_t& f, int flag) { switch (flag) { case 0: // NZ - return returnConditional(!(F() & ZF)); + return returnConditional(!(f & ZF)); case 1: // Z - return returnConditional(F() & ZF); + return returnConditional(f & ZF); case 2: // NC - return returnConditional(!(F() & CF)); + return returnConditional(!(f & CF)); case 3: // C - return returnConditional(F() & CF); + return returnConditional(f & CF); case 4: // PO - return returnConditional(!(F() & PF)); + return returnConditional(!(f & PF)); case 5: // PE - return returnConditional(F() & PF); + return returnConditional(f & PF); case 6: // P - return returnConditional(!(F() & SF)); + return returnConditional(!(f & SF)); case 7: // M - return returnConditional(F() & SF); + return returnConditional(f & SF); + default: + __assume(0); } throw std::logic_error("Unhandled RET conditional"); } -bool EightBit::Z80::callConditionalFlag(int flag) { +bool EightBit::Z80::callConditionalFlag(uint8_t& f, int flag) { switch (flag) { case 0: // NZ - return callConditional(!(F() & ZF)); + return callConditional(!(f & ZF)); case 1: // Z - return callConditional(F() & ZF); + return callConditional(f & ZF); case 2: // NC - return callConditional(!(F() & CF)); + return callConditional(!(f & CF)); case 3: // C - return callConditional(F() & CF); + return callConditional(f & CF); case 4: // PO - return callConditional(!(F() & PF)); + return callConditional(!(f & PF)); case 5: // PE - return callConditional(F() & PF); + return callConditional(f & PF); case 6: // P - return callConditional(!(F() & SF)); + return callConditional(!(f & SF)); case 7: // M - return callConditional(F() & SF); + return callConditional(f & SF); + default: + __assume(0); } throw std::logic_error("Unhandled CALL conditional"); } @@ -227,75 +239,73 @@ bool EightBit::Z80::callConditionalFlag(int flag) { #pragma region 16-bit arithmetic -void EightBit::Z80::sbc(register16_t& operand, register16_t value) { +void EightBit::Z80::sbc(uint8_t& f, register16_t& operand, register16_t value) { - auto& f = F(); + MEMPTR() = operand; - auto before = operand; + const auto beforeNegative = MEMPTR().high & SF; + const auto valueNegative = value.high & SF; - auto beforeNegative = before.high & SF; - auto valueNegative = value.high & SF; + const auto result = MEMPTR().word - value.word - (f & CF); + operand.word = result; - auto result = before.word - value.word - (f & CF); + const auto afterNegative = operand.high & SF; + + setFlag(f, SF, afterNegative); + clearFlag(f, ZF, operand.word); + adjustHalfCarrySub(f, MEMPTR().high, value.high, operand.high); + adjustOverflowSub(f, beforeNegative, valueNegative, afterNegative); + setFlag(f, NF); + setFlag(f, CF, result & Bit16); + adjustXY(f, operand.high); + + MEMPTR().word++; +} + +void EightBit::Z80::adc(uint8_t& f, register16_t& operand, register16_t value) { + + MEMPTR() = operand; + + const auto beforeNegative = MEMPTR().high & SF; + const auto valueNegative = value.high & SF; + + const auto result = MEMPTR().word + value.word + (f & CF); operand.word = result; auto afterNegative = operand.high & SF; setFlag(f, SF, afterNegative); clearFlag(f, ZF, operand.word); - adjustHalfCarrySub(f, before.high, value.high, operand.high); - adjustOverflowSub(f, beforeNegative, valueNegative, afterNegative); - setFlag(f, NF); - setFlag(f, CF, result & Bit16); - adjustXY(f, operand.high); -} - -void EightBit::Z80::adc(register16_t& operand, register16_t value) { - - auto& f = F(); - - auto before = operand; - - auto beforeNegative = before.high & SF; - auto valueNegative = value.high & SF; - - auto result = before.word + value.word + (f & CF); - operand.word = result; - - auto afterNegative = operand.high & SF; - - setFlag(f, SF, afterNegative); - clearFlag(f, ZF, result); - adjustHalfCarryAdd(f, before.high, value.high, operand.high); + adjustHalfCarryAdd(f, MEMPTR().high, value.high, operand.high); adjustOverflowAdd(f, beforeNegative, valueNegative, afterNegative); clearFlag(f, NF); setFlag(f, CF, result & Bit16); adjustXY(f, operand.high); + + MEMPTR().word++; } -void EightBit::Z80::add(register16_t& operand, register16_t value) { +void EightBit::Z80::add(uint8_t& f, register16_t& operand, register16_t value) { - auto& f = F(); + MEMPTR() = operand; - auto before = operand; - - auto result = before.word + value.word; + const auto result = MEMPTR().word + value.word; operand.word = result; clearFlag(f, NF); setFlag(f, CF, result & Bit16); - adjustHalfCarryAdd(f, before.high, value.high, operand.high); + adjustHalfCarryAdd(f, MEMPTR().high, value.high, operand.high); adjustXY(f, operand.high); + + MEMPTR().word++; } #pragma endregion 16-bit arithmetic #pragma region ALU -void EightBit::Z80::add(uint8_t& operand, uint8_t value, int carry) { - - auto& f = F(); +void EightBit::Z80::add(uint8_t& f, uint8_t& operand, uint8_t value, int carry) { register16_t result; result.word = operand + value + carry; @@ -310,13 +320,11 @@ void EightBit::Z80::add(uint8_t& operand, uint8_t value, int carry) { adjustSZXY(f, operand); } -void EightBit::Z80::adc(uint8_t& operand, uint8_t value) { - add(operand, value, F() & CF); +void EightBit::Z80::adc(uint8_t& f, uint8_t& operand, uint8_t value) { + add(f, operand, value, f & CF); } -void EightBit::Z80::sub(uint8_t& operand, uint8_t value, int carry) { - - auto& f = F(); +void EightBit::Z80::subtract(uint8_t& f, uint8_t& operand, uint8_t value, int carry) { register16_t result; result.word = operand - value - carry; @@ -328,133 +336,107 @@ void EightBit::Z80::sub(uint8_t& operand, uint8_t value, int carry) { setFlag(f, NF); setFlag(f, CF, result.word & Bit8); - adjustSZXY(f, operand); + adjustSZ(f, operand); } -void EightBit::Z80::sbc(uint8_t& operand, uint8_t value) { - sub(operand, value, F() & CF); +void EightBit::Z80::sub(uint8_t& f, uint8_t& operand, uint8_t value, int carry) { + subtract(f, operand, value, carry); + adjustXY(f, operand); } -void EightBit::Z80::andr(uint8_t& operand, uint8_t value) { - auto& f = F(); - operand &= value; +void EightBit::Z80::sbc(uint8_t& f, uint8_t& operand, uint8_t value) { + sub(f, operand, value, f & CF); +} + +void EightBit::Z80::andr(uint8_t& f, uint8_t& operand, uint8_t value) { setFlag(f, HC); clearFlag(f, CF | NF); - adjustSZPXY(f, operand); + adjustSZPXY(f, operand &= value); } -void EightBit::Z80::xorr(uint8_t& operand, uint8_t value) { - auto& f = F(); - operand ^= value; +void EightBit::Z80::xorr(uint8_t& f, uint8_t& operand, uint8_t value) { clearFlag(f, HC | CF | NF); - adjustSZPXY(f, operand); + adjustSZPXY(f, operand ^= value); } -void EightBit::Z80::orr(uint8_t& operand, uint8_t value) { - auto& f = F(); - operand |= value; +void EightBit::Z80::orr(uint8_t& f, uint8_t& operand, uint8_t value) { clearFlag(f, HC | CF | NF); - adjustSZPXY(f, operand); + adjustSZPXY(f, operand |= value); } -void EightBit::Z80::compare(uint8_t value) { - auto check = A(); - sub(check, value); - adjustXY(F(), value); +void EightBit::Z80::compare(uint8_t& f, uint8_t check, uint8_t value) { + subtract(f, check, value); + adjustXY(f, value); } #pragma endregion ALU #pragma region Shift and rotate -uint8_t& EightBit::Z80::rlc(uint8_t& operand) { - auto& f = F(); - auto carry = operand & Bit7; - operand <<= 1; - carry ? operand |= Bit0 : operand &= ~Bit0; - setFlag(f, CF, carry); +uint8_t& EightBit::Z80::rlc(uint8_t& f, uint8_t& operand) { clearFlag(f, NF | HC); + setFlag(f, CF, operand & Bit7); + operand = _rotl8(operand, 1); adjustXY(f, operand); return operand; } -uint8_t& EightBit::Z80::rrc(uint8_t& operand) { - auto& f = F(); - auto carry = operand & Bit0; - operand >>= 1; - carry ? operand |= Bit7 : operand &= ~Bit7; - setFlag(f, CF, carry); +uint8_t& EightBit::Z80::rrc(uint8_t& f, uint8_t& operand) { clearFlag(f, NF | HC); + setFlag(f, CF, operand & Bit0); + operand = _rotr8(operand, 1); adjustXY(f, operand); return operand; } -uint8_t& EightBit::Z80::rl(uint8_t& operand) { - auto& f = F(); - auto oldCarry = f & CF; - auto newCarry = operand & Bit7; - operand <<= 1; - oldCarry ? operand |= Bit0 : operand &= ~Bit0; - setFlag(f, CF, newCarry); +uint8_t& EightBit::Z80::rl(uint8_t& f, uint8_t& operand) { clearFlag(f, NF | HC); + const auto carry = f & CF; + setFlag(f, CF, operand & Bit7); + operand = (operand << 1) | carry; adjustXY(f, operand); return operand; } -uint8_t& EightBit::Z80::rr(uint8_t& operand) { - auto& f = F(); - auto oldCarry = f & CF; - auto newCarry = operand & Bit0; - operand >>= 1; - operand |= oldCarry << 7; - setFlag(f, CF, newCarry); +uint8_t& EightBit::Z80::rr(uint8_t& f, uint8_t& operand) { clearFlag(f, NF | HC); + const auto carry = f & CF; + setFlag(f, CF, operand & Bit0); + operand = (operand >> 1) | (carry << 7); adjustXY(f, operand); return operand; } // -uint8_t& EightBit::Z80::sla(uint8_t& operand) { - auto& f = F(); - auto newCarry = operand & Bit7; +uint8_t& EightBit::Z80::sla(uint8_t& f, uint8_t& operand) { + clearFlag(f, NF | HC); + setFlag(f, CF, operand & Bit7); operand <<= 1; - setFlag(f, CF, newCarry); - clearFlag(f, NF | HC); adjustXY(f, operand); return operand; } -uint8_t& EightBit::Z80::sra(uint8_t& operand) { - auto& f = F(); - auto new7 = operand & Bit7; - auto newCarry = operand & Bit0; - operand >>= 1; - operand |= new7; - setFlag(f, CF, newCarry); +uint8_t& EightBit::Z80::sra(uint8_t& f, uint8_t& operand) { clearFlag(f, NF | HC); + setFlag(f, CF, operand & Bit0); + operand = (operand >> 1) | operand & Bit7; adjustXY(f, operand); return operand; } -uint8_t& EightBit::Z80::sll(uint8_t& operand) { - auto& f = F(); - auto newCarry = operand & Bit7; - operand <<= 1; - operand |= 1; - setFlag(f, CF, newCarry); +uint8_t& EightBit::Z80::sll(uint8_t& f, uint8_t& operand) { clearFlag(f, NF | HC); + setFlag(f, CF, operand & Bit7); + operand = (operand << 1) | Bit0; adjustXY(f, operand); return operand; } -uint8_t& EightBit::Z80::srl(uint8_t& operand) { - auto& f = F(); - auto newCarry = operand & Bit0; - operand >>= 1; - operand &= ~Bit7; // clear bit 7 - setFlag(f, CF, newCarry); +uint8_t& EightBit::Z80::srl(uint8_t& f, uint8_t& operand) { clearFlag(f, NF | HC); + setFlag(f, CF, operand & Bit0); + operand = (operand >> 1) & ~Bit7; adjustXY(f, operand); setFlag(f, ZF, operand); return operand; @@ -464,93 +446,85 @@ uint8_t& EightBit::Z80::srl(uint8_t& operand) { #pragma region BIT/SET/RES -uint8_t& EightBit::Z80::bit(int n, uint8_t& operand) { - auto& f = F(); - auto carry = f & CF; - uint8_t discarded = operand; - andr(discarded, 1 << n); +uint8_t& EightBit::Z80::bit(uint8_t& f, int n, uint8_t& operand) { + setFlag(f, HC); + clearFlag(f, NF); + const auto discarded = operand & (1 << n); + adjustSZXY(f, discarded); clearFlag(f, PF, discarded); - setFlag(f, CF, carry); return operand; } uint8_t& EightBit::Z80::res(int n, uint8_t& operand) { - auto bit = 1 << n; - operand &= ~bit; - return operand; + return operand &= ~(1 << n); } uint8_t& EightBit::Z80::set(int n, uint8_t& operand) { - auto bit = 1 << n; - operand |= bit; - return operand; + return operand |= (1 << n); } #pragma endregion BIT/SET/RES #pragma region Miscellaneous instructions -void EightBit::Z80::neg() { - auto& a = A(); - auto& f = F(); - auto original = a; - a = 0; - sub(a, original); - setFlag(f, PF, original == Bit7); - setFlag(f, CF, original); +void EightBit::Z80::neg(uint8_t& a, uint8_t& f) { + + setFlag(f, PF, a == Bit7); + setFlag(f, CF, a); + setFlag(f, NF); + + const auto original = a; + + a = (~a + 1); // two's complement + + adjustHalfCarrySub(f, 0U, original, a); + adjustOverflowSub(f, 0U, original, a); + + adjustSZXY(f, a); } -void EightBit::Z80::daa() { +void EightBit::Z80::daa(uint8_t& a, uint8_t& f) { - auto& acc = A(); - auto& f = F(); + auto updated = a; - auto a = acc; - - auto lowAdjust = (f & HC) | (lowNibble(acc) > 9); - auto highAdjust = (f & CF) | (acc > 0x99); + auto lowAdjust = (f & HC) | (lowNibble(a) > 9); + auto highAdjust = (f & CF) | (a > 0x99); if (f & NF) { if (lowAdjust) - a -= 6; + updated -= 6; if (highAdjust) - a -= 0x60; + updated -= 0x60; } else { if (lowAdjust) - a += 6; + updated += 6; if (highAdjust) - a += 0x60; + updated += 0x60; } - f = (f & (CF | NF)) | (acc > 0x99) | ((acc ^ a) & HC); + f = (f & (CF | NF)) | (a > 0x99) | ((a ^ updated) & HC); + a = updated; adjustSZPXY(f, a); - - acc = a; } -void EightBit::Z80::cpl() { - auto& a = A(); - auto& f = F(); - a = ~a; - adjustXY(f, A()); +void EightBit::Z80::cpl(uint8_t& a, uint8_t& f) { setFlag(f, HC | NF); + adjustXY(f, a = ~a); } -void EightBit::Z80::scf() { - auto& f = F(); +void EightBit::Z80::scf(uint8_t a, uint8_t& f) { setFlag(f, CF); - adjustXY(f, A()); clearFlag(f, HC | NF); + adjustXY(f, a); } -void EightBit::Z80::ccf() { - auto& f = F(); - auto carry = f & CF; +void EightBit::Z80::ccf(uint8_t a, uint8_t& f) { + clearFlag(f, NF); + const auto carry = f & CF; setFlag(f, HC, carry); clearFlag(f, CF, carry); - clearFlag(f, NF); - adjustXY(f, A()); + adjustXY(f, a); } void EightBit::Z80::xhtl(register16_t& operand) { @@ -564,24 +538,17 @@ void EightBit::Z80::xhtl(register16_t& operand) { operand.high = MEMPTR().high; } -void EightBit::Z80::xhtl() { - xhtl(HL2()); -} - #pragma endregion Miscellaneous instructions #pragma region Block instructions #pragma region Block compare instructions -void EightBit::Z80::blockCompare() { - - const auto& a = A(); - auto& f = F(); +void EightBit::Z80::blockCompare(uint8_t a, uint8_t& f) { m_memory.ADDRESS() = HL(); - auto value = m_memory.reference(); + const auto value = m_memory.reference(); uint8_t result = a - value; setFlag(f, PF, --BC().word); @@ -590,38 +557,37 @@ void EightBit::Z80::blockCompare() { adjustHalfCarrySub(f, a, value, result); setFlag(f, NF); - if (f & HC) - result -= 1; + result -= ((f & HC) >> 4); setFlag(f, YF, result & Bit1); setFlag(f, XF, result & Bit3); } -void EightBit::Z80::cpi() { - blockCompare(); +void EightBit::Z80::cpi(uint8_t a, uint8_t& f) { + blockCompare(a, f); HL().word++; MEMPTR().word++; } -void EightBit::Z80::cpd() { - blockCompare(); +void EightBit::Z80::cpd(uint8_t a, uint8_t& f) { + blockCompare(a, f); HL().word--; MEMPTR().word--; } -bool EightBit::Z80::cpir() { - cpi(); +bool EightBit::Z80::cpir(uint8_t a, uint8_t& f) { + cpi(a, f); MEMPTR() = PC(); - auto again = (F() & PF) && !(F() & ZF); // See CPI + auto again = (f & PF) && !(f & ZF); // See CPI if (again) MEMPTR().word--; return again; } -bool EightBit::Z80::cpdr() { - cpd(); +bool EightBit::Z80::cpdr(uint8_t a, uint8_t& f) { + cpd(a, f); MEMPTR().word = PC().word - 1; - auto again = (F() & PF) && !(F() & ZF); // See CPD + auto again = (f & PF) && !(f & ZF); // See CPD if (!again) MEMPTR().word--; return again; @@ -631,42 +597,41 @@ bool EightBit::Z80::cpdr() { #pragma region Block load instructions -void EightBit::Z80::blockLoad(register16_t source, register16_t destination) { - auto& f = F(); +void EightBit::Z80::blockLoad(uint8_t a, uint8_t& f, register16_t source, register16_t destination) { m_memory.ADDRESS() = source; auto value = m_memory.reference(); m_memory.ADDRESS() = destination; m_memory.reference() = value; - auto xy = A() + value; + auto xy = a + value; setFlag(f, XF, xy & 8); setFlag(f, YF, xy & 2); clearFlag(f, NF | HC); setFlag(f, PF, --BC().word); } -void EightBit::Z80::ldd() { - blockLoad(HL(), DE()); +void EightBit::Z80::ldd(uint8_t a, uint8_t& f) { + blockLoad(a, f, HL(), DE()); HL().word--; DE().word--; } -void EightBit::Z80::ldi() { - blockLoad(HL(), DE()); +void EightBit::Z80::ldi(uint8_t a, uint8_t& f) { + blockLoad(a, f, HL(), DE()); HL().word++; DE().word++; } -bool EightBit::Z80::ldir() { - ldi(); - auto again = (F() & PF) != 0; +bool EightBit::Z80::ldir(uint8_t a, uint8_t& f) { + ldi(a, f); + auto again = (f & PF) != 0; if (again) // See LDI MEMPTR().word = PC().word - 1; return again; } -bool EightBit::Z80::lddr() { - ldd(); - auto again = (F() & PF) != 0; +bool EightBit::Z80::lddr(uint8_t a, uint8_t& f) { + ldd(a, f); + auto again = (f & PF) != 0; if (again) // See LDR MEMPTR().word = PC().word - 1; return again; @@ -676,8 +641,7 @@ bool EightBit::Z80::lddr() { #pragma region Block input instructions -void EightBit::Z80::ini() { - auto& f = F(); +void EightBit::Z80::ini(uint8_t& f) { MEMPTR() = m_memory.ADDRESS() = BC(); MEMPTR().word++; readPort(); @@ -688,8 +652,7 @@ void EightBit::Z80::ini() { setFlag(f, NF); } -void EightBit::Z80::ind() { - auto& f = F(); +void EightBit::Z80::ind(uint8_t& f) { MEMPTR() = m_memory.ADDRESS() = BC(); MEMPTR().word--; readPort(); @@ -700,22 +663,21 @@ void EightBit::Z80::ind() { setFlag(f, NF); } -bool EightBit::Z80::inir() { - ini(); - return !(F() & ZF); // See INI +bool EightBit::Z80::inir(uint8_t& f) { + ini(f); + return !(f & ZF); // See INI } -bool EightBit::Z80::indr() { - ind(); - return !(F() & ZF); // See IND +bool EightBit::Z80::indr(uint8_t& f) { + ind(f); + return !(f & ZF); // See IND } #pragma endregion Block input instructions #pragma region Block output instructions -void EightBit::Z80::blockOut() { - auto& f = F(); +void EightBit::Z80::blockOut(uint8_t& f) { auto value = m_memory.reference(); m_memory.ADDRESS().word = BC().word; writePort(); @@ -725,26 +687,26 @@ void EightBit::Z80::blockOut() { adjustParity(f, ((value + L()) & 7) ^ B()); } -void EightBit::Z80::outi() { +void EightBit::Z80::outi(uint8_t& f) { m_memory.ADDRESS().word = HL().word++; - blockOut(); + blockOut(f); MEMPTR().word = BC().word + 1; } -void EightBit::Z80::outd() { +void EightBit::Z80::outd(uint8_t& f) { m_memory.ADDRESS().word = HL().word--; - blockOut(); + blockOut(f); MEMPTR().word = BC().word - 1; } -bool EightBit::Z80::otir() { - outi(); - return !(F() & ZF); // See OUTI +bool EightBit::Z80::otir(uint8_t& f) { + outi(f); + return !(f & ZF); // See OUTI } -bool EightBit::Z80::otdr() { - outd(); - return !(F() & ZF); // See OUTD +bool EightBit::Z80::otdr(uint8_t& f) { + outd(f); + return !(f & ZF); // See OUTD } #pragma endregion Block output instructions @@ -753,9 +715,7 @@ bool EightBit::Z80::otdr() { #pragma region Nibble rotation -void EightBit::Z80::rrd() { - auto& a = A(); - auto& f = F(); +void EightBit::Z80::rrd(uint8_t& a, uint8_t& f) { MEMPTR() = HL(); auto memory = memptrReference(); m_memory.reference() = promoteNibble(a) | highNibble(memory); @@ -764,9 +724,7 @@ void EightBit::Z80::rrd() { clearFlag(f, NF | HC); } -void EightBit::Z80::rld() { - auto& a = A(); - auto& f = F(); +void EightBit::Z80::rld(uint8_t& a, uint8_t& f) { MEMPTR() = HL(); auto memory = memptrReference(); m_memory.reference() = promoteNibble(memory) | lowNibble(a); @@ -777,6 +735,36 @@ void EightBit::Z80::rld() { #pragma endregion Nibble rotation +#pragma region I/O instructions + +void EightBit::Z80::writePort(uint8_t port, uint8_t data) { + m_memory.ADDRESS().low = port; + m_memory.ADDRESS().high = data; + MEMPTR() = m_memory.ADDRESS(); + m_memory.placeDATA(data); + writePort(); + MEMPTR().low++; +} + +void EightBit::Z80::writePort() { + m_ports.write(m_memory.ADDRESS().low, m_memory.DATA()); +} + +void EightBit::Z80::readPort(uint8_t port, uint8_t& a) { + m_memory.ADDRESS().low = port; + m_memory.ADDRESS().high = a; + MEMPTR() = m_memory.ADDRESS(); + readPort(); + a = m_memory.DATA(); + MEMPTR().low++; +} + +void EightBit::Z80::readPort() { + m_memory.placeDATA(m_ports.read(m_memory.ADDRESS().low)); +} + +#pragma endregion I/O instructions + int EightBit::Z80::step() { ExecutingInstruction.fire(*this); m_displaced = m_prefixCB = m_prefixDD = m_prefixED = m_prefixFD = false; @@ -789,24 +777,29 @@ int EightBit::Z80::execute(uint8_t opcode) { if (!M1()) throw std::logic_error("M1 cannot be high"); - auto x = (opcode & 0b11000000) >> 6; - auto y = (opcode & 0b111000) >> 3; - auto z = (opcode & 0b111); - - auto p = (y & 0b110) >> 1; - auto q = (y & 1); - if (!(m_prefixCB && m_displaced)) { - incrementRefresh(); + ++REFRESH(); M1() = false; } - if (m_prefixCB) - executeCB(x, y, z, p, q); - else if (m_prefixED) - executeED(x, y, z, p, q); - else + const auto& decoded = m_decodedOpcodes[opcode]; + + auto x = decoded.x; + auto y = decoded.y; + auto z = decoded.z; + + auto p = decoded.p; + auto q = decoded.q; + + auto prefixed = m_prefixCB || m_prefixED; + if (!prefixed) { executeOther(x, y, z, p, q); + } else { + if (m_prefixCB) + executeCB(x, y, z); + else if (m_prefixED) + executeED(x, y, z, p, q); + } if (cycles == 0) throw std::logic_error("Unhandled opcode"); @@ -814,86 +807,90 @@ int EightBit::Z80::execute(uint8_t opcode) { return cycles; } -void EightBit::Z80::executeCB(int x, int y, int z, int p, int q) { +void EightBit::Z80::executeCB(int x, int y, int z) { + auto& a = A(); auto& f = F(); switch (x) { case 0: // rot[y] r[z] switch (y) { case 0: - adjustSZP(f, m_displaced ? R2(z) = rlc(DISPLACED()) : rlc(R(z))); + adjustSZP(f, m_displaced ? R2(z, a) = rlc(f, DISPLACED()) : rlc(f, R(z, a))); break; case 1: - adjustSZP(f, m_displaced ? R2(z) = rrc(DISPLACED()) : rrc(R(z))); + adjustSZP(f, m_displaced ? R2(z, a) = rrc(f, DISPLACED()) : rrc(f, R(z, a))); break; case 2: - adjustSZP(f, m_displaced ? R2(z) = rl(DISPLACED()) : rl(R(z))); + adjustSZP(f, m_displaced ? R2(z, a) = rl(f, DISPLACED()) : rl(f, R(z, a))); break; case 3: - adjustSZP(f, m_displaced ? R2(z) = rr(DISPLACED()) : rr(R(z))); + adjustSZP(f, m_displaced ? R2(z, a) = rr(f, DISPLACED()) : rr(f, R(z, a))); break; case 4: - adjustSZP(f, m_displaced ? R2(z) = sla(DISPLACED()) : sla(R(z))); + adjustSZP(f, m_displaced ? R2(z, a) = sla(f, DISPLACED()) : sla(f, R(z, a))); break; case 5: - adjustSZP(f, m_displaced ? R2(z) = sra(DISPLACED()) : sra(R(z))); + adjustSZP(f, m_displaced ? R2(z, a) = sra(f, DISPLACED()) : sra(f, R(z, a))); break; case 6: - adjustSZP(f, m_displaced ? R2(z) = sll(DISPLACED()) : sll(R(z))); + adjustSZP(f, m_displaced ? R2(z, a) = sll(f, DISPLACED()) : sll(f, R(z, a))); break; case 7: - adjustSZP(f, m_displaced ? R2(z) = srl(DISPLACED()) : srl(R(z))); + adjustSZP(f, m_displaced ? R2(z, a) = srl(f, DISPLACED()) : srl(f, R(z, a))); break; } - if (m_displaced) { - cycles += 23; - } else { - cycles += 8; + cycles += 8; + if (!m_displaced) { if (z == 6) cycles += 7; + } else { + cycles += 15; } break; case 1: // BIT y, r[z] - if (m_displaced) { - bit(y, DISPLACED()); - adjustXY(f, MEMPTR().high); - cycles += 20; - } else { - auto operand = bit(y, R(z)); - cycles += 8; + cycles += 8; + if (!m_displaced) { + auto operand = bit(f, y, R(z, a)); if (z == 6) { adjustXY(f, MEMPTR().high); cycles += 4; } else { adjustXY(f, operand); } + } else { + bit(f, y, DISPLACED()); + adjustXY(f, MEMPTR().high); + cycles += 12; } break; case 2: // RES y, r[z] - if (m_displaced) { - R2(z) = res(y, DISPLACED()); - cycles += 23; - } else { - res(y, R(z)); - cycles += 8; + cycles += 8; + if (!m_displaced) { + res(y, R(z, a)); if (z == 6) cycles += 7; + } else { + R2(z, a) = res(y, DISPLACED()); + cycles += 15; } break; case 3: // SET y, r[z] - if (m_displaced) { - R2(z) = set(y, DISPLACED()); - cycles += 23; - } else { - set(y, R(z)); - cycles += 8; + cycles += 8; + if (!m_displaced) { + set(y, R(z, a)); if (z == 6) cycles += 7; + } else { + R2(z, a) = set(y, DISPLACED()); + cycles += 15; } break; + default: + __assume(0); } } void EightBit::Z80::executeED(int x, int y, int z, int p, int q) { + auto& a = A(); auto& f = F(); switch (x) { case 0: @@ -907,7 +904,7 @@ void EightBit::Z80::executeED(int x, int y, int z, int p, int q) { MEMPTR().word++; readPort(); if (y != 6) // IN r[y],(C) - R(y) = m_memory.DATA(); + R(y, a) = m_memory.DATA(); adjustSZPXY(f, m_memory.DATA()); clearFlag(f, NF | HC); cycles += 12; @@ -918,18 +915,20 @@ void EightBit::Z80::executeED(int x, int y, int z, int p, int q) { if (y == 6) // OUT (C),0 m_memory.placeDATA(0); else // OUT (C),r[y] - m_memory.placeDATA(R(y)); + m_memory.placeDATA(R(y, a)); writePort(); cycles += 12; break; case 2: // 16-bit add/subtract with carry switch (q) { case 0: // SBC HL, rp[p] - sbcViaMemptr(HL2(), RP(p)); + sbc(f, HL2(), RP(p)); break; case 1: // ADC HL, rp[p] - adcViaMemptr(HL2(), RP(p)); + adc(f, HL2(), RP(p)); break; + default: + __assume(0); } cycles += 15; break; @@ -943,11 +942,13 @@ void EightBit::Z80::executeED(int x, int y, int z, int p, int q) { fetchWord(); getWordViaMemptr(RP(p)); break; + default: + __assume(0); } cycles += 20; break; case 4: // Negate accumulator - neg(); + neg(a, f); cycles += 8; break; case 5: // Return from interrupt @@ -978,47 +979,54 @@ void EightBit::Z80::executeED(int x, int y, int z, int p, int q) { case 1: case 5: IM() = 0; + break; + default: + __assume(0); } cycles += 8; break; case 7: // Assorted ops switch (y) { case 0: // LD I,A - IV() = A(); + IV() = a; cycles += 9; break; case 1: // LD R,A - REFRESH() = A(); + REFRESH() = a; cycles += 9; break; case 2: // LD A,I - A() = IV(); - adjustSZXY(f, A()); - setFlag(f, PF, IFF2()); + a = IV(); + adjustSZXY(f, a); clearFlag(f, NF | HC); + setFlag(f, PF, IFF2()); cycles += 9; break; case 3: // LD A,R - A() = REFRESH(); - adjustSZXY(f, A()); + a = REFRESH(); + adjustSZXY(f, a); clearFlag(f, NF | HC); setFlag(f, PF, IFF2()); cycles += 9; break; case 4: // RRD - rrd(); + rrd(a, f); cycles += 18; break; case 5: // RLD - rld(); + rld(a, f); cycles += 18; break; case 6: // NOP case 7: // NOP cycles += 4; break; + default: + __assume(0); } break; + default: + __assume(0); } break; case 2: @@ -1026,19 +1034,19 @@ void EightBit::Z80::executeED(int x, int y, int z, int p, int q) { case 0: // LD switch (y) { case 4: // LDI - ldi(); + ldi(a, f); break; case 5: // LDD - ldd(); + ldd(a, f); break; case 6: // LDIR - if (ldir()) { + if (ldir(a, f)) { PC().word -= 2; cycles += 5; } break; case 7: // LDDR - if (lddr()) { + if (lddr(a, f)) { PC().word -= 2; cycles += 5; } @@ -1048,19 +1056,19 @@ void EightBit::Z80::executeED(int x, int y, int z, int p, int q) { case 1: // CP switch (y) { case 4: // CPI - cpi(); + cpi(a, f); break; case 5: // CPD - cpd(); + cpd(a, f); break; case 6: // CPIR - if (cpir()) { + if (cpir(a, f)) { PC().word -= 2; cycles += 5; } break; case 7: // CPDR - if (cpdr()) { + if (cpdr(a, f)) { PC().word -= 2; cycles += 5; } @@ -1070,19 +1078,19 @@ void EightBit::Z80::executeED(int x, int y, int z, int p, int q) { case 2: // IN switch (y) { case 4: // INI - ini(); + ini(f); break; case 5: // IND - ind(); + ind(f); break; case 6: // INIR - if (inir()) { + if (inir(f)) { PC().word -= 2; cycles += 5; } break; case 7: // INDR - if (indr()) { + if (indr(f)) { PC().word -= 2; cycles += 5; } @@ -1092,19 +1100,19 @@ void EightBit::Z80::executeED(int x, int y, int z, int p, int q) { case 3: // OUT switch (y) { case 4: // OUTI - outi(); + outi(f); break; case 5: // OUTD - outd(); + outd(f); break; case 6: // OTIR - if (otir()) { + if (otir(f)) { PC().word -= 2; cycles += 5; } break; case 7: // OTDR - if (otdr()) { + if (otdr(f)) { PC().word -= 2; cycles += 5; } @@ -1118,6 +1126,7 @@ void EightBit::Z80::executeED(int x, int y, int z, int p, int q) { } void EightBit::Z80::executeOther(int x, int y, int z, int p, int q) { + auto& a = A(); auto& f = F(); switch (x) { case 0: @@ -1141,7 +1150,7 @@ void EightBit::Z80::executeOther(int x, int y, int z, int p, int q) { cycles += 12; break; default: // JR cc,d - if (jrConditionalFlag(y - 4)) + if (jrConditionalFlag(f, y - 4)) cycles += 5; cycles += 5; break; @@ -1150,13 +1159,15 @@ void EightBit::Z80::executeOther(int x, int y, int z, int p, int q) { case 1: // 16-bit load immediate/add switch (q) { case 0: // LD rp,nn - Processor::fetchWord(RP(p)); + fetchWord(RP(p)); cycles += 10; break; case 1: // ADD HL,rp - addViaMemptr(HL2(), RP(p)); + add(f, HL2(), RP(p)); cycles += 11; break; + default: + __assume(0); } break; case 2: // Indirect loading @@ -1165,12 +1176,12 @@ void EightBit::Z80::executeOther(int x, int y, int z, int p, int q) { switch (p) { case 0: // LD (BC),A MEMPTR() = BC(); - MEMPTR().high = memptrReference() = A(); + MEMPTR().high = memptrReference() = a; cycles += 7; break; case 1: // LD (DE),A MEMPTR() = DE(); - MEMPTR().high = memptrReference() = A(); + MEMPTR().high = memptrReference() = a; cycles += 7; break; case 2: // LD (nn),HL @@ -1180,21 +1191,23 @@ void EightBit::Z80::executeOther(int x, int y, int z, int p, int q) { break; case 3: // LD (nn),A fetchWord(); - MEMPTR().high = memptrReference() = A(); + MEMPTR().high = memptrReference() = a; cycles += 13; break; + default: + __assume(0); } break; case 1: switch (p) { case 0: // LD A,(BC) MEMPTR() = BC(); - A() = memptrReference(); + a = memptrReference(); cycles += 7; break; case 1: // LD A,(DE) MEMPTR() = DE(); - A() = memptrReference(); + a = memptrReference(); cycles += 7; break; case 2: // LD HL,(nn) @@ -1204,11 +1217,15 @@ void EightBit::Z80::executeOther(int x, int y, int z, int p, int q) { break; case 3: // LD A,(nn) fetchWord(); - A() = memptrReference(); + a = memptrReference(); cycles += 13; break; + default: + __assume(0); } break; + default: + __assume(0); } break; case 3: // 16-bit INC/DEC @@ -1219,21 +1236,23 @@ void EightBit::Z80::executeOther(int x, int y, int z, int p, int q) { case 1: // DEC rp --RP(p).word; break; + default: + __assume(0); } cycles += 6; break; case 4: // 8-bit INC - postIncrement(f, ++R(y)); // INC r + postIncrement(f, ++R(y, a)); // INC r cycles += 4; break; case 5: // 8-bit DEC - postDecrement(f, --R(y)); // DEC r + postDecrement(f, --R(y, a)); // DEC r cycles += 4; if (y == 6) cycles += 7; break; case 6: { // 8-bit load immediate - auto& r = R(y); // LD r,n + auto& r = R(y, a); // LD r,n r = fetchByte(); cycles += 7; if (y == 6) @@ -1242,32 +1261,36 @@ void EightBit::Z80::executeOther(int x, int y, int z, int p, int q) { } case 7: // Assorted operations on accumulator/flags switch (y) { case 0: - rlc(A()); + rlc(f, a); break; case 1: - rrc(A()); + rrc(f, a); break; case 2: - rl(A()); + rl(f, a); break; case 3: - rr(A()); + rr(f, a); break; case 4: - daa(); + daa(a, f); break; case 5: - cpl(); + cpl(a, f); break; case 6: - scf(); + scf(a, f); break; case 7: - ccf(); + ccf(a, f); break; + default: + __assume(0); } cycles += 4; break; + default: + __assume(0); } break; case 1: // 8-bit loading @@ -1279,11 +1302,11 @@ void EightBit::Z80::executeOther(int x, int y, int z, int p, int q) { if (z == 6) { switch (y) { case 4: - H() = R(z); + H() = R(z, a); normal = false; break; case 5: - L() = R(z); + L() = R(z, a); normal = false; break; } @@ -1291,18 +1314,18 @@ void EightBit::Z80::executeOther(int x, int y, int z, int p, int q) { if (y == 6) { switch (z) { case 4: - R(y) = H(); + R(y, a) = H(); normal = false; break; case 5: - R(y) = L(); + R(y, a) = L(); normal = false; break; } } } if (normal) - R(y) = R(z); + R(y, a) = R(z, a); if ((y == 6) || (z == 6)) // M operations cycles += 3; } @@ -1311,29 +1334,31 @@ void EightBit::Z80::executeOther(int x, int y, int z, int p, int q) { case 2: // Operate on accumulator and register/memory location switch (y) { case 0: // ADD A,r - add(A(), R(z)); + add(f, a, R(z, a)); break; case 1: // ADC A,r - adc(A(), R(z)); + adc(f, a, R(z, a)); break; case 2: // SUB r - sub(A(), R(z)); + sub(f, a, R(z, a)); break; case 3: // SBC A,r - sbc(A(), R(z)); + sbc(f, a, R(z, a)); break; case 4: // AND r - andr(A(), R(z)); + andr(f, a, R(z, a)); break; case 5: // XOR r - xorr(A(), R(z)); + xorr(f, a, R(z, a)); break; case 6: // OR r - orr(A(), R(z)); + orr(f, a, R(z, a)); break; case 7: // CP r - compare(R(z)); + compare(f, a, R(z, a)); break; + default: + __assume(0); } cycles += 4; if (z == 6) @@ -1342,7 +1367,7 @@ void EightBit::Z80::executeOther(int x, int y, int z, int p, int q) { case 3: switch (z) { case 0: // Conditional return - if (returnConditionalFlag(y)) + if (returnConditionalFlag(f, y)) cycles += 6; cycles += 5; break; @@ -1370,11 +1395,16 @@ void EightBit::Z80::executeOther(int x, int y, int z, int p, int q) { SP() = HL2(); cycles += 4; break; + default: + __assume(0); } + break; + default: + __assume(0); } break; case 2: // Conditional jump - jumpConditionalFlag(y); + jumpConditionalFlag(f, y); cycles += 10; break; case 3: // Assorted operations @@ -1391,25 +1421,15 @@ void EightBit::Z80::executeOther(int x, int y, int z, int p, int q) { fetchExecute(); break; case 2: // OUT (n),A - m_memory.ADDRESS().low = fetchByte(); - m_memory.ADDRESS().high = A(); - MEMPTR() = m_memory.ADDRESS(); - m_memory.placeDATA(A()); - writePort(); - MEMPTR().low++; + writePort(fetchByte(), a); cycles += 11; break; case 3: // IN A,(n) - m_memory.ADDRESS().low = fetchByte(); - m_memory.ADDRESS().high = A(); - MEMPTR() = m_memory.ADDRESS(); - readPort(); - A() = m_memory.DATA(); - MEMPTR().low++; + readPort(fetchByte(), a); cycles += 11; break; case 4: // EX (SP),HL - xhtl(); + xhtl(HL2()); cycles += 19; break; case 5: // EX DE,HL @@ -1424,10 +1444,12 @@ void EightBit::Z80::executeOther(int x, int y, int z, int p, int q) { ei(); cycles += 4; break; + default: + __assume(0); } break; case 4: // Conditional call: CALL cc[y], nn - if (callConditionalFlag(y)) + if (callConditionalFlag(f, y)) cycles += 7; cycles += 10; break; @@ -1456,35 +1478,42 @@ void EightBit::Z80::executeOther(int x, int y, int z, int p, int q) { m_displaced = m_prefixFD = true; fetchExecute(); break; + default: + __assume(0); } + break; + default: + __assume(0); } break; case 6: // Operate on accumulator and immediate operand: alu[y] n switch (y) { case 0: // ADD A,n - add(A(), fetchByte()); + add(f, a, fetchByte()); break; case 1: // ADC A,n - adc(A(), fetchByte()); + adc(f, a, fetchByte()); break; case 2: // SUB n - sub(A(), fetchByte()); + sub(f, a, fetchByte()); break; case 3: // SBC A,n - sbc(A(), fetchByte()); + sbc(f, a, fetchByte()); break; case 4: // AND n - andr(A(), fetchByte()); + andr(f, a, fetchByte()); break; case 5: // XOR n - xorr(A(), fetchByte()); + xorr(f, a, fetchByte()); break; case 6: // OR n - orr(A(), fetchByte()); + orr(f, a, fetchByte()); break; case 7: // CP n - compare(fetchByte()); + compare(f, a, fetchByte()); break; + default: + __assume(0); } cycles += 7; break; @@ -1492,6 +1521,8 @@ void EightBit::Z80::executeOther(int x, int y, int z, int p, int q) { restart(y << 3); cycles += 11; break; + default: + __assume(0); } break; } diff --git a/Z80/src/Z80.vcxproj b/Z80/src/Z80.vcxproj index f43ea30..3fb7c70 100644 --- a/Z80/src/Z80.vcxproj +++ b/Z80/src/Z80.vcxproj @@ -23,7 +23,6 @@ {01974F81-2750-45AF-B845-2C903A54A334} Win32Proj Z80 - 8.1 diff --git a/Z80/src/stdafx.h b/Z80/src/stdafx.h index fe79e2d..ef248a1 100644 --- a/Z80/src/stdafx.h +++ b/Z80/src/stdafx.h @@ -19,24 +19,3 @@ #include #include - -//#include -//#include -// -//#if SDL_BYTEORDER == SDL_LIL_ENDIAN -//#define HOST_LITTLE_ENDIAN -//#endif -// -//#if SDL_BYTEORDER == SDL_BIG_ENDIAN -//#define HOST_BIG_ENDIAN -//#endif -// -//#define RUN_TESTS -////#define RUN_CPM -////#define RUN_INVADERS -// -//#ifdef _MSC_VER -//#pragma comment(lib, "SDL2.lib") -//#pragma comment(lib, "SDL2main.lib") -//#pragma comment(lib, "SDL2_mixer.lib") -//#endif diff --git a/Z80/test/Board.h b/Z80/test/Board.h index dfb4eaf..0c7c4be 100644 --- a/Z80/test/Board.h +++ b/Z80/test/Board.h @@ -14,9 +14,8 @@ class Board { public: Board(const Configuration& configuration); - EightBit::Memory& getMemory() { return m_memory; } - const EightBit::Z80& getCPU() const { return m_cpu; } - EightBit::Z80& getCPUMutable() { return m_cpu; } + EightBit::Memory& Memory() { return m_memory; } + EightBit::Z80& CPU() { return m_cpu; } void initialise(); diff --git a/Z80/test/test_Z80.vcxproj b/Z80/test/test_Z80.vcxproj index c215a2b..f61a03a 100644 --- a/Z80/test/test_Z80.vcxproj +++ b/Z80/test/test_Z80.vcxproj @@ -84,6 +84,7 @@ ..\inc;..\..\inc;C:\local\boost_1_64_0;$(IncludePath) C:\local\boost_1_64_0\lib64-msvc-14.0;$(LibraryPath) + false diff --git a/inc/AddressEventArgs.h b/inc/AddressEventArgs.h new file mode 100644 index 0000000..ef1e036 --- /dev/null +++ b/inc/AddressEventArgs.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace EightBit { + class AddressEventArgs { + private: + uint16_t m_address; + uint8_t m_cell; + + public: + AddressEventArgs(uint16_t address, uint8_t cell) + : m_address(address), m_cell(cell) {} + + uint16_t getAddress() const { return m_address; } + uint8_t getCell() const { return m_cell; } + }; +} \ No newline at end of file diff --git a/inc/IntelProcessor.h b/inc/IntelProcessor.h index d6fe05d..2fda28c 100644 --- a/inc/IntelProcessor.h +++ b/inc/IntelProcessor.h @@ -13,6 +13,8 @@ namespace EightBit { virtual void initialise(); + register16_t& SP() { return sp; } + virtual register16_t& AF() = 0; uint8_t& A() { return AF().high; } uint8_t& F() { return AF().low; } @@ -32,19 +34,6 @@ namespace EightBit { protected: IntelProcessor(Memory& memory); - static void clearFlag(uint8_t& f, int flag) { f &= ~flag; } - static void setFlag(uint8_t& f, int flag) { f |= flag; } - - static void setFlag(uint8_t& f, int flag, int condition) { setFlag(f, flag, condition != 0); } - static void setFlag(uint8_t& f, int flag, uint32_t condition) { setFlag(f, flag, condition != 0); } - static void setFlag(uint8_t& f, int flag, bool condition) { condition ? setFlag(f, flag) : clearFlag(f, flag); } - - static void clearFlag(uint8_t& f, int flag, int condition) { clearFlag(f, flag, condition != 0); } - static void clearFlag(uint8_t& f, int flag, uint32_t condition) { clearFlag(f, flag, condition != 0); } - static void clearFlag(uint8_t& f, int flag, bool condition) { condition ? clearFlag(f, flag) : setFlag(f, flag); } - - // - template static void adjustSign(uint8_t& f, uint8_t value) { setFlag(f, T::SF, value & T::SF); } @@ -54,7 +43,7 @@ namespace EightBit { } template static void adjustParity(uint8_t& f, uint8_t value) { - static const uint8_t lookup[0x10] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 }; + static const int lookup[0x10] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 }; auto set = lookup[highNibble(value)] + lookup[lowNibble(value)]; clearFlag(f, T::PF, set % 2); } @@ -87,27 +76,40 @@ namespace EightBit { // static int buildHalfCarryIndex(uint8_t before, uint8_t value, int calculation) { + __assume(calculation < 0x1ffff); return ((before & 0x88) >> 1) | ((value & 0x88) >> 2) | ((calculation & 0x88) >> 3); } static bool calculateHalfCarryAdd(uint8_t before, uint8_t value, int calculation) { + __assume(calculation < 0x1ffff); static std::array m_halfCarryTableAdd = { { false, false, true, false, true, false, true, true } }; auto index = buildHalfCarryIndex(before, value, calculation); return m_halfCarryTableAdd[index & Mask3]; } static bool calculateHalfCarrySub(uint8_t before, uint8_t value, int calculation) { + __assume(calculation < 0x1ffff); std::array m_halfCarryTableSub = { { false, true, true, true, false, false, false, true } }; auto index = buildHalfCarryIndex(before, value, calculation); return m_halfCarryTableSub[index & Mask3]; } + uint8_t fetchByte() { + m_memory.ADDRESS().word = PC().word++; + return m_memory.reference(); + } + + void fetchWord(register16_t& output) { + output.low = fetchByte(); + output.high = fetchByte(); + } + void push(uint8_t value) { m_memory.ADDRESS().word = --SP().word; m_memory.reference() = value; } - void pushWord(register16_t value) { + void pushWord(const register16_t& value) { push(value.high); push(value.low); } @@ -123,7 +125,7 @@ namespace EightBit { } void fetchWord() { - Processor::fetchWord(MEMPTR()); + fetchWord(MEMPTR()); } // @@ -202,5 +204,6 @@ namespace EightBit { private: register16_t m_memptr; + register16_t sp; }; } \ No newline at end of file diff --git a/inc/Memory.h b/inc/Memory.h index a10f90e..685f474 100644 --- a/inc/Memory.h +++ b/inc/Memory.h @@ -4,6 +4,9 @@ #include #include +#include "Signal.h" +#include "AddressEventArgs.h" + #if defined(_M_X64) || defined(_M_IX86 ) # define HOST_LITTLE_ENDIAN #else @@ -29,26 +32,23 @@ namespace EightBit { class Memory { public: - static uint8_t highByte(uint16_t value) { - return value >> 8; - } - - static uint8_t lowByte(uint16_t value) { - return value & 0xff; - } - Memory(uint16_t addressMask); + virtual ~Memory(); - virtual register16_t& ADDRESS() { return m_address; } - virtual uint8_t& DATA() { return *m_data; } + // Only fired with read/write methods + Signal WrittenByte; + Signal ReadByte; - virtual uint8_t& placeDATA(uint8_t value) { + register16_t& ADDRESS() { return m_address; } + uint8_t& DATA() { return *m_data; } + + uint8_t& placeDATA(uint8_t value) { m_temporary = value; m_data = &m_temporary; return DATA(); } - virtual uint8_t& referenceDATA(uint8_t& value) { + uint8_t& referenceDATA(uint8_t& value) { m_data = &value; return DATA(); } @@ -61,10 +61,39 @@ namespace EightBit { return m_locked[effective] ? placeDATA(m_bus[effective]) : referenceDATA(m_bus[effective]); } - virtual uint16_t effectiveAddress(uint16_t address) const { + virtual int effectiveAddress(int address) const { return address & m_addressMask; } + void fireReadBusEvent() { + ReadByte.fire(AddressEventArgs(ADDRESS().word, DATA())); + } + + uint8_t read() { + auto content = reference(); + fireReadBusEvent(); + return content; + } + + uint8_t read(uint16_t offset) { + ADDRESS().word = offset; + return read(); + } + + void fireWriteBusEvent() { + WrittenByte.fire(AddressEventArgs(ADDRESS().word, DATA())); + } + + void write(uint8_t value) { + reference() = value; + fireWriteBusEvent(); + } + + void write(uint16_t offset, uint8_t value) { + ADDRESS().word = offset; + write(value); + } + void clear(); void loadRom(const std::string& path, uint16_t offset); void loadRam(const std::string& path, uint16_t offset); diff --git a/inc/Processor.h b/inc/Processor.h index 7c1da28..abd7641 100644 --- a/inc/Processor.h +++ b/inc/Processor.h @@ -38,16 +38,15 @@ namespace EightBit { Bit0 = 0x1, }; - static uint8_t highNibble(uint8_t value) { return value >> 4; } - static uint8_t lowNibble(uint8_t value) { return value & Mask4; } + static int highNibble(int value) { return value >> 4; } + static int lowNibble(int value) { return value & Mask4; } - static uint8_t promoteNibble(uint8_t value) { return value << 4; } - static uint8_t demoteNibble(uint8_t value) { return highNibble(value); } + static int promoteNibble(int value) { return value << 4; } + static int demoteNibble(int value) { return highNibble(value); } const Memory& getMemory() const { return m_memory; } register16_t& PC() { return pc; } - register16_t& SP() { return sp; } bool isHalted() const { return m_halted; } void halt() { --PC().word; m_halted = true; } @@ -57,24 +56,24 @@ namespace EightBit { void reset(); protected: + static void clearFlag(uint8_t& f, int flag) { f &= ~flag; } + static void setFlag(uint8_t& f, int flag) { f |= flag; } + + static void setFlag(uint8_t& f, int flag, int condition) { setFlag(f, flag, condition != 0); } + static void setFlag(uint8_t& f, int flag, uint32_t condition) { setFlag(f, flag, condition != 0); } + static void setFlag(uint8_t& f, int flag, bool condition) { condition ? setFlag(f, flag) : clearFlag(f, flag); } + + static void clearFlag(uint8_t& f, int flag, int condition) { clearFlag(f, flag, condition != 0); } + static void clearFlag(uint8_t& f, int flag, uint32_t condition) { clearFlag(f, flag, condition != 0); } + static void clearFlag(uint8_t& f, int flag, bool condition) { condition ? clearFlag(f, flag) : setFlag(f, flag); } + Processor(Memory& memory); Memory& m_memory; int cycles; - uint8_t fetchByte() { - m_memory.ADDRESS().word = PC().word++; - return m_memory.reference(); - } - - void fetchWord(register16_t& output) { - output.low = fetchByte(); - output.high = fetchByte(); - } - private: register16_t pc; - register16_t sp; bool m_halted; }; } \ No newline at end of file diff --git a/inc/TestHarness.h b/inc/TestHarness.h index 885f6ca..2d5667c 100644 --- a/inc/TestHarness.h +++ b/inc/TestHarness.h @@ -1,46 +1,66 @@ #pragma once #include +#include +#include namespace EightBit { template class TestHarness { public: TestHarness(const ConfigurationT& configuration) - : m_configuration(configuration), - m_board(configuration) { + : m_board(configuration), + m_totalCycles(0), + m_instructions(0), + m_startHostCycles(0), + m_finishHostCycles(0) { } ~TestHarness() { - std::cout << std::endl; + std::cout << std::dec << std::endl; std::cout.imbue(std::locale("")); - std::cout << "Cycles = " << m_totalCycles << std::endl; + std::cout << "Guest cycles = " << m_totalCycles << std::endl; std::cout << "Seconds = " << getElapsedSeconds() << std::endl; - std::cout << getCyclesPerSecond() << " cycles/second" << std::endl; + std::cout << getCyclesPerSecond() << " MHz" << std::endl; + std::cout << getInstructionsPerSecond() << " instructions per second" << std::endl; + + auto elapsedHostCycles = m_finishHostCycles - m_startHostCycles; + std::cout << "Host cycles = " << elapsedHostCycles << std::endl; + + auto efficiency = elapsedHostCycles / m_totalCycles; + std::cout << "Efficiency = " << efficiency << std::endl; } std::chrono::steady_clock::duration getElapsedTime() const { return m_finishTime - m_startTime; } - long long getElapsedSeconds() const { - return std::chrono::duration_cast(getElapsedTime()).count(); + double getElapsedSeconds() const { + return std::chrono::duration_cast>(getElapsedTime()).count(); } - long long getCyclesPerSecond() const { - return m_totalCycles / getElapsedSeconds(); + double getCyclesPerSecond() const { + return (m_totalCycles / 1000000 ) / getElapsedSeconds(); + } + + long long getInstructionsPerSecond() { + auto floating = m_instructions / getElapsedSeconds(); + return (long long)floating; } void runLoop() { m_startTime = now(); - m_totalCycles = 0UL; + m_totalCycles = m_instructions = 0L; + m_startHostCycles = currentHostCycles(); - auto& cpu = m_board.getCPUMutable(); + auto& cpu = m_board.CPU(); while (!cpu.isHalted()) { m_totalCycles += cpu.step(); + ++m_instructions; } + m_finishHostCycles = currentHostCycles(); m_finishTime = now(); } @@ -49,14 +69,20 @@ namespace EightBit { } private: - const ConfigurationT& m_configuration; BoardT m_board; long long m_totalCycles; + long long m_instructions; std::chrono::steady_clock::time_point m_startTime; std::chrono::steady_clock::time_point m_finishTime; + unsigned __int64 m_startHostCycles; + unsigned __int64 m_finishHostCycles; - std::chrono::steady_clock::time_point now() { + static std::chrono::steady_clock::time_point now() { return std::chrono::steady_clock::now(); } + + static unsigned __int64 currentHostCycles() { + return __rdtsc(); + } }; } \ No newline at end of file diff --git a/src/EightBit.vcxproj b/src/EightBit.vcxproj index 2e34973..2532ba8 100644 --- a/src/EightBit.vcxproj +++ b/src/EightBit.vcxproj @@ -138,6 +138,7 @@ + diff --git a/src/EightBit.vcxproj.filters b/src/EightBit.vcxproj.filters index bf1e258..57c17c6 100644 --- a/src/EightBit.vcxproj.filters +++ b/src/EightBit.vcxproj.filters @@ -32,6 +32,9 @@ Header Files + + Header Files + diff --git a/src/IntelProcessor.cpp b/src/IntelProcessor.cpp index 5f89687..042bffc 100644 --- a/src/IntelProcessor.cpp +++ b/src/IntelProcessor.cpp @@ -4,9 +4,11 @@ EightBit::IntelProcessor::IntelProcessor(Memory& memory) : Processor(memory) { MEMPTR().word = 0; + SP().word = 0xffff; } void EightBit::IntelProcessor::initialise() { Processor::initialise(); MEMPTR().word = 0; + SP().word = 0xffff; } diff --git a/src/Memory.cpp b/src/Memory.cpp index 29c3d5b..060a711 100644 --- a/src/Memory.cpp +++ b/src/Memory.cpp @@ -14,6 +14,9 @@ EightBit::Memory::Memory(uint16_t addressMask) m_data = &(m_bus[m_address.word]); } +EightBit::Memory::~Memory() { +} + uint8_t EightBit::Memory::peek(uint16_t address) const { return m_bus[address]; } diff --git a/src/Processor.cpp b/src/Processor.cpp index 5a177c6..19d952d 100644 --- a/src/Processor.cpp +++ b/src/Processor.cpp @@ -5,7 +5,6 @@ EightBit::Processor::Processor(Memory& memory) : m_memory(memory), cycles(0), m_halted(false) { - SP().word = 0xffff; PC().word = 0; } @@ -14,6 +13,5 @@ void EightBit::Processor::reset() { } void EightBit::Processor::initialise() { - SP().word = 0xffff; reset(); }