Dump of all my C++ emulators, only Intel8080 integrated so far...

Signed-off-by: Adrian.Conlon <adrian.conlon@gmail.com>
This commit is contained in:
Adrian.Conlon 2017-06-04 21:38:34 +01:00
commit 105032f08a
67 changed files with 10430 additions and 0 deletions

51
.gitignore vendored Normal file
View File

@ -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

38
EightBit.sln Normal file
View File

@ -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

View File

@ -0,0 +1,21 @@
#pragma once
#include <string>
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);
};
}

View File

@ -0,0 +1,38 @@
#pragma once
#include <cstdint>
#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<PortEventArgs> ReadingPort;
Signal<PortEventArgs> ReadPort;
Signal<PortEventArgs> WritingPort;
Signal<PortEventArgs> 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<uint8_t, 0x100> input;
std::array<uint8_t, 0x100> output;
};
}

708
Intel8080/inc/Intel8080.h Normal file
View File

@ -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<void()> 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<Intel8080> ExecutingInstruction;
const std::array<Instruction, 0x100>& 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<Instruction, 0x100> instructions;
std::array<bool, 8> m_halfCarryTableAdd = { { false, false, true, false, true, false, true, true } };
std::array<bool, 8> 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; }
};
}

View File

@ -0,0 +1,18 @@
#pragma once
#include <cstdint>
namespace EightBit {
class PortEventArgs {
public:
PortEventArgs(uint8_t port)
: m_port(port) {}
uint8_t getPort() const {
return m_port;
}
private:
uint8_t m_port;
};
}

24
Intel8080/inc/Profiler.h Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include <array>
#include <cstdint>
namespace EightBit {
class Profiler {
public:
Profiler();
~Profiler();
void addInstruction(uint8_t instruction);
void addAddress(uint16_t address);
void dump() const;
private:
std::array<uint64_t, 0x100> m_instructions;
std::array<uint64_t, 0x10000> m_addresses;
void dumpInstructionProfiles() const;
void dumpAddressProfiles() const;
};
}

View File

@ -0,0 +1,72 @@
#pragma once
#include <string>
#include <cstdint>
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;
}
};
}

View File

@ -0,0 +1,112 @@
#include "stdafx.h"
#include "Disassembler.h"
#include <sstream>
#include <iomanip>
#include <bitset>
#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();
}

View File

@ -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));
}

View File

@ -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);
}

View File

@ -0,0 +1,162 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{93BDC8D8-9F0D-44ED-94FB-1BE6AC4B6BD6}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>Win32Project1</RootNamespace>
<WindowsTargetPlatformVersion>10.0.14393.0</WindowsTargetPlatformVersion>
<ProjectName>Intel8080</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<IncludePath>../inc;../../inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<IncludePath>../inc;../../inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IncludePath>../inc;../../inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IncludePath>../inc;../../inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>Use</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>Use</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="..\inc\Disassembler.h" />
<ClInclude Include="..\inc\InputOutput.h" />
<ClInclude Include="..\inc\Intel8080.h" />
<ClInclude Include="..\inc\PortEventArgs.h" />
<ClInclude Include="..\inc\Profiler.h" />
<ClInclude Include="..\inc\StatusFlags.h" />
<ClInclude Include="stdafx.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Disassembler.cpp" />
<ClCompile Include="InputOutput.cpp" />
<ClCompile Include="Intel8080.cpp" />
<ClCompile Include="Profiler.cpp" />
<ClCompile Include="stdafx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\inc\Disassembler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\inc\InputOutput.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\inc\Intel8080.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\inc\PortEventArgs.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\inc\Profiler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\inc\StatusFlags.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Disassembler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="InputOutput.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Intel8080.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Profiler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -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;
}
}

1
Intel8080/src/stdafx.cpp Normal file
View File

@ -0,0 +1 @@
#include "stdafx.h"

19
Intel8080/src/stdafx.h Normal file
View File

@ -0,0 +1,19 @@
#ifdef _MSC_VER
#pragma once
#endif
#include <cstdint>
#include <functional>
#include <iostream>
#include <fstream>
#include <string>
#include <array>
#include <vector>
#if defined(_M_X64) || defined(_M_IX86 )
# define HOST_LITTLE_ENDIAN
#else
# define HOST_BIG_ENDIAN
#endif

339
LICENSE Normal file
View File

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
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.

View File

@ -0,0 +1,53 @@
#pragma once
#include <string>
#include <boost/format.hpp>
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);
};

222
LR35902/inc/LR35902.h Normal file
View File

