commit 105032f08a5b275e8e9fc23ccb9b91ab6303c02e Author: Adrian.Conlon Date: Sun Jun 4 21:38:34 2017 +0100 Dump of all my C++ emulators, only Intel8080 integrated so far... Signed-off-by: Adrian.Conlon diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bf8350d --- /dev/null +++ b/.gitignore @@ -0,0 +1,51 @@ +.vs/ +Debug/ +Release/ +x64/ +ipch/ +cov-int/ +packages/ + +*.opendb +*.sdf +*.vsp +*.VC.db +*.vcxproj.user + +*.swp +*~ + +*.bin +*.rom + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app +cpp_chip8 + +src/state diff --git a/EightBit.sln b/EightBit.sln new file mode 100644 index 0000000..f9de29c --- /dev/null +++ b/EightBit.sln @@ -0,0 +1,38 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26430.12 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EightBit", "src\EightBit.vcxproj", "{A9C24BD9-0CB4-4C84-B09B-46B815F9DA47}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Intel8080", "Intel8080\src\Intel8080.vcxproj", "{93BDC8D8-9F0D-44ED-94FB-1BE6AC4B6BD6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A9C24BD9-0CB4-4C84-B09B-46B815F9DA47}.Debug|x64.ActiveCfg = Debug|x64 + {A9C24BD9-0CB4-4C84-B09B-46B815F9DA47}.Debug|x64.Build.0 = Debug|x64 + {A9C24BD9-0CB4-4C84-B09B-46B815F9DA47}.Debug|x86.ActiveCfg = Debug|Win32 + {A9C24BD9-0CB4-4C84-B09B-46B815F9DA47}.Debug|x86.Build.0 = Debug|Win32 + {A9C24BD9-0CB4-4C84-B09B-46B815F9DA47}.Release|x64.ActiveCfg = Release|x64 + {A9C24BD9-0CB4-4C84-B09B-46B815F9DA47}.Release|x64.Build.0 = Release|x64 + {A9C24BD9-0CB4-4C84-B09B-46B815F9DA47}.Release|x86.ActiveCfg = Release|Win32 + {A9C24BD9-0CB4-4C84-B09B-46B815F9DA47}.Release|x86.Build.0 = Release|Win32 + {93BDC8D8-9F0D-44ED-94FB-1BE6AC4B6BD6}.Debug|x64.ActiveCfg = Debug|x64 + {93BDC8D8-9F0D-44ED-94FB-1BE6AC4B6BD6}.Debug|x64.Build.0 = Debug|x64 + {93BDC8D8-9F0D-44ED-94FB-1BE6AC4B6BD6}.Debug|x86.ActiveCfg = Debug|Win32 + {93BDC8D8-9F0D-44ED-94FB-1BE6AC4B6BD6}.Debug|x86.Build.0 = Debug|Win32 + {93BDC8D8-9F0D-44ED-94FB-1BE6AC4B6BD6}.Release|x64.ActiveCfg = Release|x64 + {93BDC8D8-9F0D-44ED-94FB-1BE6AC4B6BD6}.Release|x64.Build.0 = Release|x64 + {93BDC8D8-9F0D-44ED-94FB-1BE6AC4B6BD6}.Release|x86.ActiveCfg = Release|Win32 + {93BDC8D8-9F0D-44ED-94FB-1BE6AC4B6BD6}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Intel8080/inc/Disassembler.h b/Intel8080/inc/Disassembler.h new file mode 100644 index 0000000..baa4735 --- /dev/null +++ b/Intel8080/inc/Disassembler.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +namespace EightBit { + class Intel8080; + + class Disassembler { + public: + Disassembler(); + + static std::string state(Intel8080& cpu); + static std::string disassemble(Intel8080& cpu); + + static std::string hex(uint8_t value); + static std::string hex(uint16_t value); + static std::string binary(uint8_t value); + + static std::string invalid(uint8_t value); + }; +} \ No newline at end of file diff --git a/Intel8080/inc/InputOutput.h b/Intel8080/inc/InputOutput.h new file mode 100644 index 0000000..c51eda6 --- /dev/null +++ b/Intel8080/inc/InputOutput.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include "Signal.h" +#include "PortEventArgs.h" + +namespace EightBit { + class InputOutput { + public: + InputOutput(); + + uint8_t read(uint8_t port) { return readInputPort(port); } + void write(uint8_t port, uint8_t value) { return writeOutputPort(port, value); } + + uint8_t readInputPort(uint8_t port); + void writeInputPort(uint8_t port, uint8_t value) { input[port] = value; } + + uint8_t readOutputPort(uint8_t port) { return output[port]; } + void writeOutputPort(uint8_t port, uint8_t value); + + Signal ReadingPort; + Signal ReadPort; + + Signal WritingPort; + Signal WrittenPort; + + protected: + void OnReadingPort(uint8_t port); + void OnReadPort(uint8_t port); + + void OnWritingPort(uint8_t port); + void OnWrittenPort(uint8_t port); + + private: + std::array input; + std::array output; + }; +} \ No newline at end of file diff --git a/Intel8080/inc/Intel8080.h b/Intel8080/inc/Intel8080.h new file mode 100644 index 0000000..368da3c --- /dev/null +++ b/Intel8080/inc/Intel8080.h @@ -0,0 +1,708 @@ +#pragma once + +// Auxiliary carry logic from https://github.com/begoon/i8080-core + +#include "Processor.h" +#include "StatusFlags.h" +#include "InputOutput.h" + +namespace EightBit { + class Intel8080 : public Processor { + public: + typedef std::function instruction_t; + + enum AddressingMode { + Unknown, + Implied, // zero bytes + Immediate, // single byte + Absolute // two bytes, little endian + }; + + struct Instruction { + instruction_t vector = nullptr; + AddressingMode mode = Unknown; + std::string disassembly; + int count = 0; + }; + + Intel8080(Memory& memory, InputOutput& ports); + + Signal ExecutingInstruction; + + const std::array& getInstructions() const { return instructions; } + + uint8_t& A() { return a; } + StatusFlags& F() { return f; } + + register16_t& BC() { return bc; } + uint8_t& B() { return BC().high; } + uint8_t& C() { return BC().low; } + + register16_t& DE() { return de; } + uint8_t& D() { return DE().high; } + uint8_t& E() { return DE().low; } + + register16_t& HL() { return hl; } + uint8_t& H() { return HL().high; } + uint8_t& L() { return HL().low; } + + bool isInterruptable() const { + return m_interrupt; + } + + void disableInterrupts() { m_interrupt = false; } + void enableInterrupts() { m_interrupt = true; } + + int interrupt(uint8_t value) { + if (isInterruptable()) { + disableInterrupts(); + return execute(value); + } + return 0; + } + + virtual void initialise(); + int step(); + + private: + InputOutput m_ports; + + std::array instructions; + + std::array m_halfCarryTableAdd = { { false, false, true, false, true, false, true, true } }; + std::array m_halfCarryTableSub = { { false, true, true, true, false, false, false, true } }; + + uint8_t a; + StatusFlags f; + + register16_t bc; + register16_t de; + register16_t hl; + + bool m_interrupt; + + int execute(uint8_t opcode); + + int execute(const Instruction& instruction) { + cycles = 0; + instruction.vector(); + return cycles + instruction.count; + } + + void adjustSign(uint8_t value) { F().S = ((value & 0x80) != 0); } + void adjustZero(uint8_t value) { F().Z = (value == 0); } + + void adjustParity(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 }; + auto set = (lookup[highNibble(value)] + lookup[lowNibble(value)]); + F().P = (set % 2) == 0; + } + + void adjustSZP(uint8_t value) { + adjustSign(value); + adjustZero(value); + adjustParity(value); + } + + int buildAuxiliaryCarryIndex(uint8_t value, int calculation) { + return ((A() & 0x88) >> 1) | ((value & 0x88) >> 2) | ((calculation & 0x88) >> 3); + } + + void adjustAuxiliaryCarryAdd(uint8_t value, int calculation) { + auto index = buildAuxiliaryCarryIndex(value, calculation); + F().AC = m_halfCarryTableAdd[index & 0x7]; + } + + void adjustAuxiliaryCarrySub(uint8_t value, int calculation) { + auto index = buildAuxiliaryCarryIndex(value, calculation); + F().AC = !m_halfCarryTableSub[index & 0x7]; + } + + void postIncrement(uint8_t value) { + adjustSZP(value); + F().AC = (value & 0x0f) == 0; + } + + void postDecrement(uint8_t value) { + adjustSZP(value); + F().AC = (value & 0x0f) != 0xf; + } + + static Instruction INS(instruction_t method, AddressingMode mode, std::string disassembly, int cycles); + Instruction UNKNOWN(); + + void installInstructions(); + + // + + void compare(uint8_t value) { + uint16_t subtraction = A() - value; + adjustSZP((uint8_t)subtraction); + adjustAuxiliaryCarrySub(value, subtraction); + F().C = subtraction > 0xff; + } + + void callAddress(uint16_t address) { + register16_t saved = pc; + saved.word += 2; + pushWord(saved); + pc.word = address; + } + + void restart(uint8_t position) { + uint16_t address = position << 3; + pushWord(pc); + pc.word = address; + } + + void jmpConditional(int conditional) { + auto destination = fetchWord(); + if (conditional) + pc.word = destination; + } + + void callConditional(int condition) { + if (condition) { + call(); + cycles += 6; + } + else { + pc.word += 2; + } + } + + void returnConditional(int condition) { + if (condition) { + ret(); + cycles += 6; + } + } + + void anda(uint8_t value) { + F().AC = (((A() | value) & 0x8) != 0); + F().C = false; + adjustSZP(A() &= value); + } + + void ora(uint8_t value) { + F().AC = F().C = false; + adjustSZP(A() |= value); + } + + void xra(uint8_t value) { + F().AC = F().C = false; + adjustSZP(A() ^= value); + } + + void add(uint8_t value, int carry = 0) { + register16_t sum; + sum.word = A() + value + carry; + adjustAuxiliaryCarryAdd(value, sum.word); + A() = sum.low; + F().C = sum.word > 0xff; + adjustSZP(A()); + } + + void adc(uint8_t value) { + add(value, F().C); + } + + void dad(uint16_t value) { + uint32_t sum = HL().word + value; + F().C = sum > 0xffff; + HL().word = (uint16_t)sum; + } + + void sub(uint8_t value, int carry = 0) { + register16_t difference; + difference.word = A() - value - carry; + adjustAuxiliaryCarrySub(value, difference.word); + A() = difference.low; + F().C = difference.word > 0xff; + adjustSZP(A()); + } + + void sbb(uint8_t value) { + sub(value, F().C); + } + + void mov_m_r(uint8_t value) { + m_memory.set(HL().word, value); + } + + uint8_t mov_r_m() { + return m_memory.get(HL().word); + } + + // + + void ___(); + + // Move, load, and store + + void mov_a_a() { } + void mov_a_b() { A() = B(); } + void mov_a_c() { A() = C(); } + void mov_a_d() { A() = D(); } + void mov_a_e() { A() = E(); } + void mov_a_h() { A() = H(); } + void mov_a_l() { A() = L(); } + + void mov_b_a() { B() = A(); } + void mov_b_b() { } + void mov_b_c() { B() = C(); } + void mov_b_d() { B() = D(); } + void mov_b_e() { B() = E(); } + void mov_b_h() { B() = H(); } + void mov_b_l() { B() = L(); } + + void mov_c_a() { C() = A(); } + void mov_c_b() { C() = B(); } + void mov_c_c() { } + void mov_c_d() { C() = D(); } + void mov_c_e() { C() = E(); } + void mov_c_h() { C() = H(); } + void mov_c_l() { C() = L(); } + + void mov_d_a() { D() = A(); } + void mov_d_b() { D() = B(); } + void mov_d_c() { D() = C(); } + void mov_d_d() { } + void mov_d_e() { D() = E(); } + void mov_d_h() { D() = H(); } + void mov_d_l() { D() = L(); } + + void mov_e_a() { E() = A(); } + void mov_e_b() { E() = B(); } + void mov_e_c() { E() = C(); } + void mov_e_d() { E() = D(); } + void mov_e_e() { } + void mov_e_h() { E() = H(); } + void mov_e_l() { E() = L(); } + + void mov_h_a() { H() = A(); } + void mov_h_b() { H() = B(); } + void mov_h_c() { H() = C(); } + void mov_h_d() { H() = D(); } + void mov_h_e() { H() = E(); } + void mov_h_h() { } + void mov_h_l() { H() = L(); } + + void mov_l_a() { L() = A(); } + void mov_l_b() { L() = B(); } + void mov_l_c() { L() = C(); } + void mov_l_d() { L() = D(); } + void mov_l_e() { L() = E(); } + void mov_l_h() { L() = H(); } + void mov_l_l() { } + + void mov_m_a() { mov_m_r(A()); } + void mov_m_b() { mov_m_r(B()); } + void mov_m_c() { mov_m_r(C()); } + void mov_m_d() { mov_m_r(D()); } + void mov_m_e() { mov_m_r(E()); } + void mov_m_h() { mov_m_r(H()); } + void mov_m_l() { mov_m_r(L()); } + + void mov_a_m() { A() = mov_r_m(); } + void mov_b_m() { B() = mov_r_m(); } + void mov_c_m() { C() = mov_r_m(); } + void mov_d_m() { D() = mov_r_m(); } + void mov_e_m() { E() = mov_r_m(); } + void mov_h_m() { H() = mov_r_m(); } + void mov_l_m() { L() = mov_r_m(); } + + void mvi_a() { A() = fetchByte(); } + void mvi_b() { B() = fetchByte(); } + void mvi_c() { C() = fetchByte(); } + void mvi_d() { D() = fetchByte(); } + void mvi_e() { E() = fetchByte(); } + void mvi_h() { H() = fetchByte(); } + void mvi_l() { L() = fetchByte(); } + + void mvi_m() { + auto data = fetchByte(); + m_memory.set(HL().word, data); + } + + void lxi_b() { BC().word = fetchWord(); } + void lxi_d() { DE().word = fetchWord(); } + void lxi_h() { HL().word = fetchWord(); } + + void stax_b() { m_memory.set(BC().word, A()); } + void stax_d() { m_memory.set(DE().word, A()); } + + void ldax_b() { A() = m_memory.get(BC().word); } + void ldax_d() { A() = m_memory.get(DE().word); } + + void sta() { + auto destination = fetchWord(); + m_memory.set(destination, A()); + } + + void lda() { + auto source = fetchWord(); + A() = m_memory.get(source); + } + + void shld() { + auto destination = fetchWord(); + setWord(destination, HL()); + } + + void lhld() { + HL() = getWord(fetchWord()); + } + + void xchg() { + std::swap(DE(), HL()); + } + + // stack ops + + void push_b() { pushWord(BC()); } + void push_d() { pushWord(DE()); } + void push_h() { pushWord(HL()); } + + void push_psw() { + register16_t pair; + pair.low = F(); + pair.high = A(); + pushWord(pair); + } + + void pop_b() { BC() = popWord(); } + void pop_d() { DE() = popWord(); } + void pop_h() { HL() = popWord(); } + + void pop_psw() { + auto af = popWord(); + A() = af.high; + F() = af.low; + } + + void xhtl() { + auto tos = getWord(sp.word); + setWord(sp.word, HL()); + HL() = tos; + } + + void sphl() { + sp = HL(); + } + + void lxi_sp() { + sp.word = fetchWord(); + } + + void inx_sp() { ++sp.word; } + void dcx_sp() { --sp.word; } + + // jump + + void jmp() { jmpConditional(true); } + + void jc() { jmpConditional(F().C); } + void jnc() { jmpConditional(!F().C); } + + void jz() { jmpConditional(F().Z); } + void jnz() { jmpConditional(!F().Z); } + + void jpe() { jmpConditional(F().P); } + void jpo() { jmpConditional(!F().P); } + + void jm() { jmpConditional(F().S); } + void jp() { jmpConditional(!F().S); } + + void pchl() { + pc = HL(); + } + + // call + + void call() { + auto destination = getWord(pc.word); + callAddress(destination.word); + } + + void cc() { callConditional(F().C); } + void cnc() { callConditional(!F().C); } + + void cpe() { callConditional(F().P); } + void cpo() { callConditional(!F().P); } + + void cz() { callConditional(F().Z); } + void cnz() { callConditional(!F().Z); } + + void cm() { callConditional(F().S); } + void cp() { callConditional(!F().S); } + + // return + + void ret() { + pc = popWord(); + } + + void rc() { returnConditional(F().C); } + void rnc() { returnConditional(!F().C); } + + void rz() { returnConditional(F().Z); } + void rnz() { returnConditional(!F().Z); } + + void rpe() { returnConditional(F().P); } + void rpo() { returnConditional(!F().P); } + + void rm() { returnConditional(F().S); } + void rp() { returnConditional(!F().S); } + + // restart + + void rst_0() { restart(0); } + void rst_1() { restart(1); } + void rst_2() { restart(2); } + void rst_3() { restart(3); } + void rst_4() { restart(4); } + void rst_5() { restart(5); } + void rst_6() { restart(6); } + void rst_7() { restart(7); } + + // increment and decrement + + void inr_a() { postIncrement(++A()); } + void inr_b() { postIncrement(++B()); } + void inr_c() { postIncrement(++C()); } + void inr_d() { postIncrement(++D()); } + void inr_e() { postIncrement(++E()); } + void inr_h() { postIncrement(++H()); } + void inr_l() { postIncrement(++L()); } + + void inr_m() { + auto value = m_memory.get(HL().word); + postIncrement(++value); + m_memory.set(HL().word, value); + } + + void dcr_a() { postDecrement(--A()); } + void dcr_b() { postDecrement(--B()); } + void dcr_c() { postDecrement(--C()); } + void dcr_d() { postDecrement(--D()); } + void dcr_e() { postDecrement(--E()); } + void dcr_h() { postDecrement(--H()); } + void dcr_l() { postDecrement(--L()); } + + void dcr_m() { + auto value = m_memory.get(HL().word); + postDecrement(--value); + m_memory.set(HL().word, value); + } + + void inx_b() { ++BC().word; } + void inx_d() { ++DE().word; } + void inx_h() { ++HL().word; } + + void dcx_b() { --BC().word; } + void dcx_d() { --DE().word; } + void dcx_h() { --HL().word; } + + // add + + void add_a() { add(A()); } + void add_b() { add(B()); } + void add_c() { add(C()); } + void add_d() { add(D()); } + void add_e() { add(E()); } + void add_h() { add(H()); } + void add_l() { add(L()); } + + void add_m() { + auto value = m_memory.get(HL().word); + add(value); + } + + void adi() { add(fetchByte()); } + + void adc_a() { adc(A()); } + void adc_b() { adc(B()); } + void adc_c() { adc(C()); } + void adc_d() { adc(D()); } + void adc_e() { adc(E()); } + void adc_h() { adc(H()); } + void adc_l() { adc(L()); } + + void adc_m() { + auto value = m_memory.get(HL().word); + adc(value); + } + + void aci() { adc(fetchByte()); } + + void dad_b() { dad(BC().word); } + void dad_d() { dad(DE().word); } + void dad_h() { dad(HL().word); } + void dad_sp() { dad(sp.word); } + + // subtract + + void sub_a() { sub(A()); } + void sub_b() { sub(B()); } + void sub_c() { sub(C()); } + void sub_d() { sub(D()); } + void sub_e() { sub(E()); } + void sub_h() { sub(H()); } + void sub_l() { sub(L()); } + + void sub_m() { + auto value = m_memory.get(HL().word); + sub(value); + } + + void sbb_a() { sbb(A()); } + void sbb_b() { sbb(B()); } + void sbb_c() { sbb(C()); } + void sbb_d() { sbb(D()); } + void sbb_e() { sbb(E()); } + void sbb_h() { sbb(H()); } + void sbb_l() { sbb(L()); } + + void sbb_m() { + auto value = m_memory.get(HL().word); + sbb(value); + } + + void sbi() { + auto value = fetchByte(); + sbb(value); + } + + void sui() { + auto value = fetchByte(); + sub(value); + } + + // logical + + void ana_a() { anda(A()); } + void ana_b() { anda(B()); } + void ana_c() { anda(C()); } + void ana_d() { anda(D()); } + void ana_e() { anda(E()); } + void ana_h() { anda(H()); } + void ana_l() { anda(L()); } + + void ana_m() { + auto value = m_memory.get(HL().word); + anda(value); + } + + void ani() { anda(fetchByte()); } + + void xra_a() { xra(A()); } + void xra_b() { xra(B()); } + void xra_c() { xra(C()); } + void xra_d() { xra(D()); } + void xra_e() { xra(E()); } + void xra_h() { xra(H()); } + void xra_l() { xra(L()); } + + void xra_m() { + auto value = m_memory.get(HL().word); + xra(value); + } + + void xri() { xra(fetchByte()); } + + void ora_a() { ora(A()); } + void ora_b() { ora(B()); } + void ora_c() { ora(C()); } + void ora_d() { ora(D()); } + void ora_e() { ora(E()); } + void ora_h() { ora(H()); } + void ora_l() { ora(L()); } + + void ora_m() { + auto value = m_memory.get(HL().word); + ora(value); + } + + void ori() { ora(fetchByte()); } + + void cmp_a() { compare(A()); } + void cmp_b() { compare(B()); } + void cmp_c() { compare(C()); } + void cmp_d() { compare(D()); } + void cmp_e() { compare(E()); } + void cmp_h() { compare(H()); } + void cmp_l() { compare(L()); } + + void cmp_m() { + auto value = m_memory.get(HL().word); + compare(value); + } + + void cpi() { compare(fetchByte()); } + + // rotate + + void rlc() { + auto carry = A() & 0x80; + A() <<= 1; + A() |= carry >> 7; + F().C = carry != 0; + } + + void rrc() { + auto carry = A() & 1; + A() >>= 1; + A() |= carry << 7; + F().C = carry != 0; + } + + void ral() { + auto carry = A() & 0x80; + A() <<= 1; + A() |= (uint8_t)F().C; + F().C = carry != 0; + } + + void rar() { + auto carry = A() & 1; + A() >>= 1; + A() |= F().C << 7; + F().C = carry != 0; + } + + // specials + + void cma() { A() ^= 0xff; } + void stc() { F().C = true; } + void cmc() { F().C = !F().C; } + + void daa() { + auto carry = F().C; + uint8_t addition = 0; + if (F().AC || lowNibble(A()) > 9) { + addition = 0x6; + } + if (F().C || highNibble(A()) > 9 || (highNibble(A()) >= 9 && lowNibble(A()) > 9)) { + addition |= 0x60; + carry = true; + } + add(addition); + F().C = carry; + } + + // input/output + + void out() { m_ports.write(fetchByte(), A()); } + void in() { A() = m_ports.read(fetchByte()); } + + // control + + void ei() { enableInterrupts(); } + void di() { disableInterrupts(); } + + void nop() {} + + void hlt() { m_halted = true; } + }; +} \ No newline at end of file diff --git a/Intel8080/inc/PortEventArgs.h b/Intel8080/inc/PortEventArgs.h new file mode 100644 index 0000000..3455770 --- /dev/null +++ b/Intel8080/inc/PortEventArgs.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace EightBit { + class PortEventArgs { + public: + PortEventArgs(uint8_t port) + : m_port(port) {} + + uint8_t getPort() const { + return m_port; + } + + private: + uint8_t m_port; + }; +} \ No newline at end of file diff --git a/Intel8080/inc/Profiler.h b/Intel8080/inc/Profiler.h new file mode 100644 index 0000000..90a4986 --- /dev/null +++ b/Intel8080/inc/Profiler.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +namespace EightBit { + class Profiler { + public: + Profiler(); + ~Profiler(); + + void addInstruction(uint8_t instruction); + void addAddress(uint16_t address); + + void dump() const; + + private: + std::array m_instructions; + std::array m_addresses; + + void dumpInstructionProfiles() const; + void dumpAddressProfiles() const; + }; +} \ No newline at end of file diff --git a/Intel8080/inc/StatusFlags.h b/Intel8080/inc/StatusFlags.h new file mode 100644 index 0000000..c9c05ec --- /dev/null +++ b/Intel8080/inc/StatusFlags.h @@ -0,0 +1,72 @@ +#pragma once + +#include +#include + +namespace EightBit { + struct StatusFlags { + + bool S; + bool Z; + bool AC; + bool P; + bool C; + + enum StatusBits { + Sign = 0x80, // S + Zero = 0x40, // Z + AuxiliaryCarry = 0x10, // AC + Parity = 0x4, // Z + Carry = 0x1, // S + }; + + StatusFlags(uint8_t value) { + S = (value & StatusBits::Sign) != 0; + Z = (value & StatusBits::Zero) != 0; + AC = (value & StatusBits::AuxiliaryCarry) != 0; + P = (value & StatusBits::Parity) != 0; + C = (value & StatusBits::Carry) != 0; + } + + operator uint8_t() const { + + uint8_t flags = 0; + + if (S) + flags |= StatusBits::Sign; + + if (Z) + flags |= StatusBits::Zero; + + flags &= ~0x20; // Reserved off + + if (AC) + flags |= StatusBits::AuxiliaryCarry; + + flags &= ~0x8; // Reserved off + + if (P) + flags |= StatusBits::Parity; + + flags |= 0x2; // Reserved on + + if (C) + flags |= StatusBits::Carry; + + return flags; + } + + operator std::string() const { + std::string returned; + returned += S ? "S" : "-"; + returned += Z ? "Z" : "-"; + returned += "0"; + returned += AC ? "A" : "-"; + returned += "0"; + returned += P ? "P" : "-"; + returned += "1"; + returned += C ? "C" : "-"; + return returned; + } + }; +} \ No newline at end of file diff --git a/Intel8080/src/Disassembler.cpp b/Intel8080/src/Disassembler.cpp new file mode 100644 index 0000000..d0b3bc2 --- /dev/null +++ b/Intel8080/src/Disassembler.cpp @@ -0,0 +1,112 @@ +#include "stdafx.h" +#include "Disassembler.h" + +#include +#include +#include + +#include "Memory.h" +#include "Intel8080.h" +#include "StatusFlags.h" + +EightBit::Disassembler::Disassembler() { +} + +std::string EightBit::Disassembler::state(Intel8080& cpu) { + + auto pc = cpu.getProgramCounter(); + auto sp = cpu.getStackPointer(); + + auto a = cpu.A(); + auto f = cpu.F(); + + auto b = cpu.B(); + auto c = cpu.C(); + + auto d = cpu.D(); + auto e = cpu.E(); + + auto h = cpu.H(); + auto l = cpu.L(); + + std::ostringstream output; + + output + << "PC=" << hex(pc.word) + << " " + << "SP=" << hex(sp.word) + << " " << "A=" << hex(a) << " " << "F=" << (std::string)f + << " " << "B=" << hex(b) << " " << "C=" << hex(c) + << " " << "D=" << hex(d) << " " << "E=" << hex(e) + << " " << "H=" << hex(h) << " " << "L=" << hex(l); + + return output.str(); +} + +std::string EightBit::Disassembler::disassemble(Intel8080& cpu) { + + const auto& memory = cpu.getMemory(); + auto pc = cpu.getProgramCounter(); + auto opcode = memory.peek(pc.word); + const auto& instruction = cpu.getInstructions()[opcode]; + + std::ostringstream output; + + // hex opcode + output << hex(opcode); + + // hex raw operand + switch (instruction.mode) { + case Intel8080::Immediate: + output << hex(memory.peek(pc.word + 1)); + break; + case Intel8080::Absolute: + output << hex(memory.peek(pc.word + 1)); + output << hex(memory.peek(pc.word + 2)); + break; + default: + break; + } + output << "\t"; + + // base disassembly + output << instruction.disassembly; + + // disassembly operand + switch (instruction.mode) { + case Intel8080::Immediate: + output << hex(memory.peek(pc.word + 1)); + break; + case Intel8080::Absolute: + output << hex(cpu.getWord(pc.word + 1).word); + break; + default: + break; + } + + return output.str(); +} + +std::string EightBit::Disassembler::hex(uint8_t value) { + std::ostringstream output; + output << std::hex << std::setw(2) << std::setfill('0') << (int)value; + return output.str(); +} + +std::string EightBit::Disassembler::hex(uint16_t value) { + std::ostringstream output; + output << std::hex << std::setw(4) << std::setfill('0') << (int)value; + return output.str(); +} + +std::string EightBit::Disassembler::binary(uint8_t value) { + std::ostringstream output; + output << std::bitset<8>(value); + return output.str(); +} + +std::string EightBit::Disassembler::invalid(uint8_t value) { + std::ostringstream output; + output << "Invalid instruction: " << hex(value) << "(" << binary(value) << ")"; + return output.str(); +} \ No newline at end of file diff --git a/Intel8080/src/InputOutput.cpp b/Intel8080/src/InputOutput.cpp new file mode 100644 index 0000000..9444434 --- /dev/null +++ b/Intel8080/src/InputOutput.cpp @@ -0,0 +1,34 @@ +#include "stdafx.h" +#include "InputOutput.h" + +EightBit::InputOutput::InputOutput() { +} + +uint8_t EightBit::InputOutput::readInputPort(uint8_t port) { + OnReadingPort(port); + auto value = input[port]; + OnReadPort(port); + return value; +} + +void EightBit::InputOutput::writeOutputPort(uint8_t port, uint8_t value) { + OnWritingPort(port); + output[port] = value; + OnWrittenPort(port); +} + +void EightBit::InputOutput::OnReadingPort(uint8_t port) { + ReadingPort.fire(PortEventArgs(port)); +} + +void EightBit::InputOutput::OnReadPort(uint8_t port) { + ReadPort.fire(PortEventArgs(port)); +} + +void EightBit::InputOutput::OnWritingPort(uint8_t port) { + WritingPort.fire(PortEventArgs(port)); +} + +void EightBit::InputOutput::OnWrittenPort(uint8_t port) { + WrittenPort.fire(PortEventArgs(port)); +} diff --git a/Intel8080/src/Intel8080.cpp b/Intel8080/src/Intel8080.cpp new file mode 100644 index 0000000..6350ff6 --- /dev/null +++ b/Intel8080/src/Intel8080.cpp @@ -0,0 +1,83 @@ +#include "stdafx.h" +#include "Intel8080.h" + +#include "Memory.h" +#include "Disassembler.h" + +EightBit::Intel8080::Intel8080(Memory& memory, InputOutput& ports) +: Processor(memory), + a(0), + f(0), + m_interrupt(false) { + bc.word = de.word = hl.word = 0; + installInstructions(); +} + +EightBit::Intel8080::Instruction EightBit::Intel8080::INS(instruction_t method, AddressingMode mode, std::string disassembly, int cycles) { + Intel8080::Instruction returnValue; + returnValue.vector = method; + returnValue.mode = mode; + returnValue.disassembly = disassembly; + returnValue.count = cycles; + return returnValue; +} + +EightBit::Intel8080::Instruction EightBit::Intel8080::UNKNOWN() { + Intel8080::Instruction returnValue; + returnValue.vector = std::bind(&Intel8080::___, this); + returnValue.mode = Unknown; + returnValue.disassembly = ""; + returnValue.count = 0; + return returnValue; +} + +#define BIND(method) std::bind(&Intel8080:: method, this) + +void EightBit::Intel8080::installInstructions() { + instructions = { + //// 0 1 2 3 4 5 6 7 8 9 A B C D E F + /* 0 */ INS(BIND(nop), Implied, "NOP", 4), INS(BIND(lxi_b), Absolute, "LXI B,", 10), INS(BIND(stax_b), Implied, "STAX B", 7), INS(BIND(inx_b), Implied, "INX B", 5), INS(BIND(inr_b), Implied, "INR B", 5), INS(BIND(dcr_b), Implied, "DCR B", 5), INS(BIND(mvi_b), Immediate, "MVI B,", 7), INS(BIND(rlc), Implied, "RLC", 4), UNKNOWN(), INS(BIND(dad_b), Implied, "DAD B", 10), INS(BIND(ldax_b), Implied, "LDAX B", 7), INS(BIND(dcx_b), Implied, "DCX B", 5), INS(BIND(inr_c), Implied, "INR C", 5), INS(BIND(dcr_c), Implied, "DCR C", 5), INS(BIND(mvi_c), Immediate, "MVI C,", 7), INS(BIND(rrc), Implied, "RRC", 4), // 0 + /* 1 */ UNKNOWN(), INS(BIND(lxi_d), Absolute, "LXI D,", 10), INS(BIND(stax_d), Implied, "STAX D", 7), INS(BIND(inx_d), Implied, "INX D", 5), INS(BIND(inr_d), Implied, "INR D", 5), INS(BIND(dcr_d), Implied, "DCR D", 5), INS(BIND(mvi_d), Immediate, "MVI D,", 7), INS(BIND(ral), Implied, "RAL", 4), UNKNOWN(), INS(BIND(dad_d), Implied, "DAD D", 10), INS(BIND(ldax_d), Implied, "LDAX D", 7), INS(BIND(dcx_d), Implied, "DCX D", 5), INS(BIND(inr_e), Implied, "INR E", 5), INS(BIND(dcr_e), Implied, "DCR E", 5), INS(BIND(mvi_e), Immediate, "MVI E,", 7), INS(BIND(rar), Implied, "RAR", 4), // 1 + /* 2 */ UNKNOWN(), INS(BIND(lxi_h), Absolute, "LXI H,", 10), INS(BIND(shld), Absolute, "SHLD", 16), INS(BIND(inx_h), Implied, "INX H", 5), INS(BIND(inr_h), Implied, "INR H", 5), INS(BIND(dcr_h), Implied, "DCR H", 5), INS(BIND(mvi_h), Immediate, "MVI H,",7), INS(BIND(daa), Implied, "DAA", 4), UNKNOWN(), INS(BIND(dad_h), Implied, "DAD H", 10), INS(BIND(lhld), Absolute, "LHLD ", 16), INS(BIND(dcx_h), Implied, "DCX H", 5), INS(BIND(inr_l), Implied, "INR L", 5), INS(BIND(dcr_l), Implied, "DCR L", 5), INS(BIND(mvi_l), Immediate, "MVI L,", 7), INS(BIND(cma), Implied, "CMA", 4), // 2 + /* 3 */ UNKNOWN(), INS(BIND(lxi_sp), Absolute, "LXI SP,", 10), INS(BIND(sta), Absolute, "STA ", 13), INS(BIND(inx_sp), Implied, "INX SP", 5), INS(BIND(inr_m), Implied, "INR M", 10), INS(BIND(dcr_m), Implied, "DCR M", 10), INS(BIND(mvi_m), Immediate, "MVI M,", 10), INS(BIND(stc), Implied, "STC", 4), UNKNOWN(), INS(BIND(dad_sp), Implied, "DAD SP", 10), INS(BIND(lda), Absolute, "LDA ", 13), INS(BIND(dcx_sp), Implied, "DCX SP", 5), INS(BIND(inr_a), Implied, "INR A", 5), INS(BIND(dcr_a), Implied, "DCR A", 5), INS(BIND(mvi_a), Immediate, "MVI A,", 7), INS(BIND(cmc), Implied, "CMC", 4), // 3 + + /* 4 */ INS(BIND(mov_b_b), Implied, "MOV B,B", 5), INS(BIND(mov_b_c), Implied, "MOV B,C", 5), INS(BIND(mov_b_d), Implied, "MOV B,D", 5), INS(BIND(mov_b_e), Implied, "MOV B,E", 5), INS(BIND(mov_b_h), Implied, "MOV B,H", 5), INS(BIND(mov_b_l), Implied, "MOV B,L", 5), INS(BIND(mov_b_m), Implied, "MOV B,M", 7), INS(BIND(mov_b_a), Implied, "MOV B,A", 5), INS(BIND(mov_c_b), Implied, "MOV C,B", 5), INS(BIND(mov_c_c), Implied, "MOV C,C", 5), INS(BIND(mov_c_d), Implied, "MOV C,D", 5), INS(BIND(mov_c_e), Implied, "MOV C,E", 5), INS(BIND(mov_c_h), Implied, "MOV C,H", 5), INS(BIND(mov_c_l), Implied, "MOV C,L", 5), INS(BIND(mov_c_m), Implied, "MOV C,M", 7), INS(BIND(mov_c_a), Implied, "MOV C,A", 5), // 4 + /* 5 */ INS(BIND(mov_d_b), Implied, "MOV D,B", 5), INS(BIND(mov_d_c), Implied, "MOV D,C", 5), INS(BIND(mov_d_d), Implied, "MOV D,D", 5), INS(BIND(mov_d_e), Implied, "MOV D,E", 5), INS(BIND(mov_d_h), Implied, "MOV D,H", 5), INS(BIND(mov_d_l), Implied, "MOV D,L", 5), INS(BIND(mov_d_m), Implied, "MOV D,M", 7), INS(BIND(mov_d_a), Implied, "MOV D,A", 5), INS(BIND(mov_e_b), Implied, "MOV E,B", 5), INS(BIND(mov_e_c), Implied, "MOV E,C", 5), INS(BIND(mov_e_d), Implied, "MOV E,D", 5), INS(BIND(mov_e_e), Implied, "MOV E,E", 5), INS(BIND(mov_e_h), Implied, "MOV E,H", 5), INS(BIND(mov_e_l), Implied, "MOV E,L", 5), INS(BIND(mov_e_m), Implied, "MOV E,M", 7), INS(BIND(mov_e_a), Implied, "MOV E,A", 5), // 5 + /* 6 */ INS(BIND(mov_h_b), Implied, "MOV H,B", 5), INS(BIND(mov_h_c), Implied, "MOV H,C", 5), INS(BIND(mov_h_d), Implied, "MOV H,D", 5), INS(BIND(mov_h_e), Implied, "MOV H,E", 5), INS(BIND(mov_h_h), Implied, "MOV H,H", 5), INS(BIND(mov_h_l), Implied, "MOV H,L", 5), INS(BIND(mov_h_m), Implied, "MOV H,M", 7), INS(BIND(mov_h_a), Implied, "MOV H,A", 5), INS(BIND(mov_l_b), Implied, "MOV L,B", 5), INS(BIND(mov_l_c), Implied, "MOV L,C", 5), INS(BIND(mov_l_d), Implied, "MOV L,D", 5), INS(BIND(mov_l_e), Implied, "MOV L,E", 5), INS(BIND(mov_l_h), Implied, "MOV L,H", 5), INS(BIND(mov_l_l), Implied, "MOV L,L", 5), INS(BIND(mov_l_m), Implied, "MOV L,M", 7), INS(BIND(mov_l_a), Implied, "MOV L,A", 5), // 6 + /* 7 */ INS(BIND(mov_m_b), Implied, "MOV M,B", 7), INS(BIND(mov_m_c), Implied, "MOV M,C", 7), INS(BIND(mov_m_d), Implied, "MOV M,D", 7), INS(BIND(mov_m_e), Implied, "MOV M,E", 7), INS(BIND(mov_m_h), Implied, "MOV M,H", 7), INS(BIND(mov_m_l), Implied, "MOV M,L", 7), INS(BIND(hlt), Implied, "HLT", 7), INS(BIND(mov_m_a), Implied, "MOV M,A", 7), INS(BIND(mov_a_b), Implied, "MOV A,B", 5), INS(BIND(mov_a_c), Implied, "MOV A,C", 5), INS(BIND(mov_a_d), Implied, "MOV A,D", 5), INS(BIND(mov_a_e), Implied, "MOV A,E", 5), INS(BIND(mov_a_h), Implied, "MOV A,H", 5), INS(BIND(mov_a_l), Implied, "MOV A,L", 5), INS(BIND(mov_a_m), Implied, "MOV A,M", 7), INS(BIND(mov_a_a), Implied, "MOV A,A", 5), // 7 + + /* 8 */ INS(BIND(add_b), Implied, "ADD B", 4), INS(BIND(add_c), Implied, "ADD C", 4), INS(BIND(add_d), Implied, "ADD D", 4), INS(BIND(add_e), Implied, "ADD E", 4), INS(BIND(add_h), Implied, "ADD H", 4), INS(BIND(add_l), Implied, "ADD L", 4), INS(BIND(add_m), Implied, "ADD M", 7), INS(BIND(add_a), Implied, "ADD A", 4), INS(BIND(adc_b), Implied, "ADC B", 4), INS(BIND(adc_c), Implied, "ADC C", 4), INS(BIND(adc_d), Implied, "ADC D", 4), INS(BIND(adc_e), Implied, "ADC E", 4), INS(BIND(adc_h), Implied, "ADC H", 4), INS(BIND(adc_l), Implied, "ADC L", 4), INS(BIND(adc_m), Implied, "ADC M", 4), INS(BIND(adc_a), Implied, "ADC A", 4), // 8 + /* 9 */ INS(BIND(sub_b), Implied, "SUB B", 4), INS(BIND(sub_c), Implied, "SUB C", 4), INS(BIND(sub_d), Implied, "SUB D", 4), INS(BIND(sub_e), Implied, "SUB E", 4), INS(BIND(sub_h), Implied, "SUB H", 4), INS(BIND(sub_l), Implied, "SUB L", 4), INS(BIND(sub_m), Implied, "SUB M", 7), INS(BIND(sub_a), Implied, "SUB A", 4), INS(BIND(sbb_b), Implied, "SBB B", 4), INS(BIND(sbb_c), Implied, "SBB C", 4), INS(BIND(sbb_d), Implied, "SBB D", 4), INS(BIND(sbb_e), Implied, "SBB E", 4), INS(BIND(sbb_h), Implied, "SBB H", 4), INS(BIND(sbb_l), Implied, "SBB L", 4), INS(BIND(sbb_m), Implied, "SBB M", 4), INS(BIND(sbb_a), Implied, "SBB A", 4), // 9 + /* A */ INS(BIND(ana_b), Implied, "ANA B", 4), INS(BIND(ana_c), Implied, "ANA C", 4), INS(BIND(ana_d), Implied, "ANA D", 4), INS(BIND(ana_e), Implied, "ANA E", 4), INS(BIND(ana_h), Implied, "ANA H", 4), INS(BIND(ana_l), Implied, "ANA L", 4), INS(BIND(ana_m), Implied, "ANA M", 7), INS(BIND(ana_a), Implied, "ANA A", 4), INS(BIND(xra_b), Implied, "XRA B", 4), INS(BIND(xra_c), Implied, "XRA C", 4), INS(BIND(xra_d), Implied, "XRA D", 4), INS(BIND(xra_e), Implied, "XRA E", 4), INS(BIND(xra_h), Implied, "XRA H", 4), INS(BIND(xra_l), Implied, "XRA L", 4), INS(BIND(xra_m), Implied, "XRA M", 4), INS(BIND(xra_a), Implied, "XRA A", 4), // A + /* B */ INS(BIND(ora_b), Implied, "ORA B", 4), INS(BIND(ora_c), Implied, "ORA C", 4), INS(BIND(ora_d), Implied, "ORA D", 4), INS(BIND(ora_e), Implied, "ORA E", 4), INS(BIND(ora_h), Implied, "ORA H", 4), INS(BIND(ora_l), Implied, "ORA L", 4), INS(BIND(ora_m), Implied, "ORA M", 7), INS(BIND(ora_a), Implied, "ORA A", 4), INS(BIND(cmp_b), Implied, "CMP B", 4), INS(BIND(cmp_c), Implied, "CMP C", 4), INS(BIND(cmp_d), Implied, "CMP D", 4), INS(BIND(cmp_e), Implied, "CMP E", 4), INS(BIND(cmp_h), Implied, "CMP H", 4), INS(BIND(cmp_l), Implied, "CMP L", 4), INS(BIND(cmp_m), Implied, "CMP M", 4), INS(BIND(cmp_a), Implied, "CMP A", 4), // B + + /* C */ INS(BIND(rnz), Implied, "RNZ", 5), INS(BIND(pop_b), Implied, "POP B", 10), INS(BIND(jnz), Absolute, "JNZ ", 10), INS(BIND(jmp), Absolute, "JMP ", 10), INS(BIND(cnz), Absolute, "CNZ ", 11), INS(BIND(push_b), Implied, "PUSH B", 11), INS(BIND(adi), Immediate, "ADI ", 7), INS(BIND(rst_0), Implied, "RST 0", 11), INS(BIND(rz), Implied, "RZ", 11), INS(BIND(ret), Implied, "RET", 10), INS(BIND(jz), Absolute, "JZ ", 10), UNKNOWN(), INS(BIND(cz), Absolute, "CZ ", 11), INS(BIND(call), Absolute, "CALL ", 17), INS(BIND(aci), Immediate, "ACI ", 7), INS(BIND(rst_1), Implied, "RST 1", 11), // C + /* D */ INS(BIND(rnc), Implied, "RNC", 5), INS(BIND(pop_d), Implied, "POP D", 10), INS(BIND(jnc), Absolute, "JNC ", 10), INS(BIND(out), Immediate, "OUT ", 10), INS(BIND(cnc), Absolute, "CNC ", 11), INS(BIND(push_d), Implied, "PUSH D", 11), INS(BIND(sui), Immediate, "SUI ", 7), INS(BIND(rst_2), Implied, "RST 2", 11), INS(BIND(rc), Implied, "RC", 11), UNKNOWN(), INS(BIND(jc), Absolute, "JC ", 10), INS(BIND(in), Immediate, "IN ", 10), INS(BIND(cc), Absolute, "CC ", 11), UNKNOWN(), INS(BIND(sbi), Immediate, "SBI ", 7), INS(BIND(rst_3), Implied, "RST 3", 11), // D + /* E */ INS(BIND(rpo), Implied, "RPO", 5), INS(BIND(pop_h), Implied, "POP H", 10), INS(BIND(jpo), Absolute, "JPO ", 10), INS(BIND(xhtl), Implied, "XHTL", 18), INS(BIND(cpo), Absolute, "CPO ", 11), INS(BIND(push_h), Implied, "PUSH H", 11), INS(BIND(ani), Immediate, "ANI ", 7), INS(BIND(rst_4), Implied, "RST 4", 11), INS(BIND(rpe), Implied, "RPE", 11), INS(BIND(pchl), Implied, "PCHL", 5), INS(BIND(jpe), Absolute, "JPE ", 10), INS(BIND(xchg), Implied, "XCHG", 4), INS(BIND(cpe), Absolute, "CPE ", 11), UNKNOWN(), INS(BIND(xri), Immediate, "XRI ", 7), INS(BIND(rst_5), Implied, "RST 5", 11), // E + /* F */ INS(BIND(rp), Implied, "RP", 5), INS(BIND(pop_psw), Implied, "POP PSW", 10), INS(BIND(jp), Absolute, "JP ", 10), INS(BIND(di), Implied, "DI ", 4), INS(BIND(cp), Absolute, "CP ", 11), INS(BIND(push_psw), Implied, "PUSH PSW", 11), INS(BIND(ori), Immediate, "ORI ", 7), INS(BIND(rst_6), Implied, "RST 6", 11), INS(BIND(rm), Implied, "RM", 11), INS(BIND(sphl), Implied, "SPHL", 5), INS(BIND(jm), Absolute, "JM ", 10), INS(BIND(ei), Implied, "EI", 4), INS(BIND(cm), Absolute, "CM ", 11), UNKNOWN(), INS(BIND(cpi), Immediate, "CPI ", 7), INS(BIND(rst_7), Implied, "RST 7", 11), // F + }; +} + +void EightBit::Intel8080::initialise() { + Processor::initialise(); + bc.word = de.word = hl.word = 0; + a = f = 0; +} + +int EightBit::Intel8080::step() { + ExecutingInstruction.fire(*this); + return execute(fetchByte()); +} + +int EightBit::Intel8080::execute(uint8_t opcode) { + const auto& instruction = instructions[opcode]; + return execute(instruction); +} + +// + +void EightBit::Intel8080::___() { + auto opcode = m_memory.get(pc.word - 1); + auto message = Disassembler::invalid(opcode); + throw std::domain_error(message); +} diff --git a/Intel8080/src/Intel8080.vcxproj b/Intel8080/src/Intel8080.vcxproj new file mode 100644 index 0000000..640ca26 --- /dev/null +++ b/Intel8080/src/Intel8080.vcxproj @@ -0,0 +1,162 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {93BDC8D8-9F0D-44ED-94FB-1BE6AC4B6BD6} + Win32Proj + Win32Project1 + 10.0.14393.0 + Intel8080 + + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + ../inc;../../inc;$(VC_IncludePath);$(WindowsSDK_IncludePath) + + + ../inc;../../inc;$(VC_IncludePath);$(WindowsSDK_IncludePath) + + + ../inc;../../inc;$(VC_IncludePath);$(WindowsSDK_IncludePath) + + + ../inc;../../inc;$(VC_IncludePath);$(WindowsSDK_IncludePath) + + + + 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 + + + + + Level3 + Use + MaxSpeed + true + true + NDEBUG;_LIB;%(PreprocessorDefinitions) + + + Windows + true + true + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + \ No newline at end of file diff --git a/Intel8080/src/Intel8080.vcxproj.filters b/Intel8080/src/Intel8080.vcxproj.filters new file mode 100644 index 0000000..0f06fb3 --- /dev/null +++ b/Intel8080/src/Intel8080.vcxproj.filters @@ -0,0 +1,53 @@ + + + + + {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 + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/Intel8080/src/Profiler.cpp b/Intel8080/src/Profiler.cpp new file mode 100644 index 0000000..5fd39fe --- /dev/null +++ b/Intel8080/src/Profiler.cpp @@ -0,0 +1,42 @@ +#include "stdafx.h" +#include "Profiler.h" +#include "Disassembler.h" + +EightBit::Profiler::Profiler() { + m_instructions.fill(0); + m_addresses.fill(0); +} + +EightBit::Profiler::~Profiler() { +} + +void EightBit::Profiler::addInstruction(uint8_t instruction) { + m_instructions[instruction]++; +} + +void EightBit::Profiler::addAddress(uint16_t address) { + m_addresses[address]++; +} + +void EightBit::Profiler::dump() const { + dumpInstructionProfiles(); + dumpAddressProfiles(); +} + +void EightBit::Profiler::dumpInstructionProfiles() const { + std::cout << "** instructions" << std::endl; + for (int i = 0; i < 0x100; ++i) { + auto count = m_instructions[i]; + if (count > 0) + std::cout << Disassembler::hex((uint8_t)i) << "\t" << count << std::endl; + } +} + +void EightBit::Profiler::dumpAddressProfiles() const { + std::cout << "** addresses" << std::endl; + for (int i = 0; i < 0x10000; ++i) { + auto count = m_addresses[i]; + if (count > 0) + std::cout << Disassembler::hex((uint16_t)i) << "\t" << count << std::endl; + } +} diff --git a/Intel8080/src/stdafx.cpp b/Intel8080/src/stdafx.cpp new file mode 100644 index 0000000..fd4f341 --- /dev/null +++ b/Intel8080/src/stdafx.cpp @@ -0,0 +1 @@ +#include "stdafx.h" diff --git a/Intel8080/src/stdafx.h b/Intel8080/src/stdafx.h new file mode 100644 index 0000000..d3ffbf9 --- /dev/null +++ b/Intel8080/src/stdafx.h @@ -0,0 +1,19 @@ +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include + +#include +#include + +#include +#include +#include + +#if defined(_M_X64) || defined(_M_IX86 ) +# define HOST_LITTLE_ENDIAN +#else +# define HOST_BIG_ENDIAN +#endif diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..23cb790 --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) 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 +this service 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 make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. 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. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE 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. + + 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 +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + 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 2 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision 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, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This 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. diff --git a/LR35902/inc/Disassembler.h b/LR35902/inc/Disassembler.h new file mode 100644 index 0000000..c6c3101 --- /dev/null +++ b/LR35902/inc/Disassembler.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include + +class LR35902; + +class Disassembler { +public: + Disassembler(); + + static std::string state(LR35902& cpu); + std::string disassemble(LR35902& cpu); + + static std::string flag(uint8_t value, int flag, const std::string& represents); + static std::string flags(uint8_t value); + static std::string hex(uint8_t value); + static std::string hex(uint16_t value); + static std::string binary(uint8_t value); + static std::string decimal(uint8_t value); + + static std::string invalid(uint8_t value); + +private: + mutable boost::format m_formatter; + bool m_prefixCB; + + void disassemble(std::ostringstream& output, LR35902& cpu, uint16_t pc); + + void disassembleCB( + std::ostringstream& output, + LR35902& cpu, + uint16_t pc, + std::string& specification, + int& dumpCount, + int x, int y, int z, + int p, int q); + + void disassembleOther( + std::ostringstream& output, + LR35902& cpu, + uint16_t pc, + std::string& specification, + int& dumpCount, + int x, int y, int z, + int p, int q); + + std::string RP(int rp) const; + std::string RP2(int rp) const; + std::string R(int r) const; + static std::string cc(int flag); + static std::string alu(int which); +}; diff --git a/LR35902/inc/LR35902.h b/LR35902/inc/LR35902.h new file mode 100644 index 0000000..f7e7490 --- /dev/null +++ b/LR35902/inc/LR35902.h @@ -0,0 +1,222 @@ +#pragma once + +#include + +#include "Processor.h" + +class LR35902 : public Processor { +public: + enum StatusBits { + ZF = Bit7, + NF = Bit6, + HC = Bit5, + CF = Bit4, + }; + + LR35902(Bus& memory); + + Signal ExecutingInstruction; + + void stop() { m_stopped = true; } + void start() { m_stopped = false; } + bool stopped() const { return m_stopped; } + + bool& IME() { return m_ime; } + + void di(); + void ei(); + + int interrupt(uint8_t value); + + int execute(uint8_t opcode); + int step(); + + // Mutable access to processor!! + + register16_t& AF() { + m_accumulatorFlag.low &= 0xf0; + return m_accumulatorFlag; + } + + uint8_t& A() { return AF().high; } + uint8_t& F() { return AF().low; } + + register16_t& BC() { + return m_registers[BC_IDX]; + } + + uint8_t& B() { return BC().high; } + uint8_t& C() { return BC().low; } + + register16_t& DE() { + return m_registers[DE_IDX]; + } + + uint8_t& D() { return DE().high; } + uint8_t& E() { return DE().low; } + + register16_t& HL() { + return m_registers[HL_IDX]; + } + + uint8_t& H() { return HL().high; } + uint8_t& L() { return HL().low; } + + virtual void reset(); + virtual void initialise(); + +private: + enum { BC_IDX, DE_IDX, HL_IDX }; + + std::array m_registers; + register16_t m_accumulatorFlag; + + bool m_ime; + + bool m_prefixCB; + + bool m_stopped; + + std::array m_halfCarryTableAdd = { { false, false, true, false, true, false, true, true } }; + std::array m_halfCarryTableSub = { { false, true, true, true, false, false, false, true } }; + + int fetchExecute() { + return execute(fetchByte()); + } + + void clearFlag(int flag) { F() &= ~flag; } + void setFlag(int flag) { F() |= flag; } + + void setFlag(int flag, int condition) { setFlag(flag, condition != 0); } + void setFlag(int flag, uint32_t condition) { setFlag(flag, condition != 0); } + void setFlag(int flag, bool condition) { condition ? setFlag(flag) : clearFlag(flag); } + + void clearFlag(int flag, int condition) { clearFlag(flag, condition != 0); } + void clearFlag(int flag, uint32_t condition) { clearFlag(flag, condition != 0); } + void clearFlag(int flag, bool condition) { condition ? clearFlag(flag) : setFlag(flag); } + + uint8_t& R(int r) { + switch (r) { + case 0: + return B(); + case 1: + return C(); + case 2: + return D(); + case 3: + return E(); + case 4: + return H(); + case 5: + return L(); + case 6: + return m_memory.reference(HL().word); + case 7: + return A(); + } + throw std::logic_error("Unhandled registry mechanism"); + } + + uint16_t& RP(int rp) { + switch (rp) { + case 3: + return sp; + default: + return m_registers[rp].word; + } + } + + uint16_t& RP2(int rp) { + switch (rp) { + case 3: + return AF().word; + default: + return m_registers[rp].word; + } + } + + int buildHalfCarryIndex(uint8_t before, uint8_t value, int calculation) { + return ((before & 0x88) >> 1) | ((value & 0x88) >> 2) | ((calculation & 0x88) >> 3); + } + + void adjustHalfCarryAdd(uint8_t before, uint8_t value, int calculation) { + auto index = buildHalfCarryIndex(before, value, calculation); + setFlag(HC, m_halfCarryTableAdd[index & 0x7]); + } + + void adjustHalfCarrySub(uint8_t before, uint8_t value, int calculation) { + auto index = buildHalfCarryIndex(before, value, calculation); + setFlag(HC, m_halfCarryTableSub[index & 0x7]); + } + + void executeCB(int x, int y, int z, int p, int q); + void executeOther(int x, int y, int z, int p, int q); + + void adjustZero(uint8_t value); + + void postIncrement(uint8_t value); + void postDecrement(uint8_t value); + + void restart(uint8_t address); + + void jrConditional(int conditional); + void jrConditionalFlag(int flag); + + void ret(); + void reti(); + + void returnConditional(int condition); + void returnConditionalFlag(int flag); + + void jumpConditional(int condition); + void jumpConditionalFlag(int flag); + + void call(uint16_t address); + void callConditional(uint16_t address, int condition); + void callConditionalFlag(uint16_t address, int flag); + + uint16_t sbc(uint16_t value); + uint16_t adc(uint16_t value); + + uint16_t add(uint16_t value); + + void sub(uint8_t& operand, uint8_t value, bool carry); + void sub(uint8_t& operand, uint8_t value); + void sbc(uint8_t& operand, uint8_t value); + + void add(uint8_t& operand, uint8_t value, bool carry); + void add(uint8_t& operand, uint8_t value); + void adc(uint8_t& operand, uint8_t value); + + void andr(uint8_t& operand, uint8_t value); + + void anda(uint8_t value); + void xora(uint8_t value); + void ora(uint8_t value); + void compare(uint8_t value); + + void rlca(); + void rrca(); + void rla(); + void rra(); + + void rlc(uint8_t& operand); + void rrc(uint8_t& operand); + void rl(uint8_t& operand); + void rr(uint8_t& operand); + void sla(uint8_t& operand); + void sra(uint8_t& operand); + void srl(uint8_t& operand); + + void bit(int n, uint8_t& operand); + void res(int n, uint8_t& operand); + void set(int nit, uint8_t& operand); + + void daa(); + + void scf(); + void ccf(); + void cpl(); + + void swap(uint8_t& operand); +}; \ No newline at end of file diff --git a/LR35902/inc/Profiler.h b/LR35902/inc/Profiler.h new file mode 100644 index 0000000..3f49a9b --- /dev/null +++ b/LR35902/inc/Profiler.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include "Disassembler.h" + +class LR35902; + +class Profiler { +public: + Profiler(LR35902& cpu); + + void add(uint16_t address, uint8_t instruction); + + void dump() const; + +private: + std::array m_instructions; + std::array m_addresses; + LR35902& m_cpu; + + Disassembler m_disassembler; + + void dumpInstructionProfiles() const; + void dumpAddressProfiles() const; +}; + diff --git a/LR35902/src/Disassembler.cpp b/LR35902/src/Disassembler.cpp new file mode 100644 index 0000000..85c96f4 --- /dev/null +++ b/LR35902/src/Disassembler.cpp @@ -0,0 +1,555 @@ +#include "stdafx.h" +#include "Disassembler.h" + +#include +#include +#include + +#include "Memory.h" +#include "LR35902.h" +#include "StatusFlags.h" + +Disassembler::Disassembler() { + // Disable exceptions where too many format arguments are available + m_formatter.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit); +} + +std::string Disassembler::state(LR35902& cpu) { + + auto pc = cpu.getProgramCounter(); + auto sp = cpu.getStackPointer(); + + auto a = cpu.A(); + auto f = cpu.F(); + + auto b = cpu.B(); + auto c = cpu.C(); + + auto d = cpu.D(); + auto e = cpu.E(); + + auto h = cpu.H(); + auto l = cpu.L(); + + std::ostringstream output; + + output + << "PC=" << hex(pc) + << " " + << "SP=" << hex(sp) + << " " << "A=" << hex(a) << " " << "F=" << flags(f) + << " " << "B=" << hex(b) << " " << "C=" << hex(c) + << " " << "D=" << hex(d) << " " << "E=" << hex(e) + << " " << "H=" << hex(h) << " " << "L=" << hex(l); + + return output.str(); +} + +std::string Disassembler::RP(int rp) const { + switch (rp) { + case 0: + return "BC"; + case 1: + return "DE"; + case 2: + return "HL"; + case 3: + return "SP"; + } + throw std::logic_error("Unhandled register pair"); +} + +std::string Disassembler::RP2(int rp) const { + switch (rp) { + case 0: + return "BC"; + case 1: + return "DE"; + case 2: + return "HL"; + case 3: + return "AF"; + } + throw std::logic_error("Unhandled register pair"); +} + +std::string Disassembler::R(int r) const { + switch (r) { + case 0: + return "B"; + case 1: + return "C"; + case 2: + return "D"; + case 3: + return "E"; + case 4: + return "H"; + case 5: + return "L"; + case 6: + return "(HL)"; + case 7: + return "A"; + } + throw std::logic_error("Unhandled register"); +} + +std::string Disassembler::cc(int flag) { + switch (flag) { + case 0: + return "NZ"; + case 1: + return "Z"; + case 2: + return "NC"; + case 3: + return "C"; + case 4: + return "PO"; + case 5: + return "PE"; + case 6: + return "P"; + case 7: + return "M"; + } + throw std::logic_error("Unhandled condition"); +} + +std::string Disassembler::alu(int which) { + switch (which) { + case 0: // ADD A,n + return "ADD"; + case 1: // ADC + return "ADC"; + case 2: // SUB n + return "SUB"; + case 3: // SBC A,n + return "SBC"; + case 4: // AND n + return "AND"; + case 5: // XOR n + return "XOR"; + case 6: // OR n + return "OR"; + case 7: // CP n + return "CP"; + } + throw std::logic_error("Unhandled alu operation"); +} + +std::string Disassembler::disassemble(LR35902& cpu) { + m_prefixCB = false; + std::ostringstream output; + disassemble(output, cpu, cpu.getProgramCounter()); + return output.str(); +} + +void Disassembler::disassemble(std::ostringstream& output, LR35902& cpu, uint16_t pc) { + + auto& memory = cpu.getMemory(); + auto opcode = memory.peek(pc); + + // hex opcode + output << hex(opcode); + + auto x = (opcode & 0b11000000) >> 6; + auto y = (opcode & 0b111000) >> 3; + auto z = (opcode & 0b111); + + auto p = (y & 0b110) >> 1; + auto q = (y & 1); + + auto immediate = memory.peek(pc + 1); + auto absolute = cpu.getWord(pc + 1); + auto displacement = (int8_t)immediate; + auto relative = pc + displacement + 2; + auto indexedImmediate = memory.peek(pc + 1); + + auto dumpCount = 0; + + std::string specification = ""; + + if (m_prefixCB) + disassembleCB( + output, cpu, pc, + specification, dumpCount, + x, y, z, p, q); + else + disassembleOther( + output, cpu, pc, + specification, dumpCount, + x, y, z, p, q); + + for (int i = 0; i < dumpCount; ++i) + output << hex(memory.peek(pc + i + 1)); + + output << '\t'; + m_formatter.parse(specification); + output << m_formatter % (int)immediate % (int)absolute % relative % (int)displacement % indexedImmediate; +} + +void Disassembler::disassembleCB( + std::ostringstream& output, + LR35902& cpu, + uint16_t pc, + std::string& specification, + int& dumpCount, + int x, int y, int z, + int p, int q) { + + switch (x) { + case 0: // rot[y] r[z] + switch (y) { + case 0: + specification = "RLC " + R(z); + break; + case 1: + specification = "RRC " + R(z); + break; + case 2: + specification = "RL " + R(z); + break; + case 3: + specification = "RR " + R(z); + break; + case 4: + specification = "SLA " + R(z); + break; + case 5: + specification = "SRA " + R(z); + break; + case 6: + specification = "SWAP " + R(z); + break; + case 7: + specification = "SRL " + R(z); + break; + } + break; + case 1: // BIT y, r[z] + specification = "BIT " + decimal(y) + "," + R(z); + break; + case 2: // RES y, r[z] + specification = "RES " + decimal(y) + "," + R(z); + break; + case 3: // SET y, r[z] + specification = "SET " + decimal(y) + "," + R(z); + break; + } +} + +void Disassembler::disassembleOther( + std::ostringstream& output, + LR35902& cpu, + uint16_t pc, + std::string& specification, + int& dumpCount, + int x, int y, int z, + int p, int q) { + + switch (x) { + case 0: + switch (z) { + case 0: // Relative jumps and assorted ops + switch (y) { + case 0: // NOP + specification = "NOP"; + break; + case 1: // GB: LD (nn),SP + specification = "LD (%2$04XH),SP"; + dumpCount += 2; + break; + case 2: // GB: STOP + specification = "STOP"; + break; + case 3: // JR d + specification = "JR %3$04XH"; + dumpCount++; + break; + default: // JR cc,d + specification = "JR " + cc(y - 4) + ",%3$04XH"; + dumpCount++; + break; + } + break; + case 1: // 16-bit load immediate/add + switch (q) { + case 0: // LD rp,nn + specification = "LD " + RP(p) + ",%2$04XH"; + dumpCount += 2; + break; + case 1: // ADD HL,rp + specification = "ADD HL," + RP(p); + break; + } + break; + case 2: // Indirect loading + switch (q) { + case 0: + switch (p) { + case 0: // LD (BC),A + specification = "LD (BC),A"; + break; + case 1: // LD (DE),A + specification = "LD (DE),A"; + break; + case 2: // GB: LDI (HL),A + specification = "LDI (HL),A"; + break; + case 3: // GB: LDD (HL),A + specification = "LDD (HL),A"; + break; + } + break; + case 1: + switch (p) { + case 0: // LD A,(BC) + specification = "LD A,(BC)"; + break; + case 1: // LD A,(DE) + specification = "LD A,(DE)"; + break; + case 2: // GB: LDI A,(HL) + specification = "LDI A,(HL)"; + break; + case 3: // GB: LDD A,(HL) + specification = "LDD A,(HL)"; + break; + } + break; + } + break; + case 3: // 16-bit INC/DEC + switch (q) { + case 0: // INC rp + specification = "INC " + RP(p); + break; + case 1: // DEC rp + specification = "DEC " + RP(p); + break; + } + break; + case 4: // 8-bit INC + specification = "INC " + R(y); + break; + case 5: // 8-bit DEC + specification = "DEC " + R(y); + break; + case 6: // 8-bit load immediate + specification = "LD " + R(y) + ",%1$02XH"; + dumpCount++; + break; + case 7: // Assorted operations on accumulator/flags + switch (y) { + case 0: + specification = "RLCA"; + break; + case 1: + specification = "RRCA"; + break; + case 2: + specification = "RLA"; + break; + case 3: + specification = "RRA"; + break; + case 4: + specification = "DAA"; + break; + case 5: + specification = "CPL"; + break; + case 6: + specification = "SCF"; + break; + case 7: + specification = "CCF"; + break; + } + break; + } + break; + case 1: // 8-bit loading + if (z == 6 && y == 6) { // Exception (replaces LD (HL), (HL)) + specification = "HALT"; + } else { + specification = "LD " + R(y) + "," + R(z); + } + break; + case 2: // Operate on accumulator and register/memory location + specification = alu(y) + " A," + R(z); + break; + case 3: + switch (z) { + case 0: // Conditional return + switch (y) { + case 0: + case 1: + case 2: + case 3: + specification = "RET " + cc(y); + break; + case 4: + specification = "LD (FF00H+%1$02XH),A"; + dumpCount++; + break; + case 5: + specification = "ADD SP,%3$04XH"; + dumpCount++; + break; + case 6: + specification = "LD A,(FF00H+%1$02XH)"; + dumpCount++; + break; + case 7: + specification = "LD HL,SP+%3$04XH"; + dumpCount++; + break; + } + break; + case 1: // POP & various ops + switch (q) { + case 0: // POP rp2[p] + specification = "POP " + RP2(p); + break; + case 1: + switch (p) { + case 0: // RET + specification = "RET"; + break; + case 1: // GB: RETI + specification = "RETI"; + break; + case 2: // JP (HL) + specification = "JP (HL)"; + break; + case 3: // LD SP,HL + specification = "LD SP,Hl"; + break; + } + } + break; + case 2: // Conditional jump + switch (y) { + case 0: + case 1: + case 2: + case 3: + specification = "JP " + cc(y) + ",%2$04XH"; + dumpCount += 2; + break; + case 4: + specification = "LD (FF00H+C),A"; + break; + case 5: + specification = "LD (%2$04XH),A"; + dumpCount += 2; + break; + case 6: + specification = "LD A,(FF00H+C)"; + break; + case 7: + specification = "LD A,(%2$04XH)"; + dumpCount += 2; + break; + } + break; + case 3: // Assorted operations + switch (y) { + case 0: // JP nn + specification = "JP %2$04XH"; + dumpCount += 2; + break; + case 1: // CB prefix + m_prefixCB = true; + disassemble(output, cpu, pc + 1); + break; + case 6: // DI + specification = "DI"; + break; + case 7: // EI + specification = "EI"; + break; + } + break; + case 4: // Conditional call: CALL cc[y], nn + specification = "CALL " + cc(y) + ",%2$04XH"; + dumpCount += 2; + break; + case 5: // PUSH & various ops + switch (q) { + case 0: // PUSH rp2[p] + specification = "PUSH " + RP2(p); + break; + case 1: + switch (p) { + case 0: // CALL nn + specification = "CALL %2$04XH"; + dumpCount += 2; + break; + } + } + break; + case 6: // Operate on accumulator and immediate operand: alu[y] n + specification = alu(y) + " A,%1$02XH"; + dumpCount++; + break; + case 7: // Restart: RST y * 8 + specification = "RST " + hex((uint8_t)(y * 8)) + "H"; + break; + } + break; + } +} + +std::string Disassembler::flag(uint8_t value, int flag, const std::string& represents) { + std::ostringstream output; + output << (value & flag ? represents : "-"); + return output.str(); +} + +std::string Disassembler::flags(uint8_t value) { + std::ostringstream output; + output + << flag(value, LR35902::ZF, "Z") + << flag(value, LR35902::NF, "N") + << flag(value, LR35902::HC, "H") + << flag(value, LR35902::CF, "C") + << flag(value, Processor::Bit3, "+") + << flag(value, Processor::Bit2, "+") + << flag(value, Processor::Bit1, "+") + << flag(value, Processor::Bit0, "+"); + return output.str(); +} + +std::string Disassembler::hex(uint8_t value) { + std::ostringstream output; + output << std::hex << std::setw(2) << std::setfill('0') << (int)value; + return output.str(); +} + +std::string Disassembler::hex(uint16_t value) { + std::ostringstream output; + output << std::hex << std::setw(4) << std::setfill('0') << (int)value; + return output.str(); +} + +std::string Disassembler::binary(uint8_t value) { + std::ostringstream output; + output << std::bitset<8>(value); + return output.str(); +} + +std::string Disassembler::decimal(uint8_t value) { + std::ostringstream output; + output << (int)value; + return output.str(); +} + +std::string Disassembler::invalid(uint8_t value) { + std::ostringstream output; + output << "Invalid instruction: " << hex(value) << "(" << binary(value) << ")"; + return output.str(); +} \ No newline at end of file diff --git a/LR35902/src/LR35902.cpp b/LR35902/src/LR35902.cpp new file mode 100644 index 0000000..b02bc0a --- /dev/null +++ b/LR35902/src/LR35902.cpp @@ -0,0 +1,912 @@ +#include "stdafx.h" +#include "LR35902.h" + +// based on http://www.z80.info/decoding.htm +// Half carry flag help from https://github.com/oubiwann/z80 + +LR35902::LR35902(Bus& memory) +: Processor(memory), + m_ime(false), + m_prefixCB(false) { +} + +void LR35902::reset() { + Processor::reset(); + setStackPointer(0xfffe); + di(); +} + +void LR35902::initialise() { + + Processor::initialise(); + + AF().word = 0xffff; + BC().word = 0xffff; + DE().word = 0xffff; + HL().word = 0xffff; + + m_prefixCB = false; +} + +void LR35902::di() { + IME() = false; +} + +void LR35902::ei() { + IME() = true; +} + +int LR35902::interrupt(uint8_t value) { + di(); + restart(value); + return 4; +} + +void LR35902::adjustZero(uint8_t value) { + clearFlag(ZF, value); +} + +void LR35902::postIncrement(uint8_t value) { + adjustZero(value); + clearFlag(NF); + clearFlag(HC, lowNibble(value)); +} + +void LR35902::postDecrement(uint8_t value) { + adjustZero(value); + setFlag(NF); + clearFlag(HC, lowNibble(value + 1)); +} + +void LR35902::restart(uint8_t address) { + pushWord(pc); + pc = address; +} + +void LR35902::jrConditional(int conditional) { + auto offset = (int8_t)fetchByte(); + if (conditional) { + pc += offset; + cycles++; + } +} + +void LR35902::jrConditionalFlag(int flag) { + switch (flag) { + case 0: // NZ + jrConditional(!(F() & ZF)); + break; + case 1: // Z + jrConditional(F() & ZF); + break; + case 2: // NC + jrConditional(!(F() & CF)); + break; + case 3: // C + jrConditional(F() & CF); + break; + case 4: // PO + case 5: // PE + case 6: // P + case 7: // M + cycles -= 2; + break; + } +} + +void LR35902::jumpConditional(int conditional) { + auto address = fetchWord(); + if (conditional) { + pc = address; + cycles++; + } +} + +void LR35902::jumpConditionalFlag(int flag) { + switch (flag) { + case 0: // NZ + jumpConditional(!(F() & ZF)); + break; + case 1: // Z + jumpConditional(F() & ZF); + break; + case 2: // NC + jumpConditional(!(F() & CF)); + break; + case 3: // C + jumpConditional(F() & CF); + break; + case 4: // GB: LD (FF00 + C),A + m_memory.set(0xff00 + C(), A()); + cycles--; // Giving 8 cycles + break; + case 5: // GB: LD (nn),A + m_memory.set(fetchWord(), A()); + cycles++; // Giving 16 cycles + break; + case 6: // GB: LD A,(FF00 + C) + A() = m_memory.get(0xff00 + C()); + cycles--; // 8 cycles + break; + case 7: // GB: LD A,(nn) + A() = m_memory.get(fetchWord()); + cycles++; // Giving 16 cycles + break; + } +} + +void LR35902::ret() { + pc = popWord(); +} + +void LR35902::reti() { + ret(); + ei(); +} + +void LR35902::returnConditional(int condition) { + if (condition) { + ret(); + cycles += 3; + } +} + +void LR35902::returnConditionalFlag(int flag) { + switch (flag) { + case 0: // NZ + returnConditional(!(F() & ZF)); + break; + case 1: // Z + returnConditional(F() & ZF); + break; + case 2: // NC + returnConditional(!(F() & CF)); + break; + case 3: // C + returnConditional(F() & CF); + break; + case 4: // GB: LD (FF00 + n),A + m_memory.set(0xff00 + fetchByte(), A()); + cycles++; // giving 12 cycles in total + break; + case 5: { // GB: ADD SP,dd + auto before = sp; + auto value = fetchByte(); + sp += (int8_t)value; + clearFlag(ZF | NF); + setFlag(CF, sp & Bit16); + adjustHalfCarryAdd(Memory::highByte(before), value, Memory::highByte(sp)); + } + cycles += 2; // 16 cycles + break; + case 6: // GB: LD A,(FF00 + n) + A() = m_memory.get(0xff00 + fetchByte()); + cycles++; // 12 cycles + break; + case 7: { // GB: LD HL,SP + dd + auto before = sp; + auto value = fetchByte(); + uint16_t result = before + (int8_t)value; + HL().word = result; + clearFlag(ZF | NF); + setFlag(CF, result & Bit16); + adjustHalfCarryAdd(Memory::highByte(before), value, Memory::highByte(result)); + } + cycles++; // 12 cycles + break; + } +} + +void LR35902::call(uint16_t address) { + pushWord(pc + 2); + pc = address; +} + +void LR35902::callConditional(uint16_t address, int condition) { + if (condition) { + call(address); + cycles += 3; + } else { + pc += 2; + } +} + +void LR35902::callConditionalFlag(uint16_t address, int flag) { + switch (flag) { + case 0: // NZ + callConditional(address, !(F() & ZF)); + break; + case 1: // Z + callConditional(address, F() & ZF); + break; + case 2: // NC + callConditional(address, !(F() & CF)); + break; + case 3: // C + callConditional(address, F() & CF); + break; + case 4: + case 5: + case 6: + case 7: + cycles -= 3; // removed from GB + break; + } +} + +uint16_t LR35902::sbc(uint16_t value) { + + auto hl = RP(HL_IDX); + + auto high = Memory::highByte(hl); + auto highValue = Memory::highByte(value); + auto applyCarry = F() & CF; + + uint32_t result = (int)hl - (int)value; + if (applyCarry) + --result; + auto highResult = Memory::highByte(result); + + adjustZero(result); + adjustHalfCarrySub(high, highValue, highResult); + + setFlag(NF); + setFlag(CF, result & Bit16); + + return result; +} + +uint16_t LR35902::adc(uint16_t value) { + + auto hl = RP(HL_IDX); + + auto high = Memory::highByte(hl); + auto highValue = Memory::highByte(value); + auto applyCarry = F() & CF; + + uint32_t result = (int)hl + (int)value; + if (applyCarry) + ++result; + auto highResult = Memory::highByte(result); + + adjustZero(result); + adjustHalfCarryAdd(high, highValue, highResult); + + clearFlag(NF); + setFlag(CF, result & Bit16); + + return result; +} + +uint16_t LR35902::add(uint16_t value) { + + auto hl = RP(HL_IDX); + + auto high = Memory::highByte(hl); + auto highValue = Memory::highByte(value); + + uint32_t result = (int)hl + (int)value; + + auto highResult = Memory::highByte(result); + + clearFlag(NF); + setFlag(CF, result & Bit16); + adjustHalfCarryAdd(high, highValue, highResult); + + return result; +} + +void LR35902::sub(uint8_t& operand, uint8_t value, bool carry) { + + auto before = operand; + + uint16_t result = before - value; + if (carry && (F() & CF)) + --result; + + operand = Memory::lowByte(result); + + adjustZero(operand); + adjustHalfCarrySub(before, value, result); + setFlag(NF); + setFlag(CF, result & Bit8); +} + +void LR35902::sbc(uint8_t& operand, uint8_t value) { + sub(operand, value, true); +} + +void LR35902::sub(uint8_t& operand, uint8_t value) { + sub(operand, value, false); +} + +void LR35902::add(uint8_t& operand, uint8_t value, bool carry) { + + auto before = operand; + + uint16_t result = before + value; + if (carry && (F() & CF)) + ++result; + + operand = Memory::lowByte(result); + + adjustZero(operand); + adjustHalfCarryAdd(before, value, result); + clearFlag(NF); + setFlag(CF, result & Bit8); +} + +void LR35902::adc(uint8_t& operand, uint8_t value) { + add(operand, value, true); +} + +void LR35902::add(uint8_t& operand, uint8_t value) { + add(operand, value, false); +} + +// + +void LR35902::andr(uint8_t& operand, uint8_t value) { + setFlag(HC); + clearFlag(CF | NF); + operand &= value; + adjustZero(operand); +} + +void LR35902::anda(uint8_t value) { + andr(A(), value); +} + +void LR35902::xora(uint8_t value) { + clearFlag(HC | CF | NF); + A() ^= value; + adjustZero(A()); +} + +void LR35902::ora(uint8_t value) { + clearFlag(HC | CF | NF); + A() |= value; + adjustZero(A()); +} + +void LR35902::compare(uint8_t value) { + auto check = A(); + sub(check, value); +} + +// + +void LR35902::rlc(uint8_t& operand) { + auto carry = operand & Bit7; + operand <<= 1; + setFlag(CF, carry); + carry ? operand |= Bit0 : operand &= ~Bit0; + clearFlag(NF | HC); + adjustZero(operand); +} + +void LR35902::rrc(uint8_t& operand) { + auto carry = operand & Bit0; + operand >>= 1; + carry ? operand |= Bit7 : operand &= ~Bit7; + setFlag(CF, carry); + clearFlag(NF | HC); + adjustZero(operand); +} + +void LR35902::rl(uint8_t& operand) { + auto oldCarry = F() & CF; + auto newCarry = operand & Bit7; + operand <<= 1; + oldCarry ? operand |= Bit0 : operand &= ~Bit0; + setFlag(CF, newCarry); + clearFlag(NF | HC); + adjustZero(operand); +} + +void LR35902::rr(uint8_t& operand) { + auto oldCarry = F() & CF; + auto newCarry = operand & Bit0; + operand >>= 1; + operand |= oldCarry << 7; + setFlag(CF, newCarry); + clearFlag(NF | HC); + adjustZero(operand); +} + +// + +void LR35902::sla(uint8_t& operand) { + auto newCarry = operand & Bit7; + operand <<= 1; + setFlag(CF, newCarry); + clearFlag(NF | HC); + adjustZero(operand); +} + +void LR35902::sra(uint8_t& operand) { + auto new7 = operand & Bit7; + auto newCarry = operand & Bit0; + operand >>= 1; + operand |= new7; + setFlag(CF, newCarry); + clearFlag(NF | HC); + adjustZero(operand); +} + +void LR35902::srl(uint8_t& operand) { + auto newCarry = operand & Bit0; + operand >>= 1; + operand &= ~Bit7; // clear bit 7 + setFlag(CF, newCarry); + clearFlag(NF | HC); + adjustZero(operand); +} + +// + +void LR35902::rlca() { + rlc(A()); +} + +void LR35902::rrca() { + rrc(A()); +} + +void LR35902::rla() { + rl(A()); +} + +void LR35902::rra() { + rr(A()); +} + +// + +void LR35902::bit(int n, uint8_t& operand) { + auto carry = F() & CF; + uint8_t discarded = operand; + andr(discarded, 1 << n); + setFlag(CF, carry); +} + +void LR35902::res(int n, uint8_t& operand) { + auto bit = 1 << n; + operand &= ~bit; +} + +void LR35902::set(int n, uint8_t& operand) { + auto bit = 1 << n; + operand |= bit; +} + +// + +void LR35902::daa() { + + uint8_t a = A(); + + auto lowAdjust = (F() & HC) | ((A() & 0xf) > 9); + auto highAdjust = (F() & CF) | (A() > 0x99); + + if (F() & NF) { + if (lowAdjust) + a -= 6; + if (highAdjust) + a -= 0x60; + } else { + if (lowAdjust) + a += 6; + if (highAdjust) + a += 0x60; + } + + F() = (F() & (CF | NF)) | (A() > 0x99) | ((A() ^ a) & HC); + + adjustZero(a); + + A() = a; +} + +void LR35902::cpl() { + A() = ~A(); + setFlag(HC | NF); +} + +void LR35902::scf() { + setFlag(CF); + clearFlag(HC | NF); +} + +void LR35902::ccf() { + auto carry = F() & CF; + clearFlag(CF, carry); + clearFlag(NF | HC); +} + +void LR35902::swap(uint8_t& operand) { + auto low = lowNibble(operand); + auto high = highNibble(operand); + operand = promoteNibble(low) | demoteNibble(high); + adjustZero(operand); + clearFlag(NF | HC | CF); +} + +int LR35902::step() { + ExecutingInstruction.fire(*this); + m_prefixCB = false; + return fetchExecute(); +} + +int LR35902::execute(uint8_t opcode) { + + auto x = (opcode & 0b11000000) >> 6; + auto y = (opcode & 0b111000) >> 3; + auto z = (opcode & 0b111); + + auto p = (y & 0b110) >> 1; + auto q = (y & 1); + + cycles = 0; + + if (m_prefixCB) + executeCB(x, y, z, p, q); + else + executeOther(x, y, z, p, q); + + if (cycles == 0) + throw std::logic_error("Unhandled opcode"); + + return cycles * 4; +} + +void LR35902::executeCB(int x, int y, int z, int p, int q) { + switch (x) { + case 0: // rot[y] r[z] + switch (y) { + case 0: + rlc(R(z)); + break; + case 1: + rrc(R(z)); + break; + case 2: + rl(R(z)); + break; + case 3: + rr(R(z)); + break; + case 4: + sla(R(z)); + break; + case 5: + sra(R(z)); + break; + case 6: + swap(R(z)); + break; + case 7: + srl(R(z)); + break; + } + adjustZero(R(z)); + cycles += 2; + if (z == 6) + cycles += 2; + break; + case 1: // BIT y, r[z] + bit(y, R(z)); + cycles += 2; + if (z == 6) + cycles += 2; + break; + case 2: // RES y, r[z] + res(y, R(z)); + cycles += 2; + if (z == 6) + cycles += 2; + break; + case 3: // SET y, r[z] + set(y, R(z)); + cycles += 2; + if (z == 6) + cycles += 2; + break; + } +} + +void LR35902::executeOther(int x, int y, int z, int p, int q) { + switch (x) { + case 0: + switch (z) { + case 0: // Relative jumps and assorted ops + switch (y) { + case 0: // NOP + cycles++; + break; + case 1: // GB: LD (nn),SP + m_memory.setWord(fetchWord(), sp); + cycles += 5; + break; + case 2: // GB: STOP + stop(); + cycles++; + break; + case 3: // JR d + jrConditional(true); + cycles += 3; + break; + default: // JR cc,d + jrConditionalFlag(y - 4); + cycles += 2; + break; + } + break; + case 1: // 16-bit load immediate/add + switch (q) { + case 0: // LD rp,nn + RP(p) = fetchWord(); + cycles += 3; + break; + case 1: // ADD HL,rp + RP(HL_IDX) = add(RP(p)); + cycles += 2; + break; + } + break; + case 2: // Indirect loading + switch (q) { + case 0: + switch (p) { + case 0: // LD (BC),A + m_memory.set(BC().word, A()); + cycles += 2; + break; + case 1: // LD (DE),A + m_memory.set(DE().word, A()); + cycles += 2; + break; + case 2: // GB: LDI (HL),A + m_memory.set(HL().word++, A()); + cycles += 2; + break; + case 3: // GB: LDD (HL),A + m_memory.set(HL().word--, A()); + cycles += 2; + break; + } + break; + case 1: + switch (p) { + case 0: // LD A,(BC) + A() = m_memory.get(BC().word); + cycles += 2; + break; + case 1: // LD A,(DE) + A() = m_memory.get(DE().word); + cycles += 2; + break; + case 2: // GB: LDI A,(HL) + A() = m_memory.get(HL().word++); + cycles += 2; + break; + case 3: // GB: LDD A,(HL) + A() = m_memory.get(HL().word--); + cycles += 2; + break; + } + break; + } + break; + case 3: // 16-bit INC/DEC + switch (q) { + case 0: // INC rp + ++RP(p); + break; + case 1: // DEC rp + --RP(p); + break; + } + cycles += 2; + break; + case 4: // 8-bit INC + postIncrement(++R(y)); // INC r + cycles++; + if (y == 6) + cycles += 2; + break; + case 5: // 8-bit DEC + postDecrement(--R(y)); // DEC r + cycles++; + if (y == 6) + cycles += 2; + break; + case 6: // 8-bit load immediate + R(y) = fetchByte(); + cycles += 2; + break; + case 7: // Assorted operations on accumulator/flags + switch (y) { + case 0: + rlca(); + break; + case 1: + rrca(); + break; + case 2: + rla(); + break; + case 3: + rra(); + break; + case 4: + daa(); + break; + case 5: + cpl(); + break; + case 6: + scf(); + break; + case 7: + ccf(); + break; + } + cycles++; + break; + } + break; + case 1: // 8-bit loading + if (z == 6 && y == 6) { // Exception (replaces LD (HL), (HL)) + halt(); + } else { + R(y) = R(z); + if ((y == 6) || (z == 6)) // M operations + cycles++; + } + cycles++; + break; + case 2: // Operate on accumulator and register/memory location + switch (y) { + case 0: // ADD A,r + add(A(), R(z)); + break; + case 1: // ADC A,r + adc(A(), R(z)); + break; + case 2: // SUB r + sub(A(), R(z)); + break; + case 3: // SBC A,r + sbc(A(), R(z)); + break; + case 4: // AND r + anda(R(z)); + break; + case 5: // XOR r + xora(R(z)); + break; + case 6: // OR r + ora(R(z)); + break; + case 7: // CP r + compare(R(z)); + break; + } + cycles++; + if (z == 6) + cycles++; + break; + case 3: + switch (z) { + case 0: // Conditional return + returnConditionalFlag(y); + cycles += 2; + break; + case 1: // POP & various ops + switch (q) { + case 0: // POP rp2[p] + RP2(p) = popWord(); + cycles += 3; + break; + case 1: + switch (p) { + case 0: // RET + ret(); + cycles += 4; + break; + case 1: // GB: RETI + reti(); + cycles += 4; + break; + case 2: // JP HL + pc = HL().word; + cycles += 1; + break; + case 3: // LD SP,HL + sp = HL().word; + cycles += 2; + break; + } + } + break; + case 2: // Conditional jump + jumpConditionalFlag(y); + cycles += 3; + break; + case 3: // Assorted operations + switch (y) { + case 0: // JP nn + pc = fetchWord(); + cycles += 4; + break; + case 1: // CB prefix + m_prefixCB = true; + fetchExecute(); + break; + case 6: // DI + di(); + cycles++; + break; + case 7: // EI + ei(); + cycles++; + break; + } + break; + case 4: // Conditional call: CALL cc[y], nn + callConditionalFlag(getWord(pc), y); + cycles += 3; + break; + case 5: // PUSH & various ops + switch (q) { + case 0: // PUSH rp2[p] + pushWord(RP2(p)); + cycles += 4; + break; + case 1: + switch (p) { + case 0: // CALL nn + callConditional(getWord(pc), true); + cycles += 3; + break; + } + } + break; + case 6: // Operate on accumulator and immediate operand: alu[y] n + switch (y) { + case 0: // ADD A,n + add(A(), fetchByte()); + break; + case 1: // ADC A,n + adc(A(), fetchByte()); + break; + case 2: // SUB n + sub(A(), fetchByte()); + break; + case 3: // SBC A,n + sbc(A(), fetchByte()); + break; + case 4: // AND n + anda(fetchByte()); + break; + case 5: // XOR n + xora(fetchByte()); + break; + case 6: // OR n + ora(fetchByte()); + break; + case 7: // CP n + compare(fetchByte()); + break; + } + cycles += 2; + break; + case 7: // Restart: RST y * 8 + restart(y << 3); + cycles += 4; + break; + } + break; + } +} \ No newline at end of file diff --git a/LR35902/src/Profiler.cpp b/LR35902/src/Profiler.cpp new file mode 100644 index 0000000..10c7cd8 --- /dev/null +++ b/LR35902/src/Profiler.cpp @@ -0,0 +1,43 @@ +#include "stdafx.h" +#include "Profiler.h" +#include "LR35902.h" + +Profiler::Profiler(LR35902& cpu) +: m_cpu(cpu) { + std::fill(m_instructions.begin(), m_instructions.end(), 0); + std::fill(m_addresses.begin(), m_addresses.end(), 0); +} + +void Profiler::add(uint16_t address, uint8_t instruction) { + + m_instructions[instruction]++; + + auto old = m_addresses[address]; + if (old == 0) + std::cout << Disassembler::hex(address) << "\t" << m_disassembler.disassemble(m_cpu) << "\n"; + + m_addresses[address]++; +} + +void Profiler::dump() const { + dumpInstructionProfiles(); + dumpAddressProfiles(); +} + +void Profiler::dumpInstructionProfiles() const { + std::cout << "** instructions" << std::endl; + for (int i = 0; i < 0x100; ++i) { + auto count = m_instructions[i]; + if (count > 0) + std::cout << Disassembler::hex((uint8_t)i) << "\t" << count << std::endl; + } +} + +void Profiler::dumpAddressProfiles() const { + std::cout << "** addresses" << std::endl; + for (int i = 0; i < 0x10000; ++i) { + auto count = m_addresses[i]; + if (count > 0) + std::cout << Disassembler::hex((uint16_t)i) << "\t" << count << std::endl; + } +} diff --git a/M6502/inc/AddressEventArgs.h b/M6502/inc/AddressEventArgs.h new file mode 100644 index 0000000..b13d738 --- /dev/null +++ b/M6502/inc/AddressEventArgs.h @@ -0,0 +1,17 @@ +#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 new file mode 100644 index 0000000..1f4db11 --- /dev/null +++ b/M6502/inc/AddressingMode.h @@ -0,0 +1,21 @@ +#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 new file mode 100644 index 0000000..57da6c7 --- /dev/null +++ b/M6502/inc/AddressingModeDumper.h @@ -0,0 +1,11 @@ +#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 new file mode 100644 index 0000000..7239730 --- /dev/null +++ b/M6502/inc/Disassembly.h @@ -0,0 +1,64 @@ +#pragma once + +#include +#include + +#include +#include + +#include "Symbols.h" +#include "AddressingModeDumper.h" + +class Disassembly +{ +public: + MOS6502& processor; + const Symbols& symbols; + + std::map dumpers; + + Disassembly(MOS6502& processor, const Symbols& symbols); + + std::string Dump_ByteValue(uint8_t value) const; + std::string Dump_WordValue(uint16_t value) const; + + 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; + +private: + uint8_t GetByte(uint16_t address) const; + uint16_t GetWord(uint16_t address) const; + + 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 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 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; + + 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 diff --git a/M6502/inc/DisassemblyEventArgs.h b/M6502/inc/DisassemblyEventArgs.h new file mode 100644 index 0000000..a99642e --- /dev/null +++ b/M6502/inc/DisassemblyEventArgs.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +class DisassemblyEventArgs +{ +private: + std::string m_output; + +public: + DisassemblyEventArgs(std::string output) + : m_output(output) {} + + 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 new file mode 100644 index 0000000..5166b3e --- /dev/null +++ b/M6502/inc/Memory.h @@ -0,0 +1,76 @@ +#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 new file mode 100644 index 0000000..3471cd6 --- /dev/null +++ b/M6502/inc/ProcessorType.h @@ -0,0 +1,7 @@ +#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 new file mode 100644 index 0000000..cb9b8b8 --- /dev/null +++ b/M6502/inc/ProfileEventArgs.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +class ProfileEventArgs +{ +private: + std::string m_output; + +public: + ProfileEventArgs(std::string output) + : m_output(output) {} + + 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 new file mode 100644 index 0000000..31e1682 --- /dev/null +++ b/M6502/inc/ProfileLineEventArgs.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +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) {} + + 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 new file mode 100644 index 0000000..2f787f1 --- /dev/null +++ b/M6502/inc/ProfileScopeEventArgs.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +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; } +}; + diff --git a/M6502/inc/Profiler.h b/M6502/inc/Profiler.h new file mode 100644 index 0000000..c59cf26 --- /dev/null +++ b/M6502/inc/Profiler.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +#include "Disassembly.h" +#include "Symbols.h" + +#include "ProfileLineEventArgs.h" +#include "ProfileScopeEventArgs.h" + +class Profiler +{ +public: + std::array instructionCounts; + std::array addressProfiles; + std::array addressCounts; + + std::array addressScopes; + std::map scopeCycles; + + System6502& processor; + const Disassembly& disassembler; + const Symbols& symbols; + + bool countInstructions; + bool profileAddresses; + + uint64_t priorCycleCount = 0; + + Profiler(System6502& processor, Disassembly& disassembler, Symbols& symbols, bool countInstructions, bool profileAddresses); + + Signal StartingOutput; + Signal FinishedOutput; + + Signal StartingLineOutput; + Signal FinishedLineOutput; + + Signal EmitLine; + + Signal StartingScopeOutput; + Signal FinishedScopeOutput; + + Signal EmitScope; + + void Generate(); + +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(); +}; diff --git a/M6502/inc/StatusFlags.h b/M6502/inc/StatusFlags.h new file mode 100644 index 0000000..7526e4d --- /dev/null +++ b/M6502/inc/StatusFlags.h @@ -0,0 +1,82 @@ +#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 new file mode 100644 index 0000000..8ab5083 --- /dev/null +++ b/M6502/inc/Symbols.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include +#include + +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; } + +private: + void AssignScopes(); + void AssignSymbols(); + + void Parse(std::string path); + + std::map labels; + std::map constants; + std::map scopes; + std::map addresses; + + struct kv_pair_t { + std::map element; + }; + + std::vector split(const std::string& input, const std::vector& delimiters); + + std::map> parsed; +}; diff --git a/M6502/inc/mos6502.h b/M6502/inc/mos6502.h new file mode 100644 index 0000000..62a0a94 --- /dev/null +++ b/M6502/inc/mos6502.h @@ -0,0 +1,464 @@ +#pragma once + +#include +#include +#include +#include + +#include "ProcessorType.h" +#include "StatusFlags.h" +#include "AddressingMode.h" + +class MOS6502 { +public: + typedef std::function instruction_t; + + struct Instruction { + instruction_t vector = nullptr; + uint64_t count = 0; + AddressingMode mode = AddressingMode::Illegal; + std::string display = ""; + }; + + 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 diff --git a/M6502/inc/system6502.h b/M6502/inc/system6502.h new file mode 100644 index 0000000..1d47e1e --- /dev/null +++ b/M6502/inc/system6502.h @@ -0,0 +1,69 @@ +#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 new file mode 100644 index 0000000..d11aa76 --- /dev/null +++ b/M6502/src/AddressingMode.cpp @@ -0,0 +1,23 @@ +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 new file mode 100644 index 0000000..7f25442 --- /dev/null +++ b/M6502/src/Disassembly.cpp @@ -0,0 +1,232 @@ +#include "stdafx.h" +#include "Disassembly.h" + +#include +#include +#include + +using namespace std::placeholders; + +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) } }, + }; +} + +std::string 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::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 Disassembly::Disassemble(uint16_t current) const { + + std::ostringstream output; + + auto content = processor.GetByte(current); + 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 Disassembly::GetByte(uint16_t address) const { + return processor.GetByte(address); +} + +uint16_t Disassembly::GetWord(uint16_t address) const { + return processor.GetWord(address); +} + +//// + +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 { + auto label = symbols.getLabels().find(address); + if (label != symbols.getLabels().end()) + return label->second; + std::ostringstream output; + output << "$" << Dump_WordValue(address); + return output.str(); +} + +std::string 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); + return output.str(); +} + +std::string Disassembly::ConvertConstant(uint16_t constant) const { + auto label = symbols.getConstants().find(constant); + if (label != symbols.getConstants().end()) + return label->second; + return Dump_DByte(constant); +} + +std::string 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(); +} + \ No newline at end of file diff --git a/M6502/src/ProcessorType.cpp b/M6502/src/ProcessorType.cpp new file mode 100644 index 0000000..aa0fe7b --- /dev/null +++ b/M6502/src/ProcessorType.cpp @@ -0,0 +1,9 @@ +namespace Processor +{ + public enum ProcessorType + { + Cpu6502, + Cpu65SC02, + Cpu65C02 + } +} diff --git a/M6502/src/Profiler.cpp b/M6502/src/Profiler.cpp new file mode 100644 index 0000000..b5b53ca --- /dev/null +++ b/M6502/src/Profiler.cpp @@ -0,0 +1,95 @@ +#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) +{ + 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() { + StartingOutput.fire(EventArgs()); + EmitProfileInformation(); + StartingOutput.fire(EventArgs()); +} + +void Profiler::EmitProfileInformation() { + + { + StartingLineOutput.fire(EventArgs()); + // For each memory address + for (uint16_t 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); + EmitLine.fire(ProfileLineEventArgs(source, cycles)); + } + } + FinishedLineOutput.fire(EventArgs()); + } + + { + StartingScopeOutput.fire(EventArgs()); + for (auto& scopeCycle : scopeCycles) { + auto name = scopeCycle.first; + auto cycles = scopeCycle.second; + auto namedAddress = (size_t)symbols.getAddresses().find(name)->second; + auto count = addressCounts[namedAddress]; + EmitScope.fire(ProfileScopeEventArgs(name, cycles, count)); + } + FinishedScopeOutput.fire(EventArgs()); + } +} + +void Profiler::Processor_ExecutingInstruction_ProfileAddresses(const AddressEventArgs& addressEvent) { + assert(profileAddresses); + priorCycleCount = processor.getCycles(); + addressCounts[addressEvent.getAddress()]++; +} + +void Profiler::Processor_ExecutingInstruction_CountInstructions(const AddressEventArgs& addressEvent) { + assert(countInstructions); + ++instructionCounts[addressEvent.getCell()]; +} + +void Profiler::Processor_ExecutedInstruction_ProfileAddresses(const AddressEventArgs& addressEvent) { + assert(profileAddresses); + auto cycles = processor.getCycles() - priorCycleCount; + addressProfiles[addressEvent.getAddress()] += cycles; + auto addressScope = addressScopes[addressEvent.getAddress()]; + if (!addressScope.empty()) { + if (scopeCycles.find(addressScope) == scopeCycles.end()) + scopeCycles[addressScope] = 0; + scopeCycles[addressScope] += cycles; + } +} + +void Profiler::BuildAddressScopes() { + for (auto& label : symbols.getLabels()) { + auto address = label.first; + auto key = label.second; + auto scope = symbols.getScopes().find(key); + if (scope != symbols.getScopes().end()) { + for (uint16_t i = address; i < address + scope->second; ++i) { + addressScopes[i] = key; + } + } + } +} \ No newline at end of file diff --git a/M6502/src/Symbols.cpp b/M6502/src/Symbols.cpp new file mode 100644 index 0000000..47b6283 --- /dev/null +++ b/M6502/src/Symbols.cpp @@ -0,0 +1,84 @@ +#include "stdafx.h" +#include "Symbols.h" + +#include + +#include + +#include +#include +#include +#include + +Symbols::Symbols(std::string path) { + if (!path.empty()) { + Parse(path); + AssignSymbols(); + AssignScopes(); + } +} + +void Symbols::AssignScopes() { + auto parsedScopes = parsed["scope"]; + for(auto& parsedScopeElement : parsedScopes) { + auto& parsedScope = parsedScopeElement.second.element; + auto name = parsedScope["name"]; + auto trimmedName = name.substr(1, name.length() - 2); + auto size = parsedScope["size"]; + scopes[trimmedName] = (uint16_t)std::stoi(size); + } +} + +void Symbols::AssignSymbols() { + auto symbols = parsed["sym"]; + for(auto& symbolElement : symbols) { + auto& symbol = symbolElement.second.element; + auto name = symbol["name"]; + auto trimmedName = name.substr(1, name.length() - 2); + auto value = symbol["val"].substr(2); + auto number = (uint16_t)std::stoi(value, nullptr, 16); + auto symbolType = symbol["type"]; + if (symbolType == "lab") { + labels[number] = trimmedName; + addresses[trimmedName] = number; + } else if (symbolType == "equ") { + constants[number] = trimmedName; + } + } +} + +void Symbols::Parse(std::string path) { + std::string line; + std::ifstream reader(path); + while (std::getline(reader, line)) { + auto lineElements = split(line, { " ", "\t" }); + if (lineElements.size() == 2) { + auto type = lineElements[0]; + auto dataElements = split(lineElements[1], { "," }); + kv_pair_t data; + for (auto& dataElement : dataElements) { + auto definition = split(dataElement, { "=" }); + if (definition.size() == 2) + data.element[definition[0]] = definition[1]; + } + + if (data.element.find("id") != data.element.end()) { + if (parsed.find(type) == parsed.end()) + parsed[type] = std::map(); + auto id = data.element["id"]; + data.element.erase("id"); + parsed[type][id] = data; + } + } + } +} + +std::vector Symbols::split(const std::string& input, const std::vector& delimiters) { + std::vector tokens; + boost::algorithm::split_regex( + tokens, + input, + boost::regex(boost::join(delimiters, "|")) + ); + return tokens; +} diff --git a/M6502/src/mos6502.cpp b/M6502/src/mos6502.cpp new file mode 100644 index 0000000..e6f6554 --- /dev/null +++ b/M6502/src/mos6502.cpp @@ -0,0 +1,1653 @@ +#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, "___"), + }; + + 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, "___"), + }; + + OverlayInstructionSet(overlay65sc02); + } +} + +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); + } +} + +void MOS6502::InstallInstructionSet(std::array basis) { + OverlayInstructionSet(basis, true); +} + +void MOS6502::OverlayInstructionSet(std::array overlay) { + OverlayInstructionSet(overlay, false); +} + +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."); + } + + instructions[i] = newInstruction; + } + } +} + +//// + +bool MOS6502::UpdateZeroFlag(uint8_t datum) { + return p.zero = datum == 0; +} + +bool MOS6502::UpdateNegativeFlag(int8_t datum) { + return p.negative = datum < 0; +} + +void MOS6502::UpdateZeroNegativeFlags(uint8_t datum) { + if (UpdateNegativeFlag((int8_t)datum)) + p.zero = false; + else + UpdateZeroFlag(datum); +} + +//// + +void MOS6502::PushByte(uint8_t value) { + SetByte(PageOne + s--, value); +} + +uint8_t MOS6502::PopByte() { + return GetByte(PageOne + ++s); +} + +void MOS6502::PushWord(uint16_t value) { + PushByte(HighByte(value)); + PushByte(LowByte(value)); +} + +uint16_t MOS6502::PopWord() { + auto low = PopByte(); + auto high = PopByte(); + return MakeWord(low, high); +} + +uint8_t MOS6502::FetchByte() { + return GetByte(pc++); +} + +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) + 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; + + UpdateZeroNegativeFlags((uint8_t)difference); + p.overflow = ((a ^ data) & (a ^ difference) & 0x80) != 0; + p.carry = HighByte((uint16_t)difference) == 0; + + a = (uint8_t)difference; +} + +void MOS6502::SBC_d(uint8_t data) { + auto carry = p.carry ? 0 : 1; + auto difference = a - data - carry; + + if (level < ProcessorType::Cpu65SC02) + UpdateZeroNegativeFlags((uint8_t)difference); + + p.overflow = ((a ^ data) & (a ^ difference) & 0x80) != 0; + p.carry = HighByte((uint16_t)difference) == 0; + + auto low = (uint8_t)(LowNybble(a) - LowNybble(data) - carry); + + auto lowNegative = (int8_t)low < 0; + if (lowNegative) + low -= 6; + + uint8_t high = HighNybble(a) - HighNybble(data) - (lowNegative ? 1 : 0); + + if ((int8_t)high < 0) + high -= 6; + + a = PromoteNybble(high) | LowNybble(low); + if (level >= ProcessorType::Cpu65SC02) + UpdateZeroNegativeFlags(a); +} + +void MOS6502::EOR(uint8_t data) { + a ^= data; + UpdateZeroNegativeFlags(a); +} + +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) + 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); + + UpdateZeroNegativeFlags((uint8_t)sum); + p.overflow = (~(a ^ data) & (a ^ sum) & 0x80) != 0; + p.carry = HighByte(sum) != 0; + + a = (uint8_t)sum; +} + +void MOS6502::ADC_d(uint8_t data) { + auto carry = (uint8_t)(p.carry ? 1 : 0); + auto sum = (uint16_t)(a + data + carry); + + if (level < ProcessorType::Cpu65SC02) + UpdateZeroNegativeFlags((uint8_t)sum); + + auto low = (uint8_t)(LowNybble(a) + LowNybble(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; + + if (high > 9) + high += 6; + + p.carry = high > 0xf; + + a = (uint8_t)(PromoteNybble(high) | LowNybble(low)); + if (level >= ProcessorType::Cpu65SC02) + UpdateZeroNegativeFlags(a); +} + +//// + +void MOS6502::RMB(uint16_t address, uint8_t flag) { + auto data = GetByte(address); + data &= (uint8_t)~flag; + SetByte(address, data); +} + +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(); + 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 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 MOS6502::NOP_imp() { +} + +void MOS6502::NOP2_imp() { + FetchByte(); +} + +void MOS6502::NOP3_imp() { + FetchWord(); +} + +// + +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 MOS6502::BBS6_zprel() { + BitBranch_Set(0x40); +} + +void MOS6502::BBS7_zprel() { + BitBranch_Set(0x80); +} diff --git a/M6502/src/system6502.cpp b/M6502/src/system6502.cpp new file mode 100644 index 0000000..72a3356 --- /dev/null +++ b/M6502/src/system6502.cpp @@ -0,0 +1,91 @@ +#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/README.md b/README.md new file mode 100644 index 0000000..966d557 --- /dev/null +++ b/README.md @@ -0,0 +1,68 @@ +## Invaders + +[![Build Status](https://travis-ci.org/MoleskiCoder/invaders.svg?branch=master)](https://travis-ci.org/MoleskiCoder/invaders) +[![Coverity Scan Build Status](https://scan.coverity.com/projects/12171/badge.svg)](https://scan.coverity.com/projects/moleskicoder-invaders) +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/b40b739726bd410186f700546b40e604)](https://www.codacy.com/app/MoleskiCoder/invaders?utm_source=github.com&utm_medium=referral&utm_content=MoleskiCoder/invaders&utm_campaign=Badge_Grade) + +Space Invaders Emulator written in C++ + +Uses SDL2 to provide graphics and SDL_mixer for sound effects. + +### Features + +* 2 player controls +* Coloured gel screen +* Sound effects +* Event driven Intel 8080 emulator +* Basic CP/M emulation (enough to run CPU tests) +* Intel 8080 profiler +* Intel 8080 disassembler + +### To be done + +* AppVeyor integration +* 8080 test cases. Maybe... + +## Compiling + +### Windows + +Compile with Visual Studio 2015 (via the solution) + +#### Prerequisites + +* SDL_Mixer +* Visual Studio 2015 + +### Linux + +* make opt +* make debug +* make coverage + +#### Prerequisites + +apt-get install build-essential libsdl2-dev libsdl2-mixer-dev + +## Running + +src/invaders + +### Keyboard Controls + +* 3: Insert Coin +* 1: 1P Start +* 2: 2P Start +* z: Left 1P +* x: Right 1P +* \\: Fire 1P +* ,: Left 2P +* .: Right 2P +* /: Fire 2P + +### XBox360 Controller + +* Left Bumper: Left +* Right Bumper: Right +* A Button: Fire + diff --git a/Z80/inc/Disassembler.h b/Z80/inc/Disassembler.h new file mode 100644 index 0000000..a007704 --- /dev/null +++ b/Z80/inc/Disassembler.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include + +class Z80; + +class Disassembler { +public: + Disassembler(); + + static std::string state(Z80& cpu); + std::string disassemble(const Z80& cpu); + + static std::string flag(uint8_t value, int flag, const std::string& represents); + static std::string flags(uint8_t value); + static std::string hex(uint8_t value); + static std::string hex(uint16_t value); + static std::string binary(uint8_t value); + static std::string decimal(uint8_t value); + + static std::string invalid(uint8_t value); + +private: + mutable boost::format m_formatter; + bool m_prefixCB; + bool m_prefixDD; + bool m_prefixED; + bool m_prefixFD; + + void disassemble(std::ostringstream& output, const Z80& cpu, uint16_t pc); + + void disassembleCB( + std::ostringstream& output, + const Z80& cpu, + uint16_t pc, + std::string& specification, + int& dumpCount, + int x, int y, int z, + int p, int q); + + void disassembleED( + std::ostringstream& output, + const Z80& cpu, + uint16_t pc, + std::string& specification, + int& dumpCount, + int x, int y, int z, + int p, int q); + + void disassembleOther( + std::ostringstream& output, + const Z80& cpu, + uint16_t pc, + std::string& specification, + int& dumpCount, + int x, int y, int z, + int p, int q); + + std::string RP(int rp) const; + std::string RP2(int rp) const; + std::string R(int r) const; + static std::string cc(int flag); + static std::string alu(int which); +}; diff --git a/Z80/inc/InputOutput.h b/Z80/inc/InputOutput.h new file mode 100644 index 0000000..bfc538f --- /dev/null +++ b/Z80/inc/InputOutput.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include "Signal.h" +#include "PortEventArgs.h" + +class InputOutput { +public: + InputOutput(); + + uint8_t read(uint8_t port) { return readInputPort(port); } + void write(uint8_t port, uint8_t value) { return writeOutputPort(port, value); } + + uint8_t readInputPort(uint8_t port); + void writeInputPort(uint8_t port, uint8_t value) { input[port] = value; } + + uint8_t readOutputPort(uint8_t port) { return output[port]; } + void writeOutputPort(uint8_t port, uint8_t value); + + Signal ReadingPort; + Signal ReadPort; + + Signal WritingPort; + Signal WrittenPort; + +protected: + void OnReadingPort(uint8_t port); + void OnReadPort(uint8_t port); + + void OnWritingPort(uint8_t port); + void OnWrittenPort(uint8_t port); + +private: + std::array input; + std::array output; +}; diff --git a/Z80/inc/PortEventArgs.h b/Z80/inc/PortEventArgs.h new file mode 100644 index 0000000..ea4c49b --- /dev/null +++ b/Z80/inc/PortEventArgs.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +class PortEventArgs { +public: + PortEventArgs(uint8_t port) + : m_port(port) {} + + uint8_t getPort() const { + return m_port; + } + +private: + uint8_t m_port; +}; diff --git a/Z80/inc/Profiler.h b/Z80/inc/Profiler.h new file mode 100644 index 0000000..c833b80 --- /dev/null +++ b/Z80/inc/Profiler.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +class Profiler { +public: + Profiler(); + ~Profiler(); + + void addInstruction(uint8_t instruction); + void addAddress(uint16_t address); + + void dump() const; + +private: + std::array m_instructions; + std::array m_addresses; + + void dumpInstructionProfiles() const; + void dumpAddressProfiles() const; +}; + diff --git a/Z80/inc/Signal.h b/Z80/inc/Signal.h new file mode 100644 index 0000000..96a2ad4 --- /dev/null +++ b/Z80/inc/Signal.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +template class Signal { +private: + typedef std::function delegate_t; + typedef std::vector delegates_t; + + delegates_t delegates; + +public: + void connect(delegate_t functor) { + delegates.push_back(functor); + } + + void fire(T& e) const { + if (!delegates.empty()) + for (auto& delegate : delegates) + delegate(e); + } +}; diff --git a/Z80/inc/Z80.h b/Z80/inc/Z80.h new file mode 100644 index 0000000..8fe63d8 --- /dev/null +++ b/Z80/inc/Z80.h @@ -0,0 +1,449 @@ +#pragma once + +#include "Processor.h" + +class Z80 : public Processor { +public: + enum StatusBits { + SF = Bit7, + ZF = Bit6, + YF = Bit5, + HC = Bit4, + XF = Bit3, + PF = Bit2, + VF = Bit2, + NF = Bit1, + CF = Bit0, + }; + + Z80(Memory& memory, InputOutput& ports); + + Signal ExecutingInstruction; + + void disableInterrupts(); + void enableInterrupts(); + + int interruptMaskable(uint8_t value) { return interrupt(true, value); } + int interruptMaskable() { return interruptMaskable(0); } + int interruptNonMaskable() { return interrupt(false, 0); } + + int interrupt(bool maskable, uint8_t value); + + int execute(uint8_t opcode); + int step(); + + bool getM1() const { return m1; } + + // Mutable access to processor!! + + register16_t& AF() { + return m_accumulatorFlags[m_accumulatorFlagsSet]; + } + + uint8_t& A() { return AF().high; } + uint8_t& F() { return AF().low; } + + register16_t& BC() { + return m_registers[m_registerSet][BC_IDX]; + } + + uint8_t& B() { return BC().high; } + uint8_t& C() { return BC().low; } + + register16_t& DE() { + return m_registers[m_registerSet][DE_IDX]; + } + + uint8_t& D() { return DE().high; } + uint8_t& E() { return DE().low; } + + register16_t& HL() { + return m_registers[m_registerSet][HL_IDX]; + } + + uint8_t& H() { return HL().high; } + uint8_t& L() { return HL().low; } + + register16_t& IX() { return m_ix; } + uint8_t& IXH() { return IX().high; } + uint8_t& IXL() { return IX().low; } + + register16_t& IY() { return m_iy; } + uint8_t& IYH() { return IY().high; } + uint8_t& IYL() { return IY().low; } + + uint8_t& REFRESH() { return m_refresh; } + uint8_t& IV() { return iv; } + int& IM() { return m_interruptMode; } + bool& IFF1() { return m_iff1; } + bool& IFF2() { return m_iff2; } + + register16_t& MEMPTR() { return m_memptr; } + + bool& M1() { return m1; } + + void exx() { + m_registerSet ^= 1; + } + + void exxAF() { + m_accumulatorFlagsSet = !m_accumulatorFlagsSet; + } + + virtual void reset(); + virtual void initialise(); + +private: + enum { BC_IDX, DE_IDX, HL_IDX }; + + std::array, 2> m_registers; + int m_registerSet; + + std::array m_accumulatorFlags; + int m_accumulatorFlagsSet; + + register16_t m_ix; + register16_t m_iy; + + uint8_t m_refresh; + uint8_t iv; + int m_interruptMode; + bool m_iff1; + bool m_iff2; + + register16_t m_memptr; + + bool m1; + + bool m_prefixCB; + bool m_prefixDD; + bool m_prefixED; + bool m_prefixFD; + + int8_t m_displacement; + + std::array m_halfCarryTableAdd = { { false, false, true, false, true, false, true, true } }; + std::array m_halfCarryTableSub = { { false, true, true, true, false, false, false, true } }; + + int fetchExecute() { + M1() = true; + return execute(fetchByteExecute()); + } + + uint8_t fetchByteExecute() { + if (!getM1()) + throw std::logic_error("M1 cannot be high"); + return fetchByte(); + } + + uint8_t fetchByteData() { + if (getM1()) + throw std::logic_error("M1 cannot be low"); + return fetchByte(); + } + + void incrementRefresh() { + auto incremented = ((REFRESH() & Mask7) + 1) & Mask7; + REFRESH() = (REFRESH() & Bit7) | incremented; + } + + void clearFlag(int flag) { F() &= ~flag; } + void setFlag(int flag) { F() |= flag; } + + void setFlag(int flag, int condition) { setFlag(flag, condition != 0); } + void setFlag(int flag, uint32_t condition) { setFlag(flag, condition != 0); } + void setFlag(int flag, bool condition) { condition ? setFlag(flag) : clearFlag(flag); } + + void clearFlag(int flag, int condition) { clearFlag(flag, condition != 0); } + void clearFlag(int flag, uint32_t condition) { clearFlag(flag, condition != 0); } + void clearFlag(int flag, bool condition) { condition ? clearFlag(flag) : setFlag(flag); } + + uint8_t& DISPLACED() { + if (!(m_prefixDD || m_prefixFD)) + throw std::logic_error("Unprefixed indexed displacement requested"); + m_memory.ADDRESS().word = MEMPTR().word = (m_prefixDD ? IX() : IY()).word + m_displacement; + return m_memory.reference(); + } + + uint8_t& R(int r) { + switch (r) { + case 0: + return B(); + case 1: + return C(); + case 2: + return D(); + case 3: + return E(); + case 4: + return ALT_HL().high; + case 5: + return ALT_HL().low; + case 6: + if (m_prefixDD || m_prefixFD) { + m_displacement = fetchByteData(); + return DISPLACED(); + } + m_memory.ADDRESS() = HL(); + return m_memory.reference(); + case 7: + return A(); + } + throw std::logic_error("Unhandled registry mechanism"); + } + + uint8_t& R2(int r) { + switch (r) { + case 0: + return B(); + case 1: + return C(); + case 2: + return D(); + case 3: + return E(); + case 4: + return H(); + case 5: + return L(); + case 6: + m_memory.ADDRESS() = HL(); + return m_memory.reference(); + case 7: + return A(); + } + throw std::logic_error("Unhandled registry mechanism"); + } + + register16_t& RP(int rp) { + switch (rp) { + case 3: + return sp; + case HL_IDX: + return ALT_HL(); + default: + return m_registers[m_registerSet][rp]; + } + } + + register16_t& ALT_HL() { + if (m_prefixDD) + return IX(); + else if (m_prefixFD) + return IY(); + return HL(); + } + + register16_t& RP2(int rp) { + switch (rp) { + case 3: + return AF(); + case HL_IDX: + return ALT_HL(); + default: + return m_registers[m_registerSet][rp]; + } + } + + uint8_t getViaMemptr(register16_t address) { + m_memory.ADDRESS() = address; + MEMPTR().word = address.word + 1; + return m_memory.reference(); + } + + void setViaMemptr(register16_t address, uint8_t value) { + m_memory.ADDRESS() = address; + m_memory.reference() = value; + ++address.word; + MEMPTR().low = address.low; + MEMPTR().high = value; + } + + register16_t getWordViaMemptr(register16_t address) { + register16_t returned; + m_memory.ADDRESS() = address; + returned.low = m_memory.reference(); + m_memory.ADDRESS().word++; + returned.high = m_memory.reference(); + MEMPTR() = m_memory.ADDRESS(); + return returned; + } + + void setWordViaMemptr(register16_t address, register16_t value) { + m_memory.ADDRESS() = address; + m_memory.reference() = value.low; + m_memory.ADDRESS().word++; + m_memory.reference() = value.high; + MEMPTR() = m_memory.ADDRESS(); + } + + void setPcViaMemptr(register16_t address) { + MEMPTR() = pc = address; + } + + 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); + } + + int buildHalfCarryIndex(uint8_t before, uint8_t value, int calculation) { + return ((before & 0x88) >> 1) | ((value & 0x88) >> 2) | ((calculation & 0x88) >> 3); + } + + void adjustHalfCarryAdd(uint8_t before, uint8_t value, int calculation) { + auto index = buildHalfCarryIndex(before, value, calculation); + setFlag(HC, m_halfCarryTableAdd[index & 0x7]); + } + + void adjustHalfCarrySub(uint8_t before, uint8_t value, int calculation) { + auto index = buildHalfCarryIndex(before, value, calculation); + setFlag(HC, m_halfCarryTableSub[index & 0x7]); + } + + void adjustOverflowAdd(uint8_t before, uint8_t value, uint8_t calculation) { + adjustOverflowAdd(before & SF, value & SF, calculation & SF); + } + + void adjustOverflowAdd(int beforeNegative, int valueNegative, int afterNegative) { + auto overflow = (beforeNegative == valueNegative) && (beforeNegative != afterNegative); + setFlag(VF, overflow); + } + + void adjustOverflowSub(uint8_t before, uint8_t value, uint8_t calculation) { + adjustOverflowSub(before & SF, value & SF, calculation & SF); + } + + void adjustOverflowSub(int beforeNegative, int valueNegative, int afterNegative) { + auto overflow = (beforeNegative != valueNegative) && (beforeNegative != afterNegative); + setFlag(VF, overflow); + } + + void executeCB(int x, int y, int z, int p, int q); + 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 adjustSign(uint8_t value); + void adjustZero(uint8_t value); + void adjustParity(uint8_t value); + void adjustSZ(uint8_t value); + void adjustSZP(uint8_t value); + void adjustXY(uint8_t value); + void adjustSZPXY(uint8_t value); + void adjustSZXY(uint8_t value); + + void postIncrement(uint8_t value); + void postDecrement(uint8_t value); + + void restart(uint8_t address); + + void jrConditional(int conditional); + void jrConditionalFlag(int flag); + + void ret(); + void retn(); + void reti(); + + void returnConditional(int condition); + void returnConditionalFlag(int flag); + + void jumpConditional(int condition); + void jumpConditionalFlag(int flag); + + void call(register16_t address); + void callConditional(register16_t address, int condition); + void callConditionalFlag(register16_t address, int flag); + + void sbc(register16_t& operand, register16_t value); + void adc(register16_t& operand, register16_t value); + + void add(register16_t& operand, register16_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); + + void rlca(); + void rrca(); + void rla(); + void rra(); + + void rlc(uint8_t& operand); + void rrc(uint8_t& operand); + void rl(uint8_t& operand); + void rr(uint8_t& operand); + void sla(uint8_t& operand); + void sra(uint8_t& operand); + void sll(uint8_t& operand); + void srl(uint8_t& operand); + + void bit(int n, uint8_t& operand); + void res(int n, uint8_t& operand); + void set(int nit, uint8_t& operand); + + void daa(); + + void scf(); + void ccf(); + void cpl(); + + void xhtl(register16_t& operand); + void xhtl(); + + void blockCompare(); + + void cpi(); + void cpir(); + + void cpd(); + void cpdr(); + + void blockLoad(register16_t source, register16_t destination); + + void ldi(); + void ldir(); + + void ldd(); + void lddr(); + + void ini(); + void inir(); + + void ind(); + void indr(); + + void blockOut(); + + void outi(); + void otir(); + + void outd(); + void otdr(); + + void neg(); + + void rrd(); + void rld(); + + void writePort() { + m_ports.write(m_memory.ADDRESS().low, m_memory.DATA()); + } + + void readPort() { + m_memory.placeDATA(m_ports.read(m_memory.ADDRESS().low)); + } +}; \ No newline at end of file diff --git a/Z80/src/Disassembler.cpp b/Z80/src/Disassembler.cpp new file mode 100644 index 0000000..3051918 --- /dev/null +++ b/Z80/src/Disassembler.cpp @@ -0,0 +1,725 @@ +#include "stdafx.h" +#include "Disassembler.h" + +#include +#include +#include + +#include "Memory.h" +#include "Z80.h" + +Disassembler::Disassembler() { + // Disable exceptions where too many format arguments are available + m_formatter.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit); +} + +std::string Disassembler::state(Z80& cpu) { + + auto pc = cpu.getProgramCounter(); + auto sp = cpu.getStackPointer(); + + auto a = cpu.A(); + auto f = cpu.F(); + + auto b = cpu.B(); + auto c = cpu.C(); + + auto d = cpu.D(); + auto e = cpu.E(); + + auto h = cpu.H(); + auto l = cpu.L(); + + auto i = cpu.IV(); + auto r = cpu.REFRESH(); + + auto im = cpu.IM(); + + std::ostringstream output; + + output + << "PC=" << hex(pc.word) + << " " + << "SP=" << hex(sp.word) + << " " << "A=" << hex(a) << " " << "F=" << flags(f) + << " " << "B=" << hex(b) << " " << "C=" << hex(c) + << " " << "D=" << hex(d) << " " << "E=" << hex(e) + << " " << "H=" << hex(h) << " " << "L=" << hex(l) + << " " << "I=" << hex(i) << " " << "R=" << hex(r) + << " " << "IM=" << im; + + return output.str(); +} + +std::string Disassembler::RP(int rp) const { + switch (rp) { + case 0: + return "BC"; + case 1: + return "DE"; + case 2: + if (m_prefixDD) + return "IX"; + if (m_prefixFD) + return "IY"; + return "HL"; + case 3: + return "SP"; + } + throw std::logic_error("Unhandled register pair"); +} + +std::string Disassembler::RP2(int rp) const { + switch (rp) { + case 0: + return "BC"; + case 1: + return "DE"; + case 2: + if (m_prefixDD) + return "IX"; + if (m_prefixFD) + return "IY"; + return "HL"; + case 3: + return "AF"; + } + throw std::logic_error("Unhandled register pair"); +} + +std::string Disassembler::R(int r) const { + switch (r) { + case 0: + return "B"; + case 1: + return "C"; + case 2: + return "D"; + case 3: + return "E"; + case 4: + if (m_prefixDD) + return "IXH"; + if (m_prefixFD) + return "IYH"; + return "H"; + case 5: + if (m_prefixDD) + return "IXL"; + if (m_prefixFD) + return "IYL"; + return "L"; + case 6: + if (m_prefixDD || m_prefixFD) { + if (m_prefixDD) + return "IX+%4%"; + if (m_prefixFD) + return "IY+%4%"; + } + else { + return "(HL)"; + } + case 7: + return "A"; + } + throw std::logic_error("Unhandled register"); +} + +std::string Disassembler::cc(int flag) { + switch (flag) { + case 0: + return "NZ"; + case 1: + return "Z"; + case 2: + return "NC"; + case 3: + return "C"; + case 4: + return "PO"; + case 5: + return "PE"; + case 6: + return "P"; + case 7: + return "M"; + } + throw std::logic_error("Unhandled condition"); +} + +std::string Disassembler::alu(int which) { + switch (which) { + case 0: // ADD A,n + return "ADD"; + case 1: // ADC + return "ADC"; + case 2: // SUB n + return "SUB"; + case 3: // SBC A,n + return "SBC"; + case 4: // AND n + return "AND"; + case 5: // XOR n + return "XOR"; + case 6: // OR n + return "OR"; + case 7: // CP n + return "CP"; + } + throw std::logic_error("Unhandled alu operation"); +} + +std::string Disassembler::disassemble(const Z80& cpu) { + m_prefixCB = m_prefixDD = m_prefixED = m_prefixFD = false; + std::ostringstream output; + disassemble(output, cpu, cpu.getProgramCounter().word); + return output.str(); +} + +void Disassembler::disassemble(std::ostringstream& output, const Z80& cpu, uint16_t pc) { + + const auto& memory = cpu.getMemory(); + auto opcode = memory.peek(pc); + + // hex opcode + output << hex(opcode); + + auto x = (opcode & 0b11000000) >> 6; + auto y = (opcode & 0b111000) >> 3; + auto z = (opcode & 0b111); + + auto p = (y & 0b110) >> 1; + auto q = (y & 1); + + auto immediate = memory.peek(pc + 1); + auto absolute = memory.peekWord(pc + 1); + auto displacement = (int8_t)immediate; + auto relative = pc + displacement + 2; + auto indexedImmediate = memory.peek(pc + 1); + + auto dumpCount = 0; + + std::string specification = ""; + + if (m_prefixCB) + disassembleCB( + output, cpu, pc, + specification, dumpCount, + x, y, z, p, q); + else if (m_prefixED) + disassembleED( + output, cpu, pc, + specification, dumpCount, + x, y, z, p, q); + else + disassembleOther( + output, cpu, pc, + specification, dumpCount, + x, y, z, p, q); + + for (int i = 0; i < dumpCount; ++i) + output << hex(memory.peek(pc + i + 1)); + + auto outputFormatSpecification = !m_prefixDD; + if (m_prefixDD) { + if (opcode != 0xdd) { + outputFormatSpecification = true; + } + } + + if (outputFormatSpecification) { + output << '\t'; + m_formatter.parse(specification); + output << m_formatter % (int)immediate % (int)absolute % relative % (int)displacement % indexedImmediate; + } +} + +void Disassembler::disassembleCB( + std::ostringstream& output, + const Z80& cpu, + uint16_t pc, + std::string& specification, + int& dumpCount, + int x, int y, int z, + int p, int q) { + + switch (x) { + case 0: // rot[y] r[z] + switch (y) { + case 0: + specification = "RLC " + R(z); + break; + case 1: + specification = "RRC " + R(z); + break; + case 2: + specification = "RL " + R(z); + break; + case 3: + specification = "RR " + R(z); + break; + case 4: + specification = "SLA " + R(z); + break; + case 5: + specification = "SRA " + R(z); + break; + case 6: + specification = "SWAP " + R(z); + break; + case 7: + specification = "SRL " + R(z); + break; + } + break; + case 1: // BIT y, r[z] + specification = "BIT " + decimal(y) + "," + R(z); + break; + case 2: // RES y, r[z] + specification = "RES " + decimal(y) + "," + R(z); + break; + case 3: // SET y, r[z] + specification = "SET " + decimal(y) + "," + R(z); + break; + } +} + +void Disassembler::disassembleED( + std::ostringstream& output, + const Z80& cpu, + uint16_t pc, + std::string& specification, + int& dumpCount, + int x, int y, int z, + int p, int q) { + switch (x) { + case 0: + case 3: + specification = "NONI NOP"; + break; + case 1: + switch (z) { + case 2: + switch (q) { + case 0: // SBC HL,rp + specification = "SBC HL," + RP(p); + break; + case 1: // ADC HL,rp + specification = "ADC HL," + RP(p); + break; + } + case 3: + switch (q) { + case 0: // LD (nn),rp + specification = "LD (%2$04XH)," + RP(p); + break; + case 1: // LD rp,(nn) + specification = "LD " + RP(p) + ",(%2$04XH)"; + break; + } + dumpCount += 2; + break; + case 7: + switch (y) { + case 0: + specification = "LD I,A"; + break; + case 1: + specification = "LD R,A"; + break; + case 2: + specification = "LD A,I"; + break; + case 3: + specification = "LD A,R"; + break; + case 4: + specification = "RRD"; + break; + case 5: + specification = "RLD"; + break; + case 6: + case 7: + specification = "NOP"; + break; + } + break; + } + break; + case 2: + switch (z) { + case 0: // LD + switch (y) { + case 4: // LDI + specification = "LDI"; + break; + case 5: // LDD + specification = "LDD"; + break; + case 6: // LDIR + specification = "LDIR"; + break; + case 7: // LDDR + specification = "LDDR"; + break; + } + break; + case 1: // CP + switch (y) { + case 4: // CPI + specification = "CPI"; + break; + case 5: // CPD + specification = "CPD"; + break; + case 6: // CPIR + specification = "CPIR"; + break; + case 7: // CPDR + specification = "CPDR"; + break; + } + break; + case 2: // IN + switch (y) { + case 4: // INI + specification = "INI"; + break; + case 5: // IND + specification = "IND"; + break; + case 6: // INIR + specification = "INIR"; + break; + case 7: // INDR + specification = "INDR"; + break; + } + break; + case 3: // OUT + switch (y) { + case 4: // OUTI + specification = "OUTI"; + break; + case 5: // OUTD + specification = "OUTD"; + break; + case 6: // OTIR + specification = "OTIR"; + break; + case 7: // OTDR + specification = "OTDR"; + break; + } + break; + } + break; + } +} + +void Disassembler::disassembleOther( + std::ostringstream& output, + const Z80& cpu, + uint16_t pc, + std::string& specification, + int& dumpCount, + int x, int y, int z, + int p, int q) { + + switch (x) { + case 0: + switch (z) { + case 0: // Relative jumps and assorted ops + switch (y) { + case 0: // NOP + specification = "NOP"; + break; + case 1: // EX AF AF' + specification = "EX AF AF'"; + break; + case 2: // DJNZ d + specification = "DJNZ %3$04XH"; + dumpCount += 2; + break; + case 3: // JR d + specification = "JR %3$04XH"; + dumpCount++; + break; + default: // JR cc,d + specification = "JR " + cc(y - 4) + ",%3$04XH"; + dumpCount++; + break; + } + break; + case 1: // 16-bit load immediate/add + switch (q) { + case 0: // LD rp,nn + specification = "LD " + RP(p) + ",%2$04XH"; + dumpCount += 2; + break; + case 1: // ADD HL,rp + specification = "ADD HL," + RP(p); + break; + } + break; + case 2: // Indirect loading + switch (q) { + case 0: + switch (p) { + case 0: // LD (BC),A + specification = "LD (BC),A"; + break; + case 1: // LD (DE),A + specification = "LD (DE),A"; + break; + case 2: // LD (nn),HL + specification = "LD (%2$04XH),HL"; + dumpCount += 2; + break; + case 3: // LD (nn),A + specification = "LD (%2$04XH),A"; + dumpCount += 2; + break; + } + break; + case 1: + switch (p) { + case 0: // LD A,(BC) + specification = "LD A,(BC)"; + break; + case 1: // LD A,(DE) + specification = "LD A,(DE)"; + break; + case 2: // LD HL,(nn) + specification = "LD HL,(%2$04XH)"; + dumpCount += 2; + break; + case 3: // LD A,(nn) + specification = "LD A,(%2$04XH)"; + dumpCount += 2; + break; + } + break; + } + break; + case 3: // 16-bit INC/DEC + switch (q) { + case 0: // INC rp + specification = "INC " + RP(p); + break; + case 1: // DEC rp + specification = "DEC " + RP(p); + break; + } + break; + case 4: // 8-bit INC + specification = "INC " + R(y); + break; + case 5: // 8-bit DEC + specification = "DEC " + R(y); + break; + case 6: // 8-bit load immediate + specification = "LD " + R(y); + if (y == 6 && (m_prefixDD || m_prefixFD)) { + specification += ",%5$02XH"; + dumpCount++; + } else { + specification += ",%1$02XH"; + } + dumpCount++; + break; + case 7: // Assorted operations on accumulator/flags + switch (y) { + case 0: + specification = "RLCA"; + break; + case 1: + specification = "RRCA"; + break; + case 2: + specification = "RLA"; + break; + case 3: + specification = "RRA"; + break; + case 4: + specification = "DAA"; + break; + case 5: + specification = "CPL"; + break; + case 6: + specification = "SCF"; + break; + case 7: + specification = "CCF"; + break; + } + break; + } + break; + case 1: // 8-bit loading + if (z == 6 && y == 6) { // Exception (replaces LD (HL), (HL)) + specification = "HALT"; + } else { + specification = "LD " + R(y) + "," + R(z); + } + break; + case 2: // Operate on accumulator and register/memory location + specification = alu(y) + " A," + R(z); + break; + case 3: + switch (z) { + case 0: // Conditional return + specification = "RET " + cc(y); + break; + case 1: // POP & various ops + switch (q) { + case 0: // POP rp2[p] + specification = "POP " + RP2(p); + break; + case 1: + switch (p) { + case 0: // RET + specification = "RET"; + break; + case 1: // EXX + specification = "EXX"; + break; + case 2: // JP (HL) + specification = "JP (HL)"; + break; + case 3: // LD SP,HL + specification = "LD SP,Hl"; + break; + } + } + break; + case 2: // Conditional jump + specification = "JP " + cc(y) + ",%2$04XH"; + dumpCount += 2; + break; + case 3: // Assorted operations + switch (y) { + case 0: // JP nn + specification = "JP %2$04XH"; + dumpCount += 2; + break; + case 1: // CB prefix + m_prefixCB = true; + disassemble(output, cpu, pc + 1); + break; + case 2: // OUT (n),A + specification = "OUT (%1$02XH),A"; + dumpCount++; + break; + case 3: // IN A,(n) + specification = "IN A,(%1$02XH)"; + dumpCount++; + break; + case 4: // EX (SP),HL + specification = "EX (SP),HL"; + break; + case 5: // EX DE,HL + specification = "EX DE,HL"; + break; + case 6: // DI + specification = "DI"; + break; + case 7: // EI + specification = "EI"; + break; + } + break; + case 4: // Conditional call: CALL cc[y], nn + specification = "CALL " + cc(y) + ",%2$04XH"; + dumpCount += 2; + break; + case 5: // PUSH & various ops + switch (q) { + case 0: // PUSH rp2[p] + specification = "PUSH " + RP2(p); + break; + case 1: + switch (p) { + case 0: // CALL nn + specification = "CALL %2$04XH"; + dumpCount += 2; + break; + case 1: // DD prefix + m_prefixDD = true; + disassemble(output, cpu, pc + 1); + break; + case 2: // ED prefix + m_prefixED = true; + disassemble(output, cpu, pc + 1); + break; + case 3: // FD prefix + m_prefixFD = true; + disassemble(output, cpu, pc + 1); + break; + } + } + break; + case 6: // Operate on accumulator and immediate operand: alu[y] n + specification = alu(y) + " A,%1$02XH"; + dumpCount++; + break; + case 7: // Restart: RST y * 8 + specification = "RST " + hex((uint8_t)(y * 8)); + break; + } + break; + } +} + +std::string Disassembler::flag(uint8_t value, int flag, const std::string& represents) { + std::ostringstream output; + output << (value & flag ? represents : "-"); + return output.str(); +} + +std::string Disassembler::flags(uint8_t value) { + std::ostringstream output; + output + << flag(value, Z80::SF, "S") + << flag(value, Z80::ZF, "Z") + << flag(value, Z80::YF, "Y") + << flag(value, Z80::HC, "H") + << flag(value, Z80::XF, "X") + << flag(value, Z80::PF, "P") + << flag(value, Z80::NF, "N") + << flag(value, Z80::CF, "C"); + return output.str(); +} + +std::string Disassembler::hex(uint8_t value) { + std::ostringstream output; + output << std::hex << std::setw(2) << std::setfill('0') << (int)value; + return output.str(); +} + +std::string Disassembler::hex(uint16_t value) { + std::ostringstream output; + output << std::hex << std::setw(4) << std::setfill('0') << (int)value; + return output.str(); +} + +std::string Disassembler::binary(uint8_t value) { + std::ostringstream output; + output << std::bitset<8>(value); + return output.str(); +} + +std::string Disassembler::decimal(uint8_t value) { + std::ostringstream output; + output << (int)value; + return output.str(); +} + +std::string Disassembler::invalid(uint8_t value) { + std::ostringstream output; + output << "Invalid instruction: " << hex(value) << "(" << binary(value) << ")"; + return output.str(); +} \ No newline at end of file diff --git a/Z80/src/InputOutput.cpp b/Z80/src/InputOutput.cpp new file mode 100644 index 0000000..d7ff558 --- /dev/null +++ b/Z80/src/InputOutput.cpp @@ -0,0 +1,38 @@ +#include "stdafx.h" +#include "InputOutput.h" + +InputOutput::InputOutput() { +} + +uint8_t InputOutput::readInputPort(uint8_t port) { + OnReadingPort(port); + auto value = input[port]; + OnReadPort(port); + return value; +} + +void InputOutput::writeOutputPort(uint8_t port, uint8_t value) { + OnWritingPort(port); + output[port] = value; + OnWrittenPort(port); +} + +void InputOutput::OnReadingPort(uint8_t port) { + PortEventArgs event(port); + ReadingPort.fire(event); +} + +void InputOutput::OnReadPort(uint8_t port) { + PortEventArgs event(port); + ReadPort.fire(event); +} + +void InputOutput::OnWritingPort(uint8_t port) { + PortEventArgs event(port); + WritingPort.fire(event); +} + +void InputOutput::OnWrittenPort(uint8_t port) { + PortEventArgs event(port); + WrittenPort.fire(event); +} diff --git a/Z80/src/Profiler.cpp b/Z80/src/Profiler.cpp new file mode 100644 index 0000000..628af2b --- /dev/null +++ b/Z80/src/Profiler.cpp @@ -0,0 +1,42 @@ +#include "stdafx.h" +#include "Profiler.h" +#include "Disassembler.h" + +Profiler::Profiler() { + std::fill(m_instructions.begin(), m_instructions.end(), 0); + std::fill(m_addresses.begin(), m_addresses.end(), 0); +} + +Profiler::~Profiler() { +} + +void Profiler::addInstruction(uint8_t instruction) { + m_instructions[instruction]++; +} + +void Profiler::addAddress(uint16_t address) { + m_addresses[address]++; +} + +void Profiler::dump() const { + dumpInstructionProfiles(); + dumpAddressProfiles(); +} + +void Profiler::dumpInstructionProfiles() const { + std::cout << "** instructions" << std::endl; + for (int i = 0; i < 0x100; ++i) { + auto count = m_instructions[i]; + if (count > 0) + std::cout << Disassembler::hex((uint8_t)i) << "\t" << count << std::endl; + } +} + +void Profiler::dumpAddressProfiles() const { + std::cout << "** addresses" << std::endl; + for (int i = 0; i < 0x10000; ++i) { + auto count = m_addresses[i]; + if (count > 0) + std::cout << Disassembler::hex((uint16_t)i) << "\t" << count << std::endl; + } +} diff --git a/Z80/src/Z80.cpp b/Z80/src/Z80.cpp new file mode 100644 index 0000000..d9e00f9 --- /dev/null +++ b/Z80/src/Z80.cpp @@ -0,0 +1,1611 @@ +#include "stdafx.h" +#include "Z80.h" + +// based on http://www.z80.info/decoding.htm +// Half carry flag help from https://github.com/oubiwann/z80 + +Z80::Z80(Memory& memory, InputOutput& ports) +: Processor(memory, ports), + m_registerSet(0), + m_accumulatorFlagsSet(0), + m_refresh(0x7f), + iv(0xff), + m_interruptMode(0), + m_iff1(false), + m_iff2(false), + m1(false), + m_prefixCB(false), + m_prefixDD(false), + m_prefixED(false), + m_prefixFD(false) { + IX().word = 0xffff; + IY().word = 0xffff; + MEMPTR().word = 0; +} + +void Z80::reset() { + Processor::reset(); + IFF1() = IFF2() = false; +} + +void Z80::initialise() { + + Processor::initialise(); + + IM() = 0; + + AF().word = 0xffff; + + BC().word = 0xffff; + DE().word = 0xffff; + HL().word = 0xffff; + + exxAF(); + exx(); + + AF().word = 0xffff; + + BC().word = 0xffff; + DE().word = 0xffff; + HL().word = 0xffff; + + IX().word = 0xffff; + IY().word = 0xffff; + + REFRESH() = 0x7f; + IV() = 0xff; + MEMPTR().word = 0; + + m_prefixCB = false; + m_prefixDD = false; + m_prefixED = false; + m_prefixFD = false; +} + +#pragma region Interrupt routines + +void Z80::disableInterrupts() { + IFF1() = IFF2() = false; +} + +void Z80::enableInterrupts() { + IFF1() = IFF2() = true; +} + +int Z80::interrupt(bool maskable, uint8_t value) { + cycles = 0; + if (!maskable || (maskable && IFF1())) { + if (maskable) { + disableInterrupts(); + switch (IM()) { + case 0: + M1() = true; + cycles += execute(value); + break; + case 1: + restart(7 << 3); + cycles += 13; + break; + case 2: + pushWord(pc); + pc.low = value; + pc.high = IV(); + cycles += 19; + break; + } + } else { + IFF1() = 0; + restart(0x66); + cycles += 13; + } + } + // Could be zero for a masked interrupt... + return cycles; +} + +#pragma endregion Interrupt routines + +#pragma region Flag manipulation helpers + +void Z80::adjustSign(uint8_t value) { + setFlag(SF, value & SF); +} + +void Z80::adjustZero(uint8_t value) { + clearFlag(ZF, value); +} + +void Z80::adjustParity(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 }; + auto set = lookup[highNibble(value)] + lookup[lowNibble(value)]; + clearFlag(PF, set % 2); +} + +void Z80::adjustSZ(uint8_t value) { + adjustSign(value); + adjustZero(value); +} + +void Z80::adjustSZP(uint8_t value) { + adjustSZ(value); + adjustParity(value); +} + +void Z80::adjustXY(uint8_t value) { + setFlag(XF, value & XF); + setFlag(YF, value & YF); +} + +void Z80::adjustSZPXY(uint8_t value) { + adjustSZP(value); + adjustXY(value); +} + +void Z80::adjustSZXY(uint8_t value) { + adjustSZ(value); + adjustXY(value); +} + +void Z80::postIncrement(uint8_t value) { + adjustSZXY(value); + clearFlag(NF); + setFlag(VF, value == Bit7); + clearFlag(HC, lowNibble(value)); +} + +void Z80::postDecrement(uint8_t value) { + adjustSZXY(value); + setFlag(NF); + setFlag(VF, value == Mask7); + clearFlag(HC, lowNibble(value + 1)); +} + +#pragma endregion Flag manipulation helpers + +#pragma region PC manipulation: call/ret/jp/jr + +void Z80::restart(uint8_t address) { + pushWord(pc); + register16_t destination; + destination.word = address; + setPcViaMemptr(destination); +} + +void Z80::jrConditional(int conditional) { + auto offset = (int8_t)fetchByteData(); + if (conditional) { + register16_t destination; + destination.word = pc.word + offset; + setPcViaMemptr(destination); + cycles += 5; + } +} + +void Z80::jrConditionalFlag(int flag) { + switch (flag) { + case 0: // NZ + jrConditional(!(F() & ZF)); + break; + case 1: // Z + jrConditional(F() & ZF); + break; + case 2: // NC + jrConditional(!(F() & CF)); + break; + case 3: // C + jrConditional(F() & CF); + break; + case 4: // PO + jrConditional(!(F() & PF)); + break; + case 5: // PE + jrConditional(F() & PF); + break; + case 6: // P + jrConditional(!(F() & SF)); + break; + case 7: // M + jrConditional(F() & SF); + break; + } +} + +void Z80::jumpConditional(int conditional) { + auto address = fetchWord(); + if (conditional) + pc = address; + MEMPTR() = address; +} + +void Z80::jumpConditionalFlag(int flag) { + switch (flag) { + case 0: // NZ + jumpConditional(!(F() & ZF)); + break; + case 1: // Z + jumpConditional(F() & ZF); + break; + case 2: // NC + jumpConditional(!(F() & CF)); + break; + case 3: // C + jumpConditional(F() & CF); + break; + case 4: // PO + jumpConditional(!(F() & PF)); + break; + case 5: // PE + jumpConditional(F() & PF); + break; + case 6: // P + jumpConditional(!(F() & SF)); + break; + case 7: // M + jumpConditional(F() & SF); + break; + } +} + +void Z80::ret() { + setPcViaMemptr(popWord()); +} + +void Z80::retn() { + ret(); + IFF1() = IFF2(); +} + +void Z80::reti() { + retn(); +} + +void Z80::returnConditional(int condition) { + if (condition) { + ret(); + cycles += 6; + } +} + +void Z80::returnConditionalFlag(int flag) { + switch (flag) { + case 0: // NZ + returnConditional(!(F() & ZF)); + break; + case 1: // Z + returnConditional(F() & ZF); + break; + case 2: // NC + returnConditional(!(F() & CF)); + break; + case 3: // C + returnConditional(F() & CF); + break; + case 4: // PO + returnConditional(!(F() & PF)); + break; + case 5: // PE + returnConditional(F() & PF); + break; + case 6: // P + returnConditional(!(F() & SF)); + break; + case 7: // M + returnConditional(F() & SF); + break; + } +} + +void Z80::call(register16_t address) { + pushWord(pc); + pc = address; +} + +void Z80::callConditional(register16_t address, int condition) { + if (condition) { + call(address); + cycles += 7; + } + MEMPTR() = address; +} + +void Z80::callConditionalFlag(register16_t address, int flag) { + switch (flag) { + case 0: // NZ + callConditional(address, !(F() & ZF)); + break; + case 1: // Z + callConditional(address, F() & ZF); + break; + case 2: // NC + callConditional(address, !(F() & CF)); + break; + case 3: // C + callConditional(address, F() & CF); + break; + case 4: // PO + callConditional(address, !(F() & PF)); + break; + case 5: // PE + callConditional(address, F() & PF); + break; + case 6: // P + callConditional(address, !(F() & SF)); + break; + case 7: // M + callConditional(address, F() & SF); + break; + } +} + +#pragma endregion PC manipulation: call/ret/jp/jr + +#pragma region 16-bit arithmetic + +void Z80::sbc(register16_t& operand, register16_t value) { + + 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(SF, afterNegative); + clearFlag(ZF, operand.word); + adjustHalfCarrySub(before.high, value.high, operand.high); + adjustOverflowSub(beforeNegative, valueNegative, afterNegative); + setFlag(NF); + setFlag(CF, result & Bit16); + adjustXY(operand.high); +} + +void Z80::adc(register16_t& operand, register16_t value) { + + 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(SF, afterNegative); + clearFlag(ZF, result); + adjustHalfCarryAdd(before.high, value.high, operand.high); + adjustOverflowAdd(beforeNegative, valueNegative, afterNegative); + clearFlag(NF); + setFlag(CF, result & Bit16); + adjustXY(operand.high); +} + +void Z80::add(register16_t& operand, register16_t value) { + + auto before = operand; + + auto result = before.word + value.word; + + operand.word = result; + + clearFlag(NF); + setFlag(CF, result & Bit16); + adjustHalfCarryAdd(before.high, value.high, operand.high); + adjustXY(operand.high); +} + +#pragma endregion 16-bit arithmetic + +#pragma region ALU + +void Z80::add(uint8_t& operand, uint8_t value, int carry) { + + register16_t result; + result.word = operand + value + carry; + + adjustHalfCarryAdd(operand, value, result.low); + adjustOverflowAdd(operand, value, result.low); + + operand = result.low; + + clearFlag(NF); + setFlag(CF, result.word & Bit8); + adjustSZXY(operand); +} + +void Z80::adc(uint8_t& operand, uint8_t value) { + add(operand, value, F() & CF); +} + +void Z80::sub(uint8_t& operand, uint8_t value, int carry) { + + register16_t result; + result.word = operand - value - carry; + + adjustHalfCarrySub(operand, value, result.low); + adjustOverflowSub(operand, value, result.low); + + operand = result.low; + + setFlag(NF); + setFlag(CF, result.word & Bit8); + adjustSZXY(operand); +} + +void Z80::sbc(uint8_t& operand, uint8_t value) { + sub(operand, value, F() & CF); +} + +void Z80::andr(uint8_t& operand, uint8_t value) { + operand &= value; + setFlag(HC); + clearFlag(CF | NF); + adjustSZPXY(operand); +} + +void Z80::xorr(uint8_t& operand, uint8_t value) { + operand ^= value; + clearFlag(HC | CF | NF); + adjustSZPXY(operand); +} + +void Z80::orr(uint8_t& operand, uint8_t value) { + operand |= value; + clearFlag(HC | CF | NF); + adjustSZPXY(operand); +} + +void Z80::compare(uint8_t value) { + auto check = A(); + sub(check, value); + adjustXY(value); +} + +#pragma endregion ALU + +#pragma region Shift and rotate + +void Z80::rlc(uint8_t& operand) { + auto carry = operand & Bit7; + operand <<= 1; + setFlag(CF, carry); + carry ? operand |= Bit0 : operand &= ~Bit0; + clearFlag(NF | HC); + adjustXY(operand); +} + +void Z80::rrc(uint8_t& operand) { + auto carry = operand & Bit0; + operand >>= 1; + carry ? operand |= Bit7 : operand &= ~Bit7; + setFlag(CF, carry); + clearFlag(NF | HC); + adjustXY(operand); +} + +void Z80::rl(uint8_t& operand) { + auto oldCarry = F() & CF; + auto newCarry = operand & Bit7; + operand <<= 1; + oldCarry ? operand |= Bit0 : operand &= ~Bit0; + setFlag(CF, newCarry); + clearFlag(NF | HC); + adjustXY(operand); +} + +void Z80::rr(uint8_t& operand) { + auto oldCarry = F() & CF; + auto newCarry = operand & Bit0; + operand >>= 1; + operand |= oldCarry << 7; + setFlag(CF, newCarry); + clearFlag(NF | HC); + adjustXY(operand); +} + +// + +void Z80::sla(uint8_t& operand) { + auto newCarry = operand & Bit7; + operand <<= 1; + setFlag(CF, newCarry); + clearFlag(NF | HC); + adjustXY(operand); +} + +void Z80::sra(uint8_t& operand) { + auto new7 = operand & Bit7; + auto newCarry = operand & Bit0; + operand >>= 1; + operand |= new7; + setFlag(CF, newCarry); + clearFlag(NF | HC); + adjustXY(operand); +} + +void Z80::sll(uint8_t& operand) { + auto newCarry = operand & Bit7; + operand <<= 1; + operand |= 1; + setFlag(CF, newCarry); + clearFlag(NF | HC); + adjustXY(operand); +} + +void Z80::srl(uint8_t& operand) { + auto newCarry = operand & Bit0; + operand >>= 1; + operand &= ~Bit7; // clear bit 7 + setFlag(CF, newCarry); + clearFlag(NF | HC); + adjustXY(operand); + setFlag(ZF, operand); +} + +// + +void Z80::rlca() { + rlc(A()); +} + +void Z80::rrca() { + rrc(A()); +} + +void Z80::rla() { + rl(A()); +} + +void Z80::rra() { + rr(A()); +} + +#pragma endregion Shift and rotate + +#pragma region BIT/SET/RES + +void Z80::bit(int n, uint8_t& operand) { + auto carry = F() & CF; + uint8_t discarded = operand; + andr(discarded, 1 << n); + clearFlag(PF, discarded); + setFlag(CF, carry); +} + +void Z80::res(int n, uint8_t& operand) { + auto bit = 1 << n; + operand &= ~bit; +} + +void Z80::set(int n, uint8_t& operand) { + auto bit = 1 << n; + operand |= bit; +} + +#pragma endregion BIT/SET/RES + +#pragma region Miscellaneous instructions + +void Z80::neg() { + auto original = A(); + A() = 0; + sub(A(), original); + setFlag(PF, original == 0x80); + setFlag(CF, original); +} + +void Z80::daa() { + + uint8_t a = A(); + + auto lowAdjust = (F() & HC) | ((A() & 0xf) > 9); + auto highAdjust = (F() & CF) | (A() > 0x99); + + if (F() & NF) { + if (lowAdjust) + a -= 6; + if (highAdjust) + a -= 0x60; + } else { + if (lowAdjust) + a += 6; + if (highAdjust) + a += 0x60; + } + + F() = (F() & (CF | NF)) | (A() > 0x99) | ((A() ^ a) & HC); + + adjustSZPXY(a); + + A() = a; +} + +void Z80::cpl() { + A() = ~A(); + adjustXY(A()); + setFlag(HC | NF); +} + +void Z80::scf() { + setFlag(CF); + adjustXY(A()); + clearFlag(HC | NF); +} + +void Z80::ccf() { + auto carry = F() & CF; + setFlag(HC, carry); + clearFlag(CF, carry); + clearFlag(NF); + adjustXY(A()); +} + +void Z80::xhtl(register16_t& operand) { + m_memory.ADDRESS() = sp; + MEMPTR().low = m_memory.reference(); + m_memory.reference() = operand.low; + operand.low = MEMPTR().low; + m_memory.ADDRESS().word++; + MEMPTR().high = m_memory.reference(); + m_memory.reference() = operand.high; + operand.high = MEMPTR().high; +} + +void Z80::xhtl() { + if (m_prefixDD) + xhtl(IX()); + else if (m_prefixFD) + xhtl(IY()); + else + xhtl(HL()); +} + +#pragma endregion Miscellaneous instructions + +#pragma region Block instructions + +#pragma region Block compare instructions + +void Z80::blockCompare() { + + m_memory.ADDRESS() = HL(); + + auto value = m_memory.reference(); + uint8_t result = A() - value; + + setFlag(PF, --BC().word); + + adjustSZ(result); + adjustHalfCarrySub(A(), value, result); + setFlag(NF); + + if (F() & HC) + result -= 1; + + setFlag(YF, result & Bit1); + setFlag(XF, result & Bit3); +} + +void Z80::cpi() { + blockCompare(); + HL().word++; + MEMPTR().word++; +} + +void Z80::cpd() { + blockCompare(); + HL().word--; + MEMPTR().word--; +} + +void Z80::cpir() { + cpi(); + if ((F() & PF) && !(F() & ZF)) { // See CPI + cycles += 5; + pc.word -= 2; + MEMPTR().word = pc.word + 1; + } else { + MEMPTR().word = pc.word; + } +} + +void Z80::cpdr() { + cpd(); + if ((F() & PF) && !(F() & ZF)) { // See CPD + cycles += 5; + pc.word -= 2; + MEMPTR().word = pc.word + 1; + } else { + MEMPTR().word = pc.word - 2; + } +} + +#pragma endregion Block compare instructions + +#pragma region Block load instructions + +void Z80::blockLoad(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; + setFlag(XF, xy & 8); + setFlag(YF, xy & 2); + clearFlag(NF | HC); + setFlag(PF, --BC().word); +} + +void Z80::ldd() { + blockLoad(HL(), DE()); + HL().word--; + DE().word--; +} + +void Z80::ldi() { + blockLoad(HL(), DE()); + HL().word++; + DE().word++; +} + +void Z80::ldir() { + ldi(); + if (F() & PF) { // See LDI + cycles += 5; + pc.word -= 2; + MEMPTR().word = pc.word + 1; + } +} + +void Z80::lddr() { + ldd(); + if (F() & PF) { // See LDR + cycles += 5; + pc.word -= 2; + MEMPTR().word = pc.word + 1; + } +} + +#pragma endregion Block load instructions + +#pragma region Block input instructions + +void Z80::ini() { + auto bc = BC().word; + m_memory.ADDRESS().word = bc; + readPort(); + auto value = m_memory.DATA(); + m_memory.ADDRESS().word = HL().word++; + m_memory.reference() = value; + postDecrement(--B()); + setFlag(NF); + MEMPTR().word = ++bc; +} + +void Z80::ind() { + auto bc = BC().word; + m_memory.ADDRESS().word = bc; + readPort(); + auto value = m_memory.DATA(); + m_memory.ADDRESS().word = HL().word--; + m_memory.reference() = value; + postDecrement(--B()); + setFlag(NF); + MEMPTR().word = --bc; +} + +void Z80::inir() { + ini(); + if (!(F() & ZF)) { // See INI + cycles += 5; + pc.word -= 2; + } +} + +void Z80::indr() { + ind(); + if (!(F() & ZF)) { // See IND + cycles += 5; + pc.word -= 2; + } +} + +#pragma endregion Block input instructions + +#pragma region Block output instructions + +void Z80::blockOut() { + auto value = m_memory.reference(); + m_memory.ADDRESS().word = BC().word; + writePort(); + postDecrement(--B()); + setFlag(NF, value & Bit7); + setFlag(HC | CF, (L() + value) > 0xff); + adjustParity(((value + L()) & 7) ^ B()); +} + +void Z80::outi() { + m_memory.ADDRESS().word = HL().word++; + blockOut(); + MEMPTR().word = BC().word + 1; +} + +void Z80::outd() { + m_memory.ADDRESS().word = HL().word--; + blockOut(); + MEMPTR().word = BC().word - 1; +} + +void Z80::otir() { + outi(); + if (!(F() & ZF)) { // See OUTI + cycles += 5; + pc.word -= 2; + } +} + +void Z80::otdr() { + outd(); + if (!(F() & ZF)) { // See OUTD + cycles += 5; + pc.word -= 2; + } +} + +#pragma endregion Block output instructions + +#pragma endregion Block instructions + +#pragma region Nibble rotation + +void Z80::rrd() { + auto accumulator = A(); + m_memory.ADDRESS() = HL(); + auto memory = m_memory.reference(); + A() = (accumulator & 0xf0) | lowNibble(memory); + uint8_t updated = promoteNibble(lowNibble(accumulator)) | highNibble(memory); + m_memory.reference() = updated; + adjustSZPXY(A()); + clearFlag(NF | HC); + MEMPTR().word = HL().word + 1; +} + +void Z80::rld() { + auto accumulator = A(); + m_memory.ADDRESS() = HL(); + auto memory = m_memory.reference(); + uint8_t updated = lowNibble(accumulator) | promoteNibble(memory); + A() = (accumulator & 0xf0) | highNibble(memory); + m_memory.reference() = updated; + adjustSZPXY(A()); + clearFlag(NF | HC); + MEMPTR().word = HL().word + 1; +} + +#pragma endregion Nibble rotation + +int Z80::step() { + ExecutingInstruction.fire(*this); + m_prefixCB = m_prefixDD = m_prefixED = m_prefixFD = false; + cycles = 0; + return fetchExecute(); +} + +int Z80::execute(uint8_t opcode) { + + if (!getM1()) + 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_prefixDD || m_prefixFD))) { + incrementRefresh(); + M1() = false; + } + + if (m_prefixCB) + executeCB(x, y, z, p, q); + else if (m_prefixED) + executeED(x, y, z, p, q); + else + executeOther(x, y, z, p, q); + + if (cycles == 0) + throw std::logic_error("Unhandled opcode"); + + return cycles; +} + +void Z80::executeCB(int x, int y, int z, int p, int q) { + switch (x) { + case 0: // rot[y] r[z] + switch (y) { + case 0: + if (m_prefixDD || m_prefixFD) { + rlc(DISPLACED()); + R2(z) = DISPLACED(); + } else { + rlc(R(z)); + } + break; + case 1: + if (m_prefixDD || m_prefixFD) { + rrc(DISPLACED()); + R2(z) = DISPLACED(); + } else { + rrc(R(z)); + } + break; + case 2: + if (m_prefixDD || m_prefixFD) { + rl(DISPLACED()); + R2(z) = DISPLACED(); + } else { + rl(R(z)); + } + break; + case 3: + if (m_prefixDD || m_prefixFD) { + rr(DISPLACED()); + R2(z) = DISPLACED(); + } else { + rr(R(z)); + } + break; + case 4: + if (m_prefixDD || m_prefixFD) { + sla(DISPLACED()); + R2(z) = DISPLACED(); + } else { + sla(R(z)); + } + break; + case 5: + if (m_prefixDD || m_prefixFD) { + sra(DISPLACED()); + R2(z) = DISPLACED(); + } else { + sra(R(z)); + } + break; + case 6: + if (m_prefixDD || m_prefixFD) { + sll(DISPLACED()); + R2(z) = DISPLACED(); + } else { + sll(R(z)); + } + break; + case 7: + if (m_prefixDD || m_prefixFD) { + srl(DISPLACED()); + R2(z) = DISPLACED(); + } else { + srl(R(z)); + } + break; + } + if (m_prefixDD || m_prefixFD) + adjustSZP(DISPLACED()); + else + adjustSZP(R(z)); + if (m_prefixDD || m_prefixFD) { + cycles += 23; + } else { + cycles += 8; + if (z == 6) + cycles += 7; + } + break; + case 1: // BIT y, r[z] + if (m_prefixDD || m_prefixFD) { + bit(y, DISPLACED()); + adjustXY(MEMPTR().high); + cycles += 20; + } else { + auto operand = R(z); + bit(y, operand); + cycles += 8; + if (z == 6) { + adjustXY(MEMPTR().high); + cycles += 4; + } else { + adjustXY(operand); + } + } + break; + case 2: // RES y, r[z] + if (m_prefixDD || m_prefixFD) { + res(y, DISPLACED()); + R2(z) = DISPLACED(); + cycles += 23; + } else { + res(y, R(z)); + cycles += 8; + if (z == 6) + cycles += 7; + } + break; + case 3: // SET y, r[z] + if (m_prefixDD || m_prefixFD) { + set(y, DISPLACED()); + R2(z) = DISPLACED(); + cycles += 23; + } else { + set(y, R(z)); + cycles += 8; + if (z == 6) + cycles += 7; + } + break; + } +} + +void Z80::executeED(int x, int y, int z, int p, int q) { + switch (x) { + case 0: + case 3: // Invalid instruction, equivalent to NONI followed by NOP + cycles += 8; + break; + case 1: + switch (z) { + case 0: // Input from port with 16-bit address + MEMPTR() = m_memory.ADDRESS() = BC(); + readPort(); + if (y != 6) // IN r[y],(C) + R(y) = m_memory.DATA(); + adjustSZPXY(m_memory.DATA()); + clearFlag(HC | NF); + MEMPTR().word++; + cycles += 12; + break; + case 1: // Output to port with 16-bit address + MEMPTR() = m_memory.ADDRESS() = BC(); + if (y == 6) // OUT (C),0 + m_memory.placeDATA(0); + else // OUT (C),r[y] + m_memory.placeDATA(R(y)); + writePort(); + MEMPTR().word++; + cycles += 12; + break; + case 2: // 16-bit add/subtract with carry + switch (q) { + case 0: // SBC HL, rp[p] + sbcViaMemptr(ALT_HL(), RP(p)); + break; + case 1: // ADC HL, rp[p] + adcViaMemptr(ALT_HL(), RP(p)); + break; + } + cycles += 15; + break; + case 3: // Retrieve/store register pair from/to immediate address + switch (q) { + case 0: // LD (nn), rp[p] + setWordViaMemptr(fetchWord(), RP(p)); + break; + case 1: // LD rp[p], (nn) + RP(p) = getWordViaMemptr(fetchWord()); + break; + } + cycles += 20; + break; + case 4: // Negate accumulator + neg(); + cycles += 8; + break; + case 5: // Return from interrupt + switch (y) { + case 1: + reti(); // RETI + break; + default: + retn(); // RETN + break; + } + cycles += 14; + break; + case 6: // Set interrupt mode + switch (y) { + case 0: + case 4: + IM() = 0; + break; + case 2: + case 6: + IM() = 1; + break; + case 3: + case 7: + IM() = 2; + break; + case 1: + case 5: + IM() = 0; + } + cycles += 8; + break; + case 7: // Assorted ops + switch (y) { + case 0: // LD I,A + IV() = A(); + cycles += 9; + break; + case 1: // LD R,A + REFRESH() = A(); + cycles += 9; + break; + case 2: // LD A,I + A() = IV(); + adjustSZXY(A()); + setFlag(PF, IFF2()); + clearFlag(NF | HC); + cycles += 9; + break; + case 3: // LD A,R + A() = REFRESH(); + adjustSZXY(A()); + clearFlag(HC | NF); + setFlag(PF, IFF2()); + cycles += 9; + break; + case 4: // RRD + rrd(); + cycles += 18; + break; + case 5: // RLD + rld(); + cycles += 18; + break; + case 6: // NOP + case 7: // NOP + cycles += 4; + break; + } + break; + } + break; + case 2: + switch (z) { + case 0: // LD + switch (y) { + case 4: // LDI + ldi(); + break; + case 5: // LDD + ldd(); + break; + case 6: // LDIR + ldir(); + break; + case 7: // LDDR + lddr(); + break; + } + break; + case 1: // CP + switch (y) { + case 4: // CPI + cpi(); + break; + case 5: // CPD + cpd(); + break; + case 6: // CPIR + cpir(); + break; + case 7: // CPDR + cpdr(); + break; + } + break; + case 2: // IN + switch (y) { + case 4: // INI + ini(); + break; + case 5: // IND + ind(); + break; + case 6: // INIR + inir(); + break; + case 7: // INDR + indr(); + break; + } + break; + case 3: // OUT + switch (y) { + case 4: // OUTI + outi(); + break; + case 5: // OUTD + outd(); + break; + case 6: // OTIR + otir(); + break; + case 7: // OTDR + otdr(); + break; + } + break; + } + cycles += 16; + break; + } +} + +void Z80::executeOther(int x, int y, int z, int p, int q) { + switch (x) { + case 0: + switch (z) { + case 0: // Relative jumps and assorted ops + switch (y) { + case 0: // NOP + cycles += 4; + break; + case 1: // EX AF AF' + exxAF(); + cycles += 4; + break; + case 2: // DJNZ d + jrConditional(--B()); + cycles += 8; + break; + case 3: // JR d + jrConditional(true); + cycles += 7; + break; + default: // JR cc,d + jrConditionalFlag(y - 4); + cycles += 5; + break; + } + break; + case 1: // 16-bit load immediate/add + switch (q) { + case 0: // LD rp,nn + RP(p) = fetchWord(); + cycles += 10; + break; + case 1: // ADD HL,rp + addViaMemptr(ALT_HL(), RP(p)); + cycles += 11; + break; + } + break; + case 2: // Indirect loading + switch (q) { + case 0: + switch (p) { + case 0: // LD (BC),A + setViaMemptr(BC(), A()); + cycles += 7; + break; + case 1: // LD (DE),A + setViaMemptr(DE(), A()); + cycles += 7; + break; + case 2: // LD (nn),HL + setWordViaMemptr(fetchWord(), ALT_HL()); + cycles += 16; + break; + case 3: // LD (nn),A + setViaMemptr(fetchWord(), A()); + cycles += 13; + break; + } + break; + case 1: + switch (p) { + case 0: // LD A,(BC) + A() = getViaMemptr(BC()); + cycles += 7; + break; + case 1: // LD A,(DE) + A() = getViaMemptr(DE()); + cycles += 7; + break; + case 2: // LD HL,(nn) + ALT_HL() = getWordViaMemptr(fetchWord()); + cycles += 16; + break; + case 3: // LD A,(nn) + A() = getViaMemptr(fetchWord()); + cycles += 13; + break; + } + break; + } + break; + case 3: // 16-bit INC/DEC + switch (q) { + case 0: // INC rp + ++RP(p).word; + break; + case 1: // DEC rp + --RP(p).word; + break; + } + cycles += 6; + break; + case 4: // 8-bit INC + postIncrement(++R(y)); // INC r + cycles += 4; + break; + case 5: // 8-bit DEC + postDecrement(--R(y)); // DEC r + cycles += 4; + if (y == 6) + cycles += 7; + break; + case 6: { // 8-bit load immediate + R(y) = fetchByteData(); // LD r,n + cycles += 7; + if (y == 6) + cycles += 3; + break; + } case 7: // Assorted operations on accumulator/flags + switch (y) { + case 0: + rlca(); + break; + case 1: + rrca(); + break; + case 2: + rla(); + break; + case 3: + rra(); + break; + case 4: + daa(); + break; + case 5: + cpl(); + break; + case 6: + scf(); + break; + case 7: + ccf(); + break; + } + cycles += 4; + break; + } + break; + case 1: // 8-bit loading + if (z == 6 && y == 6) { // Exception (replaces LD (HL), (HL)) + halt(); + } else { + bool normal = true; + if (m_prefixDD || m_prefixFD) { + if (z == 6) { + switch (y) { + case 4: + H() = R(z); + normal = false; + break; + case 5: + L() = R(z); + normal = false; + break; + } + } + if (y == 6) { + switch (z) { + case 4: + R(y) = H(); + normal = false; + break; + case 5: + R(y) = L(); + normal = false; + break; + } + } + } + if (normal) + R(y) = R(z); + if ((y == 6) || (z == 6)) // M operations + cycles += 3; + } + cycles += 4; + break; + case 2: // Operate on accumulator and register/memory location + switch (y) { + case 0: // ADD A,r + add(A(), R(z)); + break; + case 1: // ADC A,r + adc(A(), R(z)); + break; + case 2: // SUB r + sub(A(), R(z)); + break; + case 3: // SBC A,r + sbc(A(), R(z)); + break; + case 4: // AND r + andr(A(), R(z)); + break; + case 5: // XOR r + xorr(A(), R(z)); + break; + case 6: // OR r + orr(A(), R(z)); + break; + case 7: // CP r + compare(R(z)); + break; + } + cycles += 4; + if (z == 6) + cycles += 3; + break; + case 3: + switch (z) { + case 0: // Conditional return + returnConditionalFlag(y); + cycles += 5; + break; + case 1: // POP & various ops + switch (q) { + case 0: // POP rp2[p] + RP2(p) = popWord(); + cycles += 10; + break; + case 1: + switch (p) { + case 0: // RET + ret(); + cycles += 10; + break; + case 1: // EXX + exx(); + cycles += 4; + break; + case 2: // JP HL + pc = ALT_HL(); + cycles += 4; + break; + case 3: // LD SP,HL + sp = ALT_HL(); + cycles += 4; + break; + } + } + break; + case 2: // Conditional jump + jumpConditionalFlag(y); + cycles += 10; + break; + case 3: // Assorted operations + switch (y) { + case 0: // JP nn + setPcViaMemptr(fetchWord()); + cycles += 10; + break; + case 1: // CB prefix + m_prefixCB = true; + if (m_prefixDD || m_prefixFD) + m_displacement = fetchByteData(); + fetchExecute(); + break; + case 2: // OUT (n),A + m_memory.ADDRESS().low = fetchByteData(); + m_memory.ADDRESS().high = A(); + MEMPTR() = m_memory.ADDRESS(); + m_memory.placeDATA(A()); + writePort(); + MEMPTR().low++; + cycles += 11; + break; + case 3: // IN A,(n) + m_memory.ADDRESS().low = fetchByteData(); + m_memory.ADDRESS().high = A(); + MEMPTR() = m_memory.ADDRESS(); + readPort(); + A() = m_memory.DATA(); + MEMPTR().low++; + cycles += 11; + break; + case 4: // EX (SP),HL + xhtl(); + cycles += 19; + break; + case 5: // EX DE,HL + std::swap(DE(), HL()); + cycles += 4; + break; + case 6: // DI + disableInterrupts(); + cycles += 4; + break; + case 7: // EI + enableInterrupts(); + cycles += 4; + break; + } + break; + case 4: // Conditional call: CALL cc[y], nn + callConditionalFlag(fetchWord(), y); + cycles += 10; + break; + case 5: // PUSH & various ops + switch (q) { + case 0: // PUSH rp2[p] + pushWord(RP2(p)); + cycles += 11; + break; + case 1: + switch (p) { + case 0: // CALL nn + callConditional(fetchWord(), true); + cycles += 17; + break; + case 1: // DD prefix + m_prefixDD = true; + fetchExecute(); + break; + case 2: // ED prefix + m_prefixED = true; + fetchExecute(); + break; + case 3: // FD prefix + m_prefixFD = true; + fetchExecute(); + break; + } + } + break; + case 6: // Operate on accumulator and immediate operand: alu[y] n + switch (y) { + case 0: // ADD A,n + add(A(), fetchByteData()); + break; + case 1: // ADC A,n + adc(A(), fetchByteData()); + break; + case 2: // SUB n + sub(A(), fetchByteData()); + break; + case 3: // SBC A,n + sbc(A(), fetchByteData()); + break; + case 4: // AND n + andr(A(), fetchByteData()); + break; + case 5: // XOR n + xorr(A(), fetchByteData()); + break; + case 6: // OR n + orr(A(), fetchByteData()); + break; + case 7: // CP n + compare(fetchByteData()); + break; + } + cycles += 7; + break; + case 7: // Restart: RST y * 8 + restart(y << 3); + cycles += 11; + break; + } + break; + } +} \ No newline at end of file diff --git a/inc/EventArgs.h b/inc/EventArgs.h new file mode 100644 index 0000000..0f20f17 --- /dev/null +++ b/inc/EventArgs.h @@ -0,0 +1,13 @@ +#pragma once + +namespace EightBit { + class EventArgs { + private: + static EventArgs m_empty; + + public: + static EventArgs& empty() { return m_empty; } + + EventArgs() {} + }; +} \ No newline at end of file diff --git a/inc/Memory.h b/inc/Memory.h new file mode 100644 index 0000000..3a2bd83 --- /dev/null +++ b/inc/Memory.h @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include + +namespace EightBit { + + typedef union { + struct { +#ifdef HOST_LITTLE_ENDIAN + uint8_t low; + uint8_t high; +#endif +#ifdef HOST_BIG_ENDIAN + uint8_t high; + uint8_t low; +#endif + }; + uint16_t word; + } register16_t; + + class Memory { + public: + Memory(uint16_t addressMask); + + virtual uint16_t& ADDRESS() { return m_address; } + virtual uint8_t& DATA() { return *m_data; } + + virtual uint8_t& placeDATA(uint8_t value) { + m_temporary = value; + m_data = &m_temporary; + return DATA(); + } + + virtual uint8_t& referenceDATA(uint8_t& value) { + m_data = &value; + return DATA(); + } + + virtual uint8_t peek(uint16_t address) const; + virtual uint16_t peekWord(uint16_t address) const; + + virtual uint8_t get(uint16_t address) { + ADDRESS() = address; + return reference(); + } + + virtual register16_t getWord(uint16_t address); + + virtual void set(uint16_t address, uint8_t value) { + ADDRESS() = address; + reference() = value; + } + + virtual void setWord(uint16_t address, register16_t value); + + virtual uint8_t& reference() { + uint16_t effective = ADDRESS() & m_addressMask; + return m_locked[effective] ? placeDATA(m_bus[effective]) : referenceDATA(m_bus[effective]); + } + + void clear(); + void loadRom(const std::string& path, uint16_t offset); + void loadRam(const std::string& path, uint16_t offset); + + protected: + std::array m_bus; + std::array m_locked; + + uint16_t m_addressMask; // Mirror + uint8_t m_temporary; // Used to simulate ROM + uint16_t m_address; + uint8_t* m_data; + + int loadMemory(const std::string& path, uint16_t offset); + }; +} \ No newline at end of file diff --git a/inc/Processor.h b/inc/Processor.h new file mode 100644 index 0000000..ae1fcca --- /dev/null +++ b/inc/Processor.h @@ -0,0 +1,102 @@ +#pragma once + +#include +#include +#include + +#include "Memory.h" +#include "Signal.h" + +namespace EightBit { + class Processor { + public: + enum Masks { + Mask1 = 0x01, + Mask2 = 0x03, + Mask3 = 0x07, + Mask4 = 0x0f, + Mask5 = 0x1f, + Mask6 = 0x3f, + Mask7 = 0x7f, + Mask8 = 0xff, + }; + + enum Bits { + Bit16 = 0x10000, + Bit15 = 0x8000, + Bit14 = 0x4000, + Bit13 = 0x2000, + Bit12 = 0x1000, + Bit11 = 0x800, + Bit10 = 0x400, + Bit9 = 0x200, + Bit8 = 0x100, + Bit7 = 0x80, + Bit6 = 0x40, + Bit5 = 0x20, + Bit4 = 0x10, + Bit3 = 0x8, + Bit2 = 0x4, + Bit1 = 0x2, + Bit0 = 0x1, + }; + + static uint8_t highNibble(uint8_t value) { return value >> 4; } + static uint8_t lowNibble(uint8_t 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 uint16_t makeWord(uint8_t low, uint8_t high) { + return (high << 8) | low; + } + + Processor(Memory& memory); + + const Memory& getMemory() const { return m_memory; } + + register16_t getProgramCounter() const { return pc; } + void setProgramCounter(register16_t value) { pc = value; } + + register16_t getStackPointer() const { return sp; } + void setStackPointer(register16_t value) { sp = value; } + + bool isHalted() const { return m_halted; } + void halt() { --pc.word; m_halted = true; } + + virtual void initialise(); + + void reset(); + + virtual register16_t getWord(uint16_t address) const { + return m_memory.getWord(address); + } + + virtual void setWord(uint16_t address, register16_t value) { + m_memory.setWord(address, value); + } + + protected: + Memory& m_memory; + + int cycles; + + register16_t pc; + register16_t sp; + + bool m_halted; + + void pushWord(register16_t value); + register16_t popWord(); + + uint8_t fetchByte() { + return m_memory.get(pc.word++); + } + + uint16_t fetchWord() { + auto value = getWord(pc.word); + pc.word += 2; + return value.word; + } + }; +} \ No newline at end of file diff --git a/inc/Signal.h b/inc/Signal.h new file mode 100644 index 0000000..83445ef --- /dev/null +++ b/inc/Signal.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +namespace EightBit { + template class Signal { + private: + typedef std::function delegate_t; + typedef std::vector delegates_t; + + delegates_t delegates; + + public: + void connect(delegate_t functor) { + delegates.push_back(functor); + } + + void fire(const T& e) const { + if (!delegates.empty()) + for (auto& delegate : delegates) + delegate(e); + } + }; +} \ No newline at end of file diff --git a/src/EightBit.vcxproj b/src/EightBit.vcxproj new file mode 100644 index 0000000..39b608d --- /dev/null +++ b/src/EightBit.vcxproj @@ -0,0 +1,158 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {A9C24BD9-0CB4-4C84-B09B-46B815F9DA47} + Win32Proj + EightBit + 10.0.14393.0 + + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + ../inc;$(VC_IncludePath);$(WindowsSDK_IncludePath) + + + ../inc;$(VC_IncludePath);$(WindowsSDK_IncludePath) + + + ../inc;$(VC_IncludePath);$(WindowsSDK_IncludePath) + + + ../inc;$(VC_IncludePath);$(WindowsSDK_IncludePath) + + + + 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 + + + + + Level3 + Use + MaxSpeed + true + true + NDEBUG;_LIB;%(PreprocessorDefinitions) + + + Windows + true + true + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + \ No newline at end of file diff --git a/src/EightBit.vcxproj.filters b/src/EightBit.vcxproj.filters new file mode 100644 index 0000000..5103cb4 --- /dev/null +++ b/src/EightBit.vcxproj.filters @@ -0,0 +1,44 @@ + + + + + {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 + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/src/EventArgs.cpp b/src/EventArgs.cpp new file mode 100644 index 0000000..aeecdba --- /dev/null +++ b/src/EventArgs.cpp @@ -0,0 +1,4 @@ +#include "stdafx.h" +#include "EventArgs.h" + +EightBit::EventArgs EightBit::EventArgs::m_empty; \ No newline at end of file diff --git a/src/Memory.cpp b/src/Memory.cpp new file mode 100644 index 0000000..0490f9f --- /dev/null +++ b/src/Memory.cpp @@ -0,0 +1,68 @@ +#include "stdafx.h" +#include "Memory.h" +#include "Processor.h" + +#include +#include +#include + +EightBit::Memory::Memory(uint16_t addressMask) +: m_address(0), + m_addressMask(addressMask), + m_data(&(m_bus[m_address])) {} + +uint8_t EightBit::Memory::peek(uint16_t address) const { + return m_bus[address]; +} + +uint16_t EightBit::Memory::peekWord(uint16_t address) const { + auto low = peek(address); + auto high = peek(address + 1); + return Processor::makeWord(low, high); +} + +EightBit::register16_t EightBit::Memory::getWord(uint16_t address) { + register16_t returned; + returned.low = get(address); + returned.high = get(address + 1); + return returned; +} + +void EightBit::Memory::setWord(uint16_t address, register16_t value) { + set(address, value.low); + set(address + 1, value.high); +} + +void EightBit::Memory::clear() { + m_bus.fill(0); + m_locked.fill(false); +} + +void EightBit::Memory::loadRom(const std::string& path, uint16_t offset) { + auto size = loadMemory(path, offset); + std::fill(m_locked.begin() + offset, m_locked.begin() + offset + size, true); +} + +void EightBit::Memory::loadRam(const std::string& path, uint16_t offset) { + loadMemory(path, offset); +} + +int EightBit::Memory::loadMemory(const std::string& path, uint16_t offset) { + std::ifstream file; + file.exceptions(std::ios::failbit | std::ios::badbit); + + file.open(path, std::ios::binary | std::ios::ate); + auto size = (int)file.tellg(); + + size_t extent = size + offset; + + if (extent > m_bus.size()) + throw std::runtime_error("ROM cannot fit"); + + file.seekg(0, std::ios::beg); + + file.read((char*)&m_bus[offset], size); + file.close(); + + return size; +} diff --git a/src/Processor.cpp b/src/Processor.cpp new file mode 100644 index 0000000..ea14108 --- /dev/null +++ b/src/Processor.cpp @@ -0,0 +1,29 @@ +#include "stdafx.h" +#include "Processor.h" + +EightBit::Processor::Processor(Memory& memory) +: m_memory(memory), + cycles(0), + m_halted(false) { + pc.word = sp.word = 0; +} + +void EightBit::Processor::reset() { + pc.word = 0; +} + +void EightBit::Processor::initialise() { + sp.word = 0; + reset(); +} + +void EightBit::Processor::pushWord(register16_t value) { + sp.word -= 2; + setWord(sp.word, value); +} + +EightBit::register16_t EightBit::Processor::popWord() { + auto value = getWord(sp.word); + sp.word += 2; + return value; +} diff --git a/src/stdafx.cpp b/src/stdafx.cpp new file mode 100644 index 0000000..fd4f341 --- /dev/null +++ b/src/stdafx.cpp @@ -0,0 +1 @@ +#include "stdafx.h" diff --git a/src/stdafx.h b/src/stdafx.h new file mode 100644 index 0000000..d3ffbf9 --- /dev/null +++ b/src/stdafx.h @@ -0,0 +1,19 @@ +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include + +#include +#include + +#include +#include +#include + +#if defined(_M_X64) || defined(_M_IX86 ) +# define HOST_LITTLE_ENDIAN +#else +# define HOST_BIG_ENDIAN +#endif