@ -0,0 +1,222 @@
#pragma once
#include <cstdint>
#include "Processor.h"
class LR35902 : public Processor {
public:
enum StatusBits {
ZF = Bit7,
NF = Bit6,
HC = Bit5,
CF = Bit4,
};
LR35902(Bus& memory);
Signal<LR35902> 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<register16_t, 3> m_registers;
register16_t m_accumulatorFlag;
bool m_ime;
bool m_prefixCB;
bool m_stopped;
std::array<bool, 8> m_halfCarryTableAdd = { { false, false, true, false, true, false, true, true } };
std::array<bool, 8> 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);
};

28
LR35902/inc/Profiler.h Normal file
View File

@ -0,0 +1,28 @@
#pragma once
#include <array>
#include <cstdint>
#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<uint64_t, 0x100> m_instructions;
std::array<uint64_t, 0x10000> m_addresses;
LR35902& m_cpu;
Disassembler m_disassembler;
void dumpInstructionProfiles() const;
void dumpAddressProfiles() const;
};

View File

@ -0,0 +1,555 @@
#include "stdafx.h"
#include "Disassembler.h"
#include <sstream>
#include <iomanip>
#include <bitset>
#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();
}

912
LR35902/src/LR35902.cpp Normal file
View File

@ -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;
}
}

43
LR35902/src/Profiler.cpp Normal file
View File

@ -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;
}
}

View File

@ -0,0 +1,17 @@
#pragma once
#include <cstdint>
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; }
};

View File

@ -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
};

View File

@ -0,0 +1,11 @@
#pragma once
#include <cstdint>
#include <functional>
typedef std::function<std::string(uint16_t current)> dumper_t;
struct AddressingModeDumper {
dumper_t byteDumper;
dumper_t disassemblyDumper;
};

64
M6502/inc/Disassembly.h Normal file
View File

@ -0,0 +1,64 @@
#pragma once
#include <map>
#include <cstdint>
#include <mos6502.h>
#include <AddressingMode.h>
#include "Symbols.h"
#include "AddressingModeDumper.h"
class Disassembly
{
public:
MOS6502& processor;
const Symbols& symbols;
std::map<AddressingMode, AddressingModeDumper> 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;
}
};

View File

@ -0,0 +1,15 @@
#pragma once
#include <string>
class DisassemblyEventArgs
{
private:
std::string m_output;
public:
DisassemblyEventArgs(std::string output)
: m_output(output) {}
const std::string& getOutput() const { return m_output; }
};

76
M6502/inc/Memory.h Normal file
View File

@ -0,0 +1,76 @@
#pragma once
#include <vector>
#include <iostream>
#include <fstream>
#include <algorithm>
#include "Signal.h"
#include "AddressEventArgs.h"
class Memory
{
private:
std::vector<uint8_t> memory;
std::vector<bool> locked;
public:
Memory(unsigned memorySize)
: memory(memorySize),
locked(memorySize) {}
Signal<AddressEventArgs> InvalidWriteAttempt;
Signal<AddressEventArgs> WritingByte;
Signal<AddressEventArgs> 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<char> buffer(size);
file.read(&buffer[0], size);
file.close();
std::copy(buffer.begin(), buffer.end(), memory.begin() + offset);
return (uint16_t)size;
}
};

View File

@ -0,0 +1,7 @@
#pragma once
enum ProcessorType {
Cpu6502,
Cpu65SC02,
Cpu65C02
};

View File

@ -0,0 +1,15 @@
#pragma once
#include <string>
class ProfileEventArgs
{
private:
std::string m_output;
public:
ProfileEventArgs(std::string output)
: m_output(output) {}
const std::string& getOutput() const { return m_output; }
};

View File

@ -0,0 +1,18 @@
#pragma once
#include <string>
#include <cstdint>
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; }
};

View File

@ -0,0 +1,21 @@
#pragma once
#include <string>
#include <cstdint>
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; }
};

62
M6502/inc/Profiler.h Normal file
View File

@ -0,0 +1,62 @@
#pragma once
#include <array>
#include <map>
#include <cstdint>
#include <functional>
#include <system6502.h>
#include <EventArgs.h>
#include <Signal.h>
#include "Disassembly.h"
#include "Symbols.h"
#include "ProfileLineEventArgs.h"
#include "ProfileScopeEventArgs.h"
class Profiler
{
public:
std::array<uint64_t, 0x100> instructionCounts;
std::array<uint64_t, 0x10000> addressProfiles;
std::array<uint64_t, 0x10000> addressCounts;
std::array<std::string, 0x10000> addressScopes;
std::map<std::string, uint64_t> 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<EventArgs> StartingOutput;
Signal<EventArgs> FinishedOutput;
Signal<EventArgs> StartingLineOutput;
Signal<EventArgs> FinishedLineOutput;
Signal<ProfileLineEventArgs> EmitLine;
Signal<EventArgs> StartingScopeOutput;
Signal<EventArgs> FinishedScopeOutput;
Signal<ProfileScopeEventArgs> 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();
};

82
M6502/inc/StatusFlags.h Normal file
View File

@ -0,0 +1,82 @@
#pragma once
#include <string>
#include <cstdint>
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;
}
};

36
M6502/inc/Symbols.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include <cstdint>
#include <map>
#include <vector>
#include <string>
class Symbols
{
public:
Symbols(std::string path);
const std::map<uint16_t, std::string>& getLabels() const { return labels; }
const std::map<uint16_t, std::string>& getConstants() const { return constants; }
const std::map<std::string, uint16_t>& getScopes() const { return scopes; }
const std::map<std::string, uint64_t>& getAddresses() const { return addresses; }
private:
void AssignScopes();
void AssignSymbols();
void Parse(std::string path);
std::map<uint16_t, std::string> labels;
std::map<uint16_t, std::string> constants;
std::map<std::string, uint16_t> scopes;
std::map<std::string, uint64_t> addresses;
struct kv_pair_t {
std::map<std::string, std::string> element;
};
std::vector<std::string> split(const std::string& input, const std::vector<std::string>& delimiters);
std::map<std::string, std::map<std::string, kv_pair_t>> parsed;
};

464
M6502/inc/mos6502.h Normal file
View File

@ -0,0 +1,464 @@
#pragma once
#include <cstdint>
#include <string>
#include <array>
#include <functional>
#include "ProcessorType.h"
#include "StatusFlags.h"
#include "AddressingMode.h"
class MOS6502 {
public:
typedef std::function<void()> 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<Instruction, 0x100> basis);
void OverlayInstructionSet(std::array<Instruction, 0x100> overlay);
void OverlayInstructionSet(std::array<Instruction, 0x100> 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<Instruction, 0x100> instructions;
ProcessorType level;
std::array<Instruction, 0x100> overlay6502;
std::array<Instruction, 0x100> overlay65sc02;
std::array<Instruction, 0x100> overlay65c02;
};

69
M6502/inc/system6502.h Normal file
View File

@ -0,0 +1,69 @@
#pragma once
#include <cstdint>
#include <string>
#include <vector>
#include <ctime>
#include <chrono>
#include <functional>
#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<EventArgs> Starting;
Signal<EventArgs> Finished;
Signal<EventArgs> Polling;
Signal<AddressEventArgs> ExecutingInstruction;
Signal<AddressEventArgs> 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;
};

View File

@ -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
}
}

232
M6502/src/Disassembly.cpp Normal file
View File

@ -0,0 +1,232 @@
#include "stdafx.h"
#include "Disassembly.h"
#include <sstream>
#include <iomanip>
#include <functional>
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();
}

View File

@ -0,0 +1,9 @@
namespace Processor
{
public enum ProcessorType
{
Cpu6502,
Cpu65SC02,
Cpu65C02
}
}

95
M6502/src/Profiler.cpp Normal file
View File

@ -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;
}
}
}
}

84
M6502/src/Symbols.cpp Normal file
View File

@ -0,0 +1,84 @@
#include "stdafx.h"
#include "Symbols.h"
#include <string>
#include <fstream>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/regex.hpp>
#include <boost/regex.hpp>
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<std::string, kv_pair_t>();
auto id = data.element["id"];
data.element.erase("id");
parsed[type][id] = data;
}
}
}
}
std::vector<std::string> Symbols::split(const std::string& input, const std::vector<std::string>& delimiters) {
std::vector<std::string> tokens;
boost::algorithm::split_regex(
tokens,
input,
boost::regex(boost::join(delimiters, "|"))
);
return tokens;
}

1653
M6502/src/mos6502.cpp Normal file

File diff suppressed because it is too large Load Diff

91
M6502/src/system6502.cpp Normal file
View File

@ -0,0 +1,91 @@
#include "stdafx.h"
#include "system6502.h"
#include <thread>
#include <functional>
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<std::chrono::milliseconds>(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));
}
}
}

68
README.md Normal file
View File

@ -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&amp;utm_medium=referral&amp;utm_content=MoleskiCoder/invaders&amp;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

65
Z80/inc/Disassembler.h Normal file
View File

@ -0,0 +1,65 @@
#pragma once
#include <string>
#include <boost/format.hpp>
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);
};

36
Z80/inc/InputOutput.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include <cstdint>
#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<PortEventArgs> ReadingPort;
Signal<PortEventArgs> ReadPort;
Signal<PortEventArgs> WritingPort;
Signal<PortEventArgs> 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<uint8_t, 0x100> input;
std::array<uint8_t, 0x100> output;
};

16
Z80/inc/PortEventArgs.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include <cstdint>
class PortEventArgs {
public:
PortEventArgs(uint8_t port)
: m_port(port) {}
uint8_t getPort() const {
return m_port;
}
private:
uint8_t m_port;
};

23
Z80/inc/Profiler.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#include <array>
#include <cstdint>
class Profiler {
public:
Profiler();
~Profiler();
void addInstruction(uint8_t instruction);
void addAddress(uint16_t address);
void dump() const;
private:
std::array<uint64_t, 0x100> m_instructions;
std::array<uint64_t, 0x10000> m_addresses;
void dumpInstructionProfiles() const;
void dumpAddressProfiles() const;
};

23
Z80/inc/Signal.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#include <vector>
#include <functional>
template<class T> class Signal {
private:
typedef std::function<void(T&)> delegate_t;
typedef std::vector<delegate_t> 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);
}
};

449
Z80/inc/Z80.h Normal file
View File

@ -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<Z80> 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<std::array<register16_t, 3>, 2> m_registers;
int m_registerSet;
std::array<register16_t, 2> 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<bool, 8> m_halfCarryTableAdd = { { false, false, true, false, true, false, true, true } };
std::array<bool, 8> 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));
}
};

725
Z80/src/Disassembler.cpp Normal file
View File

@ -0,0 +1,725 @@
#include "stdafx.h"
#include "Disassembler.h"
#include <sstream>
#include <iomanip>
#include <bitset>
#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();
}

38
Z80/src/InputOutput.cpp Normal file
View File

@ -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);
}

42
Z80/src/Profiler.cpp Normal file
View File

@ -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;
}
}

1611
Z80/src/Z80.cpp Normal file

File diff suppressed because it is too large Load Diff

13
inc/EventArgs.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
namespace EightBit {
class EventArgs {
private:
static EventArgs m_empty;
public:
static EventArgs& empty() { return m_empty; }
EventArgs() {}
};
}

78
inc/Memory.h Normal file
View File

@ -0,0 +1,78 @@
#pragma once
#include <array>
#include <cstdint>
#include <string>
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<uint8_t, 0x10000> m_bus;
std::array<bool, 0x10000> 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);
};
}

102
inc/Processor.h Normal file
View File

@ -0,0 +1,102 @@
#pragma once
#include <cstdint>
#include <array>
#include <functional>
#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;
}
};
}

25
inc/Signal.h Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#include <vector>
#include <functional>
namespace EightBit {
template<class T> class Signal {
private:
typedef std::function<void(const T&)> delegate_t;
typedef std::vector<delegate_t> 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);
}
};
}

158
src/EightBit.vcxproj Normal file
View File

@ -0,0 +1,158 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{A9C24BD9-0CB4-4C84-B09B-46B815F9DA47}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>EightBit</RootNamespace>
<WindowsTargetPlatformVersion>10.0.14393.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<IncludePath>../inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IncludePath>../inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<IncludePath>../inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IncludePath>../inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>Use</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>Use</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="..\inc\EventArgs.h" />
<ClInclude Include="..\inc\Memory.h" />
<ClInclude Include="..\inc\Processor.h" />
<ClInclude Include="..\inc\Signal.h" />
<ClInclude Include="stdafx.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="EventArgs.cpp" />
<ClCompile Include="Memory.cpp" />
<ClCompile Include="Processor.cpp" />
<ClCompile Include="stdafx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\inc\EventArgs.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\inc\Memory.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\inc\Processor.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\inc\Signal.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="EventArgs.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Memory.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Processor.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

4
src/EventArgs.cpp Normal file
View File

@ -0,0 +1,4 @@
#include "stdafx.h"
#include "EventArgs.h"
EightBit::EventArgs EightBit::EventArgs::m_empty;

68
src/Memory.cpp Normal file
View File

@ -0,0 +1,68 @@
#include "stdafx.h"
#include "Memory.h"
#include "Processor.h"
#include <iostream>
#include <fstream>
#include <algorithm>
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;
}

29
src/Processor.cpp Normal file
View File

@ -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;
}

1
src/stdafx.cpp Normal file
View File

@ -0,0 +1 @@
#include "stdafx.h"

19
src/stdafx.h Normal file
View File

@ -0,0 +1,19 @@
#ifdef _MSC_VER
#pragma once
#endif
#include <cstdint>
#include <functional>
#include <iostream>
#include <fstream>
#include <string>
#include <array>
#include <vector>
#if defined(_M_X64) || defined(_M_IX86 )
# define HOST_LITTLE_ENDIAN
#else
# define HOST_BIG_ENDIAN
#endif