This commit is contained in:
Adrian.Conlon 2017-07-25 21:23:27 +01:00
commit 569e1925ce
17 changed files with 1199 additions and 756 deletions

View File

@ -1,8 +1,10 @@
#pragma once
#include <string>
#include <boost/format.hpp>
namespace EightBit {
class Intel8080;
class Disassembler {
@ -10,14 +12,35 @@ namespace EightBit {
Disassembler();
static std::string state(Intel8080& cpu);
static std::string disassemble(Intel8080& cpu);
std::string disassemble(Intel8080& cpu);
static std::string flag(uint8_t value, int flag, const std::string& represents);
static std::string flag(uint8_t value, int flag, std::string represents, std::string off = "-");
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 invalid(uint8_t value);
private:
mutable boost::format m_formatter;
void disassemble(std::ostringstream& output, const Intel8080& cpu, uint16_t pc);
void disassemble(
std::ostringstream& output,
const Intel8080& 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);
static std::string alu2(int which);
};
}

View File

@ -8,8 +8,6 @@
namespace EightBit {
class Intel8080 : public IntelProcessor {
public:
typedef std::function<void()> instruction_t;
enum StatusBits {
SF = Bit7,
ZF = Bit6,
@ -18,51 +16,39 @@ namespace EightBit {
CF = Bit0,
};
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; }
bool isInterruptable() const;
virtual register16_t& AF() override { return af; }
virtual register16_t& BC() override { return bc; }
virtual register16_t& DE() override { return de; }
virtual register16_t& HL() override { return hl; }
int interrupt(uint8_t value);
bool isInterruptable() const {
return m_interrupt;
int step();
virtual register16_t& AF() override {
auto& f = af.low;
f = (f | Bit1) & ~(Bit5 | Bit3);
return af;
}
int interrupt(uint8_t value) {
if (isInterruptable()) {
di();
return execute(value);
}
return 0;
virtual register16_t& BC() override {
return bc;
}
virtual register16_t& DE() override {
return de;
}
virtual register16_t& HL() override {
return hl;
}
virtual void initialise();
int step();
private:
InputOutput& m_ports;
std::array<Instruction, 0x100> instructions;
register16_t af;
register16_t bc;
register16_t de;
@ -70,16 +56,65 @@ namespace EightBit {
bool m_interrupt;
int execute(uint8_t opcode);
int execute(const Instruction& instruction) {
cycles = 0;
instruction.vector();
return cycles + instruction.count;
uint8_t& R(int r) {
__assume(r < 8);
__assume(r >= 0);
switch (r) {
case 0b000:
return B();
case 0b001:
return C();
case 0b010:
return D();
case 0b011:
return E();
case 0b100:
return H();
case 0b101:
return L();
case 0b110:
m_memory.ADDRESS() = HL();
return m_memory.reference();
case 0b111:
return A();
default:
__assume(0);
}
throw std::logic_error("Unhandled registry mechanism");
}
void adjustReservedFlags() {
F() = (F() | Bit1) & ~(Bit5 | Bit3);
register16_t& RP(int rp) {
__assume(rp < 4);
__assume(rp >= 0);
switch (rp) {
case 0b00:
return BC();
case 0b01:
return DE();
case 0b10:
return HL();
case 0b11:
return SP();
default:
__assume(0);
}
}
register16_t& RP2(int rp) {
__assume(rp < 4);
__assume(rp >= 0);
switch (rp) {
case 0b00:
return BC();
case 0b01:
return DE();
case 0b10:
return HL();
case 0b11:
return AF();
default:
__assume(0);
}
}
static void adjustAuxiliaryCarryAdd(uint8_t& f, uint8_t before, uint8_t value, int calculation) {
@ -90,571 +125,46 @@ namespace EightBit {
clearFlag(f, AC, calculateHalfCarrySub(before, value, calculation));
}
static void increment(uint8_t& f, uint8_t& operand) {
adjustSZP<Intel8080>(f, ++operand);
clearFlag(f, AC, lowNibble(operand));
}
static void subtract(uint8_t& f, uint8_t& operand, uint8_t value, int carry = 0);
static void decrement(uint8_t& f, uint8_t& operand) {
adjustSZP<Intel8080>(f, --operand);
setFlag(f, AC, lowNibble(operand) != Mask4);
}
virtual int execute(uint8_t opcode);
void execute(int x, int y, int z, int p, int q);
static Instruction INS(instruction_t method, AddressingMode mode, std::string disassembly, int cycles);
Instruction UNKNOWN();
static void increment(uint8_t& f, uint8_t& operand);
void installInstructions();
static void decrement(uint8_t& f, uint8_t& operand);
//
bool returnConditionalFlag(uint8_t& f, int flag);
bool jumpConditionalFlag(uint8_t& f, int flag);
bool callConditionalFlag(uint8_t& f, int flag);
void compare(uint8_t& f, uint8_t check, uint8_t value) {
subtract(f, check, value);
}
static void add(uint8_t& f, register16_t& operand, register16_t value);
void anda(uint8_t value) {
auto& a = A();
auto& f = F();
setFlag(f, AC, (a | value) & Bit3);
clearFlag(f, CF);
adjustSZP<Intel8080>(f, a &= value);
}
static void add(uint8_t& f, uint8_t& operand, uint8_t value, int carry = 0);
static void adc(uint8_t& f, uint8_t& operand, uint8_t value);
static void sbb(uint8_t& f, uint8_t& operand, uint8_t value);
static void andr(uint8_t& f, uint8_t& operand, uint8_t value);
static void xorr(uint8_t& f, uint8_t& operand, uint8_t value);
static void orr(uint8_t& f, uint8_t& operand, uint8_t value);
static void compare(uint8_t& f, uint8_t check, uint8_t value);
void ora(uint8_t value) {
auto& f = F();
clearFlag(f, AC | CF);
adjustSZP<Intel8080>(f, A() |= value);
}
static void rlc(uint8_t& f, uint8_t& operand);
static void rrc(uint8_t& f, uint8_t& operand);
static void rl(uint8_t& f, uint8_t& operand);
static void rr(uint8_t& f, uint8_t& operand);
void xra(uint8_t value) {
auto& f = F();
clearFlag(f, AC | CF);
adjustSZP<Intel8080>(f, A() ^= value);
}
static void daa(uint8_t& a, uint8_t& f);
void add(uint8_t value, int carry = 0) {
auto& a = A();
auto& f = F();
register16_t sum;
sum.word = a + value + carry;
adjustAuxiliaryCarryAdd(f, a, value, sum.word);
a = sum.low;
setFlag(f, CF, sum.word & Bit8);
adjustSZP<Intel8080>(f, a);
}
static void cma(uint8_t& a, uint8_t& f);
static void stc(uint8_t& a, uint8_t& f);
static void cmc(uint8_t& a, uint8_t& f);
void adc(uint8_t value) {
add(value, F() & CF);
}
void xhtl();
void dad(uint16_t value) {
auto& f = F();
auto sum = HL().word + value;
setFlag(f, CF, sum & Bit16);
HL().word = sum;
}
void out();
void in();
void subtract(uint8_t& f, uint8_t& operand, uint8_t value, int carry = 0) {
register16_t result;
result.word = operand - value - carry;
adjustAuxiliaryCarrySub(f, operand, value, result.word);
operand = result.low;
setFlag(f, CF, result.word & Bit8);
adjustSZP<Intel8080>(f, operand);
}
void sbb(uint8_t value) {
subtract(F(), A(), value, F() & CF);
}
void mov_m_r(uint8_t value) {
m_memory.ADDRESS() = HL();
m_memory.reference() = value;
}
uint8_t mov_r_m() {
m_memory.ADDRESS() = HL();
return m_memory.reference();
}
//
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.ADDRESS() = HL();
m_memory.reference() = data;
}
void lxi_b() { fetchWord(BC()); }
void lxi_d() { fetchWord(DE()); }
void lxi_h() { fetchWord(HL()); }
void stax_r(register16_t& destination) {
m_memory.ADDRESS() = destination;
m_memory.reference() = A();
}
void stax_b() { stax_r(BC()); }
void stax_d() { stax_r(DE()); }
void ldax_r(register16_t& source) {
m_memory.ADDRESS() = source;
A() = m_memory.reference();
}
void ldax_b() { ldax_r(BC()); }
void ldax_d() { ldax_r(DE()); }
void sta() {
fetchWord();
memptrReference() = A();
}
void lda() {
fetchWord();
A() = memptrReference();
}
void shld() {
fetchWord();
setWordViaMemptr(HL());
}
void lhld() {
fetchWord();
getWordViaMemptr(HL());
}
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() { pushWord(AF()); }
void pop_b() { popWord(BC()); }
void pop_d() { popWord(DE()); }
void pop_h() { popWord(HL()); }
void pop_psw() {
popWord(AF());
adjustReservedFlags();
}
void xhtl() {
m_memory.ADDRESS() = SP();
MEMPTR().low = m_memory.reference();
m_memory.reference() = L();
L() = MEMPTR().low;
m_memory.ADDRESS().word++;
MEMPTR().high = m_memory.reference();
m_memory.reference() = H();
H() = MEMPTR().high;
}
void sphl() {
SP() = HL();
}
void lxi_sp() {
fetchWord(SP());
}
void inx_sp() { ++SP().word; }
void dcx_sp() { --SP().word; }
// jump
void jmp() { jumpConditional(true); }
void jc() { jumpConditional(F() & CF); }
void jnc() { jumpConditional(!(F() & CF)); }
void jz() { jumpConditional(F() & ZF); }
void jnz() { jumpConditional(!(F() & ZF)); }
void jpe() { jumpConditional(F() & PF); }
void jpo() { jumpConditional(!(F() & PF)); }
void jm() { jumpConditional(F() & SF); }
void jp() { jumpConditional(!(F() & SF)); }
void pchl() {
PC() = HL();
}
// call
void callDirect() {
fetchWord();
call();
}
void cc() { if (callConditional(F() & CF)) cycles += 6; }
void cnc() { if (callConditional(!(F() & CF))) cycles += 6; }
void cpe() { if (callConditional(F() & PF)) cycles += 6; }
void cpo() { if (callConditional(!(F() & PF))) cycles += 6; }
void cz() { if (callConditional(F() & ZF)) cycles += 6; }
void cnz() { if (callConditional(!(F() & ZF))) cycles += 6; }
void cm() { if (callConditional(F() & SF)) cycles += 6; }
void cp() { if (callConditional(!(F() & SF))) cycles += 6; }
// return
void rc() { if (returnConditional(F() & CF)) cycles += 6; }
void rnc() { if (returnConditional(!(F() & CF))) cycles += 6; }
void rz() { if (returnConditional(F() & ZF)) cycles += 6; }
void rnz() { if (returnConditional(!(F() & ZF))) cycles += 6; }
void rpe() { if (returnConditional(F() & PF)) cycles += 6; }
void rpo() { if (returnConditional(!(F() & PF))) cycles += 6; }
void rm() { if (returnConditional(F() & SF)) cycles += 6; }
void rp() { if (returnConditional(!(F() & SF))) cycles += 6; }
// restart
void rst_0() { restart(0 << 3); }
void rst_1() { restart(1 << 3); }
void rst_2() { restart(2 << 3); }
void rst_3() { restart(3 << 3); }
void rst_4() { restart(4 << 3); }
void rst_5() { restart(5 << 3); }
void rst_6() { restart(6 << 3); }
void rst_7() { restart(7 << 3); }
// increment and decrement
void inr_a() { increment(F(), A()); }
void inr_b() { increment(F(), B()); }
void inr_c() { increment(F(), C()); }
void inr_d() { increment(F(), D()); }
void inr_e() { increment(F(), E()); }
void inr_h() { increment(F(), H()); }
void inr_l() { increment(F(), L()); }
void inr_m() {
m_memory.ADDRESS() = HL();
increment(F(), m_memory.reference());
}
void dcr_a() { decrement(F(), A()); }
void dcr_b() { decrement(F(), B()); }
void dcr_c() { decrement(F(), C()); }
void dcr_d() { decrement(F(), D()); }
void dcr_e() { decrement(F(), E()); }
void dcr_h() { decrement(F(), H()); }
void dcr_l() { decrement(F(), L()); }
void dcr_m() {
m_memory.ADDRESS() = HL();
decrement(F(), m_memory.reference());
}
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() {
m_memory.ADDRESS() = HL();
add(m_memory.reference());
}
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() {
m_memory.ADDRESS() = HL();
adc(m_memory.reference());
}
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() { subtract(F(), A(), A()); }
void sub_b() { subtract(F(), A(), B()); }
void sub_c() { subtract(F(), A(), C()); }
void sub_d() { subtract(F(), A(), D()); }
void sub_e() { subtract(F(), A(), E()); }
void sub_h() { subtract(F(), A(), H()); }
void sub_l() { subtract(F(), A(), L()); }
void sub_m() {
m_memory.ADDRESS() = HL();
subtract(F(), A(), m_memory.reference());
}
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() {
m_memory.ADDRESS() = HL();
sbb(m_memory.reference());
}
void sbi() {
sbb(fetchByte());
}
void sui() {
subtract(F(), A(), fetchByte());
}
// 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() {
m_memory.ADDRESS() = HL();
anda(m_memory.reference());
}
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() {
m_memory.ADDRESS() = HL();
xra(m_memory.reference());
}
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() {
m_memory.ADDRESS() = HL();
ora(m_memory.reference());
}
void ori() { ora(fetchByte()); }
void cmp_a() { compare(F(), A(), A()); }
void cmp_b() { compare(F(), A(), B()); }
void cmp_c() { compare(F(), A(), C()); }
void cmp_d() { compare(F(), A(), D()); }
void cmp_e() { compare(F(), A(), E()); }
void cmp_h() { compare(F(), A(), H()); }
void cmp_l() { compare(F(), A(), L()); }
void cmp_m() {
m_memory.ADDRESS() = HL();
compare(F(), A(), m_memory.reference());
}
void cpi() { compare(F(), A(), fetchByte()); }
// rotate
void rlc() {
auto& a = A();
auto carry = a & Bit7;
a = (a << 1) | (carry >> 7);
setFlag(F(), CF, carry);
}
void rrc() {
auto& a = A();
auto carry = a & Bit0;
a = (a >> 1) | (carry << 7);
setFlag(F(), CF, carry);
}
void ral() {
auto& a = A();
auto& f = F();
const auto carry = f & CF;
setFlag(f, CF, a & Bit7);
a = (a << 1) | carry;
}
void rar() {
auto& a = A();
auto& f = F();
const auto carry = f & CF;
setFlag(f, CF, a & Bit0);
a = (a >> 1) | (carry << 7);
}
// specials
void cma() { A() ^= Mask8; }
void stc() { setFlag(F(), CF); }
void cmc() { clearFlag(F(), CF, F() & CF); }
void daa() {
const auto& a = A();
auto& f = F();
auto carry = f & CF;
uint8_t addition = 0;
if ((f & AC) || lowNibble(a) > 9) {
addition = 0x6;
}
if ((f & CF) || highNibble(a) > 9 || (highNibble(a) >= 9 && lowNibble(a) > 9)) {
addition |= 0x60;
carry = true;
}
add(addition);
setFlag(f, CF, carry);
}
// input/output
void out() { m_ports.write(fetchByte(), A()); }
void in() { A() = m_ports.read(fetchByte()); }
// control
void ei() { m_interrupt = true; }
void di() { m_interrupt = false; }
void nop() {}
void hlt() { halt(); }
void ei();
void di();
};
}

View File

@ -9,6 +9,8 @@
#include "Intel8080.h"
EightBit::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 EightBit::Disassembler::state(Intel8080& cpu) {
@ -42,53 +44,402 @@ std::string EightBit::Disassembler::state(Intel8080& cpu) {
return output.str();
}
std::string EightBit::Disassembler::RP(int rp) const {
switch (rp) {
case 0:
return "B";
case 1:
return "D";
case 2:
return "H";
case 3:
return "SP";
}
throw std::logic_error("Unhandled register pair");
}
std::string EightBit::Disassembler::RP2(int rp) const {
switch (rp) {
case 0:
return "B";
case 1:
return "D";
case 2:
return "H";
case 3:
return "PSW";
}
throw std::logic_error("Unhandled register pair");
}
std::string EightBit::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 "M";
case 7:
return "A";
}
throw std::logic_error("Unhandled register");
}
std::string EightBit::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 EightBit::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 "SBB";
case 4: // AND n
return "ANA";
case 5: // XOR n
return "XRA";
case 6: // OR n
return "ORA";
case 7: // CP n
return "CMP";
}
throw std::logic_error("Unhandled alu operation");
}
std::string EightBit::Disassembler::alu2(int which) {
switch (which) {
case 0: // ADD A,n
return "ADI";
case 1: // ADC
return "ACI";
case 2: // SUB n
return "SUI";
case 3: // SBC A,n
return "SBI";
case 4: // AND n
return "ANI";
case 5: // XOR n
return "XRI";
case 6: // OR n
return "ORI";
case 7: // CP n
return "CPI";
}
throw std::logic_error("Unhandled alu operation");
}
std::string EightBit::Disassembler::disassemble(Intel8080& cpu) {
const auto& memory = cpu.getMemory();
auto pc = cpu.PC();
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(memory.peekWord(pc.word + 1));
break;
default:
break;
}
disassemble(output, cpu, cpu.PC().word);
return output.str();
}
std::string EightBit::Disassembler::flag(uint8_t value, int flag, const std::string& represents) {
void EightBit::Disassembler::disassemble(std::ostringstream& output, const Intel8080& cpu, uint16_t pc) {
const auto& memory = cpu.getMemory();
auto opcode = memory.peek(pc);
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 = "";
disassemble(
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 EightBit::Disassembler::disassemble(
std::ostringstream& output,
const Intel8080& 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'
break;
case 2: // DJNZ d
break;
case 3: // JR d
break;
default: // JR cc,d
break;
}
break;
case 1: // 16-bit load immediate/add
switch (q) {
case 0: // LD rp,nn
specification = "LXI " + RP(p) + ",%2$04XH";
dumpCount += 2;
break;
case 1: // ADD HL,rp
specification = "DAD " + RP(p);
break;
}
break;
case 2: // Indirect loading
switch (q) {
case 0:
switch (p) {
case 0: // LD (BC),A
specification = "STAX B";
break;
case 1: // LD (DE),A
specification = "STAX D";
break;
case 2: // LD (nn),HL
specification = "SHLD %2$04XH";
dumpCount += 2;
break;
case 3: // LD (nn),A
specification = "STA %2$04XH";
dumpCount += 2;
break;
}
break;
case 1:
switch (p) {
case 0: // LD A,(BC)
specification = "LDAX B";
break;
case 1: // LD A,(DE)
specification = "LDAX D";
break;
case 2: // LD HL,(nn)
specification = "LHLD %2$04XH";
dumpCount += 2;
break;
case 3: // LD A,(nn)
specification = "LDA %2$04XH";
dumpCount += 2;
break;
}
break;
}
break;
case 3: // 16-bit INC/DEC
switch (q) {
case 0: // INC rp
specification = "INX " + RP(p);
break;
case 1: // DEC rp
specification = "DCX " + RP(p);
break;
}
break;
case 4: // 8-bit INC
specification = "INR " + R(y);
break;
case 5: // 8-bit DEC
specification = "DCR " + R(y);
break;
case 6: // 8-bit load immediate
specification = "MVI " + R(y) + ",%1$02XH";
dumpCount++;
break;
case 7: // Assorted operations on accumulator/flags
switch (y) {
case 0:
specification = "RLC";
break;
case 1:
specification = "RRC";
break;
case 2:
specification = "RAL";
break;
case 3:
specification = "RAR";
break;
case 4:
specification = "DAA";
break;
case 5:
specification = "CMA";
break;
case 6:
specification = "STC";
break;
case 7:
specification = "CMC";
break;
}
break;
}
break;
case 1: // 8-bit loading
if (z == 6 && y == 6) { // Exception (replaces LD (HL), (HL))
specification = "HLT";
} else {
specification = "MOV " + R(y) + "," + R(z);
}
break;
case 2: // Operate on accumulator and register/memory location
specification = alu(y) + " " + R(z);
break;
case 3:
switch (z) {
case 0: // Conditional return
specification = "R" + 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
break;
case 2: // JP HL
specification = "PCHL";
break;
case 3: // LD SP,HL
specification = "SPHL";
break;
}
}
break;
case 2: // Conditional jump
specification = "J" + cc(y) + " %2$04XH";
dumpCount += 2;
break;
case 3: // Assorted operations
switch (y) {
case 0: // JP nn
specification = "JMP %2$04XH";
dumpCount += 2;
break;
case 1: // CB prefix
break;
case 2: // OUT (n),A
specification = "OUT %1$02XH";
dumpCount++;
break;
case 3: // IN A,(n)
specification = "IN %1$02XH";
dumpCount++;
break;
case 4: // EX (SP),HL
specification = "XHTL";
break;
case 5: // EX DE,HL
specification = "XCHG";
break;
case 6: // DI
specification = "DI";
break;
case 7: // EI
specification = "EI";
break;
}
break;
case 4: // Conditional call: CALL cc[y], nn
specification = "C" + 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
break;
case 2: // ED prefix
break;
case 3: // FD prefix
break;
}
}
break;
case 6: // Operate on accumulator and immediate operand: alu[y] n
specification = alu2(y) + " %1$02XH";
dumpCount++;
break;
case 7: // Restart: RST y * 8
specification = "RST " + hex((uint8_t)y);
break;
}
break;
}
}
std::string EightBit::Disassembler::flag(uint8_t value, int flag, std::string represents, std::string off) {
std::ostringstream output;
output << (value & flag ? represents : "-");
output << (value & flag ? represents : off);
return output.str();
}
@ -97,11 +448,11 @@ std::string EightBit::Disassembler::flags(uint8_t value) {
output
<< flag(value, Intel8080::SF, "S")
<< flag(value, Intel8080::ZF, "Z")
<< "0"
<< flag(value, Processor::Bit5, "1", "0")
<< flag(value, Intel8080::AC, "A")
<< "0"
<< flag(value, Processor::Bit3, "1", "0")
<< flag(value, Intel8080::PF, "P")
<< "1"
<< flag(value, Processor::Bit1, "1", "0")
<< flag(value, Intel8080::CF, "C");
return output.str();
}

View File

@ -9,74 +9,624 @@ EightBit::Intel8080::Intel8080(Memory& memory, InputOutput& ports)
m_interrupt(false),
m_ports(ports) {
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(callDirect), 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();
IntelProcessor::initialise();
AF().word = BC().word = DE().word = HL().word = 0;
}
#pragma region Interrupt routines
void EightBit::Intel8080::di() {
m_interrupt = false;
}
void EightBit::Intel8080::ei() {
m_interrupt = true;
}
int EightBit::Intel8080::interrupt(uint8_t value) {
if (isInterruptable()) {
di();
return execute(value);
}
return 0;
}
bool EightBit::Intel8080::isInterruptable() const {
return m_interrupt;
}
#pragma endregion Interrupt routines
#pragma region Flag manipulation helpers
void EightBit::Intel8080::increment(uint8_t& f, uint8_t& operand) {
adjustSZP<Intel8080>(f, ++operand);
clearFlag(f, AC, lowNibble(operand));
}
void EightBit::Intel8080::decrement(uint8_t& f, uint8_t& operand) {
adjustSZP<Intel8080>(f, --operand);
setFlag(f, AC, lowNibble(operand) != Mask4);
}
#pragma endregion Flag manipulation helpers
#pragma region PC manipulation: call/ret/jp/jr
bool EightBit::Intel8080::jumpConditionalFlag(uint8_t& f, int flag) {
switch (flag) {
case 0: // NZ
return jumpConditional(!(f & ZF));
case 1: // Z
return jumpConditional(f & ZF);
case 2: // NC
return jumpConditional(!(f & CF));
case 3: // C
return jumpConditional(f & CF);
case 4: // PO
return jumpConditional(!(f & PF));
case 5: // PE
return jumpConditional(f & PF);
case 6: // P
return jumpConditional(!(f & SF));
case 7: // M
return jumpConditional(f & SF);
default:
__assume(0);
}
throw std::logic_error("Unhandled JP conditional");
}
bool EightBit::Intel8080::returnConditionalFlag(uint8_t& f, int flag) {
switch (flag) {
case 0: // NZ
return returnConditional(!(f & ZF));
case 1: // Z
return returnConditional(f & ZF);
case 2: // NC
return returnConditional(!(f & CF));
case 3: // C
return returnConditional(f & CF);
case 4: // PO
return returnConditional(!(f & PF));
case 5: // PE
return returnConditional(f & PF);
case 6: // P
return returnConditional(!(f & SF));
case 7: // M
return returnConditional(f & SF);
default:
__assume(0);
}
throw std::logic_error("Unhandled RET conditional");
}
bool EightBit::Intel8080::callConditionalFlag(uint8_t& f, int flag) {
switch (flag) {
case 0: // NZ
return callConditional(!(f & ZF));
case 1: // Z
return callConditional(f & ZF);
case 2: // NC
return callConditional(!(f & CF));
case 3: // C
return callConditional(f & CF);
case 4: // PO
return callConditional(!(f & PF));
case 5: // PE
return callConditional(f & PF);
case 6: // P
return callConditional(!(f & SF));
case 7: // M
return callConditional(f & SF);
default:
__assume(0);
}
throw std::logic_error("Unhandled CALL conditional");
}
#pragma endregion PC manipulation: call/ret/jp/jr
#pragma region 16-bit arithmetic
void EightBit::Intel8080::add(uint8_t& f, register16_t& operand, register16_t value) {
const auto result = operand.word + value.word;
setFlag(f, CF, result & Bit16);
operand.word = result;
}
#pragma endregion 16-bit arithmetic
#pragma region ALU
void EightBit::Intel8080::add(uint8_t& f, uint8_t& operand, uint8_t value, int carry) {
register16_t result;
result.word = operand + value + carry;
adjustAuxiliaryCarryAdd(f, operand, value, result.word);
operand = result.low;
setFlag(f, CF, result.word & Bit8);
adjustSZP<Intel8080>(f, operand);
}
void EightBit::Intel8080::adc(uint8_t& f, uint8_t& operand, uint8_t value) {
add(f, operand, value, f & CF);
}
void EightBit::Intel8080::subtract(uint8_t& f, uint8_t& operand, uint8_t value, int carry) {
register16_t result;
result.word = operand - value - carry;
adjustAuxiliaryCarrySub(f, operand, value, result.word);
operand = result.low;
setFlag(f, CF, result.word & Bit8);
adjustSZP<Intel8080>(f, operand);
}
void EightBit::Intel8080::sbb(uint8_t& f, uint8_t& operand, uint8_t value) {
subtract(f, operand, value, f & CF);
}
void EightBit::Intel8080::andr(uint8_t& f, uint8_t& operand, uint8_t value) {
setFlag(f, AC, (operand | value) & Bit3);
clearFlag(f, CF);
adjustSZP<Intel8080>(f, operand &= value);
}
void EightBit::Intel8080::xorr(uint8_t& f, uint8_t& operand, uint8_t value) {
clearFlag(f, AC | CF);
adjustSZP<Intel8080>(f, operand ^= value);
}
void EightBit::Intel8080::orr(uint8_t& f, uint8_t& operand, uint8_t value) {
clearFlag(f, AC | CF);
adjustSZP<Intel8080>(f, operand |= value);
}
void EightBit::Intel8080::compare(uint8_t& f, uint8_t check, uint8_t value) {
subtract(f, check, value);
}
#pragma endregion ALU
#pragma region Shift and rotate
void EightBit::Intel8080::rlc(uint8_t& f, uint8_t& operand) {
auto carry = operand & Bit7;
operand = (operand << 1) | (carry >> 7);
setFlag(f, CF, carry);
}
void EightBit::Intel8080::rrc(uint8_t& f, uint8_t& operand) {
auto carry = operand & Bit0;
operand = (operand >> 1) | (carry << 7);
setFlag(f, CF, carry);
}
void EightBit::Intel8080::rl(uint8_t& f, uint8_t& operand) {
const auto carry = f & CF;
setFlag(f, CF, operand & Bit7);
operand = (operand << 1) | carry;
}
void EightBit::Intel8080::rr(uint8_t& f, uint8_t& operand) {
const auto carry = f & CF;
setFlag(f, CF, operand & Bit0);
operand = (operand >> 1) | (carry << 7);
}
#pragma endregion Shift and rotate
#pragma region Miscellaneous instructions
void EightBit::Intel8080::daa(uint8_t& a, uint8_t& f) {
const auto& before = a;
auto carry = f & CF;
uint8_t addition = 0;
if ((f & AC) || lowNibble(before) > 9) {
addition = 0x6;
}
if ((f & CF) || highNibble(before) > 9 || (highNibble(before) >= 9 && lowNibble(before) > 9)) {
addition |= 0x60;
carry = true;
}
add(f, a, addition);
setFlag(f, CF, carry);
}
void EightBit::Intel8080::cma(uint8_t& a, uint8_t& f) {
a = ~a;
}
void EightBit::Intel8080::stc(uint8_t& a, uint8_t& f) {
setFlag(f, CF);
}
void EightBit::Intel8080::cmc(uint8_t& a, uint8_t& f) {
clearFlag(f, CF, f & CF);
}
void EightBit::Intel8080::xhtl() {
m_memory.ADDRESS() = SP();
MEMPTR().low = m_memory.reference();
m_memory.reference() = L();
L() = MEMPTR().low;
m_memory.ADDRESS().word++;
MEMPTR().high = m_memory.reference();
m_memory.reference() = H();
H() = MEMPTR().high;
}
#pragma endregion Miscellaneous instructions
#pragma region I/O instructions
void EightBit::Intel8080::out() {
m_ports.write(fetchByte(), A());
}
void EightBit::Intel8080::in() {
A() = m_ports.read(fetchByte());
}
#pragma endregion I/O instructions
int EightBit::Intel8080::step() {
ExecutingInstruction.fire(*this);
return execute(fetchByte());
cycles = 0;
return fetchExecute();
}
int EightBit::Intel8080::execute(uint8_t opcode) {
const auto& instruction = instructions[opcode];
return execute(instruction);
const auto& decoded = getDecodedOpcode(opcode);
auto x = decoded.x;
auto y = decoded.y;
auto z = decoded.z;
auto p = decoded.p;
auto q = decoded.q;
execute(x, y, z, p, q);
if (cycles == 0)
throw std::logic_error("Unhandled opcode");
return cycles;
}
//
void EightBit::Intel8080::___() {
m_memory.ADDRESS().word = PC().word - 1;
auto opcode = m_memory.reference();
auto message = Disassembler::invalid(opcode);
throw std::domain_error(message);
void EightBit::Intel8080::execute(int x, int y, int z, int p, int q) {
auto& a = A();
auto& f = F();
switch (x) {
case 0:
switch (z) {
case 0: // Relative jumps and assorted ops
switch (y) {
case 0: // NOP
cycles += 4;
break;
}
break;
case 1: // 16-bit load immediate/add
switch (q) {
case 0: // LD rp,nn
Processor::fetchWord(RP(p));
cycles += 10;
break;
case 1: // ADD HL,rp
add(f, HL(), RP(p));
cycles += 11;
break;
}
break;
case 2: // Indirect loading
switch (q) {
case 0:
switch (p) {
case 0: // LD (BC),A
MEMPTR() = BC();
MEMPTR().high = memptrReference() = a;
cycles += 7;
break;
case 1: // LD (DE),A
MEMPTR() = DE();
MEMPTR().high = memptrReference() = a;
cycles += 7;
break;
case 2: // LD (nn),HL
fetchWord();
setWordViaMemptr(HL());
cycles += 16;
break;
case 3: // LD (nn),A
fetchWord();
MEMPTR().high = memptrReference() = a;
cycles += 13;
break;
default:
__assume(0);
}
break;
case 1:
switch (p) {
case 0: // LD A,(BC)
MEMPTR() = BC();
a = memptrReference();
cycles += 7;
break;
case 1: // LD A,(DE)
MEMPTR() = DE();
a = memptrReference();
cycles += 7;
break;
case 2: // LD HL,(nn)
fetchWord();
getWordViaMemptr(HL());
cycles += 16;
break;
case 3: // LD A,(nn)
fetchWord();
a = memptrReference();
cycles += 13;
break;
default:
__assume(0);
}
break;
default:
__assume(0);
}
break;
case 3: // 16-bit INC/DEC
switch (q) {
case 0: // INC rp
++RP(p).word;
break;
case 1: // DEC rp
--RP(p).word;
break;
default:
__assume(0);
}
cycles += 6;
break;
case 4: // 8-bit INC
increment(f, R(y)); // INC r
cycles += 4;
break;
case 5: // 8-bit DEC
decrement(f, R(y)); // DEC r
cycles += 4;
if (y == 6)
cycles += 7;
break;
case 6: // 8-bit load immediate
R(y) = fetchByte();
cycles += 7;
if (y == 6)
cycles += 3;
break;
case 7: // Assorted operations on accumulator/flags
switch (y) {
case 0:
rlc(f, a);
break;
case 1:
rrc(f, a);
break;
case 2:
rl(f, a);
break;
case 3:
rr(f, a);
break;
case 4:
daa(a, f);
break;
case 5:
cma(a, f);
break;
case 6:
stc(a, f);
break;
case 7:
cmc(a, f);
break;
default:
__assume(0);
}
cycles += 4;
break;
default:
__assume(0);
}
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 += 3;
}
cycles += 4;
break;
case 2: // Operate on accumulator and register/memory location
switch (y) {
case 0: // ADD A,r
add(f, a, R(z));
break;
case 1: // ADC A,r
adc(f, a, R(z));
break;
case 2: // SUB r
subtract(f, a, R(z));
break;
case 3: // SBC A,r
sbb(f, a, R(z));
break;
case 4: // AND r
andr(f, a, R(z));
break;
case 5: // XOR r
xorr(f, a, R(z));
break;
case 6: // OR r
orr(f, a, R(z));
break;
case 7: // CP r
compare(f, a, R(z));
break;
default:
__assume(0);
}
cycles += 4;
if (z == 6)
cycles += 3;
break;
case 3:
switch (z) {
case 0: // Conditional return
if (returnConditionalFlag(f, y))
cycles += 6;
cycles += 5;
break;
case 1: // POP & various ops
switch (q) {
case 0: // POP rp2[p]
popWord(RP2(p));
cycles += 10;
break;
case 1:
switch (p) {
case 0: // RET
ret();
cycles += 10;
break;
case 2: // JP HL
PC() = HL();
cycles += 4;
break;
case 3: // LD SP,HL
SP() = HL();
cycles += 4;
break;
}
break;
default:
__assume(0);
}
break;
case 2: // Conditional jump
jumpConditionalFlag(f, y);
cycles += 10;
break;
case 3: // Assorted operations
switch (y) {
case 0: // JP nn
fetchWord();
jump();
cycles += 10;
break;
case 2: // OUT (n),A
out();
cycles += 11;
break;
case 3: // IN A,(n)
in();
cycles += 11;
break;
case 4: // EX (SP),HL
xhtl();
cycles += 19;
break;
case 5: // EX DE,HL
std::swap(DE(), HL());
cycles += 4;
break;
case 6: // DI
di();
cycles += 4;
break;
case 7: // EI
ei();
cycles += 4;
break;
}
break;
case 4: // Conditional call: CALL cc[y], nn
if (callConditionalFlag(f, y))
cycles += 7;
cycles += 10;
break;
case 5: // PUSH & various ops
switch (q) {
case 0: // PUSH rp2[p]
pushWord(RP2(p));
cycles += 11;
break;
case 1:
switch (p) {
case 0: // CALL nn
fetchWord();
call();
cycles += 17;
break;
}
break;
default:
__assume(0);
}
break;
case 6: // Operate on accumulator and immediate operand: alu[y] n
switch (y) {
case 0: // ADD A,n
add(f, a, fetchByte());
break;
case 1: // ADC A,n
adc(f, a, fetchByte());
break;
case 2: // SUB n
subtract(f, a, fetchByte());
break;
case 3: // SBC A,n
sbb(f, a, fetchByte());
break;
case 4: // AND n
andr(f, a, fetchByte());
break;
case 5: // XOR n
xorr(f, a, fetchByte());
break;
case 6: // OR n
orr(f, a, fetchByte());
break;
case 7: // CP n
compare(f, a, fetchByte());
break;
default:
__assume(0);
}
cycles += 7;
break;
case 7: // Restart: RST y * 8
restart(y << 3);
cycles += 11;
break;
default:
__assume(0);
}
break;
}
}

View File

@ -71,16 +71,16 @@
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<IncludePath>../inc;../../inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)</IncludePath>
<IncludePath>..\inc;..\..\inc;C:\local\boost_1_64_0;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<IncludePath>../inc;../../inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)</IncludePath>
<IncludePath>..\inc;..\..\inc;C:\local\boost_1_64_0;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IncludePath>../inc;../../inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)</IncludePath>
<IncludePath>..\inc;..\..\inc;C:\local\boost_1_64_0;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IncludePath>../inc;../../inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)</IncludePath>
<IncludePath>..\inc;..\..\inc;C:\local\boost_1_64_0;$(IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>

View File

@ -13,6 +13,8 @@
#include <vector>
#include <bitset>
#include <boost/format.hpp>
#if defined(_M_X64) || defined(_M_IX86 )
# define HOST_LITTLE_ENDIAN
#else

View File

@ -1,6 +1,5 @@
#include "stdafx.h"
#include "Board.h"
#include "Disassembler.h"
#include "Configuration.h"
#include <iostream>
@ -83,6 +82,6 @@ void Board::Cpu_ExecutingInstruction_Debug(const EightBit::Intel8080&) {
std::cerr
<< EightBit::Disassembler::state(m_cpu)
<< "\t"
<< EightBit::Disassembler::disassemble(m_cpu)
<< m_disassembler.disassemble(m_cpu)
<< '\n';
}

View File

@ -1,12 +1,11 @@
#pragma once
//#include <string>
#include "Memory.h"
#include "InputOutput.h"
#include "Intel8080.h"
#include "Profiler.h"
#include "EventArgs.h"
#include "Disassembler.h"
class Configuration;
@ -24,6 +23,7 @@ private:
EightBit::Memory m_memory;
EightBit::InputOutput m_ports;
EightBit::Intel8080 m_cpu;
EightBit::Disassembler m_disassembler;
EightBit::Profiler m_profiler;
void Cpu_ExecutingInstruction_Cpm(const EightBit::Intel8080& cpu);

View File

@ -8,9 +8,10 @@ int main(int, char*[]) {
Configuration configuration;
#ifdef _DEBUG
configuration.setDebugMode(true);
//configuration.setDebugMode(true);
configuration.setProfileMode(true);
#endif
//configuration.setDebugMode(true);
EightBit::TestHarness<Configuration, Board> harness(configuration);
harness.initialise();

View File

@ -31,44 +31,42 @@ namespace EightBit {
int interrupt(uint8_t value);
int execute(uint8_t opcode);
virtual int execute(uint8_t opcode);
int step();
// Mutable access to processor!!
virtual register16_t& AF() override {
m_accumulatorFlag.low &= 0xf0;
return m_accumulatorFlag;
af.low &= 0xf0;
return af;
}
virtual register16_t& BC() override {
return m_registers[BC_IDX];
return bc;
}
virtual register16_t& DE() override {
return m_registers[DE_IDX];
return de;
}
virtual register16_t& HL() override {
return m_registers[HL_IDX];
return hl;
}
virtual void reset();
virtual void initialise();
protected:
virtual uint8_t fetchByte() {
virtual uint8_t fetchByte() override {
auto returned = IntelProcessor::fetchByte();
m_memory.fireReadBusEvent();
return returned;
}
virtual void push(uint8_t value) {
virtual void push(uint8_t value) override {
IntelProcessor::push(value);
m_memory.fireWriteBusEvent();
}
virtual uint8_t pop() {
virtual uint8_t pop() override {
auto returned = IntelProcessor::pop();
m_memory.fireReadBusEvent();
return returned;
@ -91,12 +89,12 @@ namespace EightBit {
}
private:
enum { BC_IDX, DE_IDX, HL_IDX };
Bus& m_bus;
std::array<register16_t, 3> m_registers;
register16_t m_accumulatorFlag;
register16_t af;
register16_t bc;
register16_t de;
register16_t hl;
bool m_ime;
@ -104,10 +102,6 @@ namespace EightBit {
bool m_stopped;
int fetchExecute() {
return execute(fetchByte());
}
uint8_t& R(int r, uint8_t& a) {
switch (r) {
case 0:
@ -132,20 +126,36 @@ namespace EightBit {
}
register16_t& RP(int rp) {
__assume(rp < 4);
__assume(rp >= 0);
switch (rp) {
case 3:
case 0b00:
return BC();
case 0b01:
return DE();
case 0b10:
return HL();
case 0b11:
return SP();
default:
return m_registers[rp];
__assume(0);
}
}
register16_t& RP2(int rp) {
__assume(rp < 4);
__assume(rp >= 0);
switch (rp) {
case 3:
case 0b00:
return BC();
case 0b01:
return DE();
case 0b10:
return HL();
case 0b11:
return AF();
default:
return m_registers[rp];
__assume(0);
}
}

View File

@ -500,7 +500,7 @@ void EightBit::LR35902::executeOther(int x, int y, int z, int p, int q) {
case 1: // 16-bit load immediate/add
switch (q) {
case 0: // LD rp,nn
fetchWord(RP(p));
Processor::fetchWord(RP(p));
cycles += 3;
break;
case 1: // ADD HL,rp

View File

@ -78,7 +78,7 @@ namespace EightBit {
protected:
virtual void interrupt(uint16_t vector);
virtual int execute(uint8_t cell);
virtual int execute(uint8_t opcode);
private:
register16_t& MEMPTR() { return m_memptr; }
@ -96,8 +96,7 @@ namespace EightBit {
void pushWord(register16_t value);
void popWord(register16_t& output);
uint8_t fetchByte();
void fetchWord(register16_t& output);
virtual uint8_t fetchByte() override;
#pragma region 6502 addressing modes

View File

@ -385,12 +385,6 @@ uint8_t EightBit::MOS6502::fetchByte() {
return getByte();
}
void EightBit::MOS6502::fetchWord(register16_t& output) {
m_memory.ADDRESS().word = PC().word++;
getWord(output);
PC().word++;
}
////
void EightBit::MOS6502::ROR(uint8_t& output) {

View File

@ -54,11 +54,9 @@ namespace EightBit {
int interrupt(bool maskable, uint8_t value);
int execute(uint8_t opcode);
virtual int execute(uint8_t opcode);
int step();
// Mutable access to processor!!
virtual register16_t& AF() override {
return m_accumulatorFlags[m_accumulatorFlagsSet];
}
@ -133,9 +131,9 @@ namespace EightBit {
int8_t m_displacement;
bool m_displaced;
int fetchExecute() {
virtual int fetchExecute() override{
M1() = true;
return execute(fetchByte());
return IntelProcessor::fetchExecute();
}
uint8_t& DISPLACED() {

View File

@ -1152,7 +1152,7 @@ void EightBit::Z80::executeOther(int x, int y, int z, int p, int q) {
case 1: // 16-bit load immediate/add
switch (q) {
case 0: // LD rp,nn
fetchWord(RP(p));
Processor::fetchWord(RP(p));
cycles += 10;
break;
case 1: // ADD HL,rp

View File

@ -119,16 +119,6 @@ namespace EightBit {
return m_halfCarryTableSub[index & Mask3];
}
virtual uint8_t fetchByte() {
m_memory.ADDRESS().word = PC().word++;
return m_memory.reference();
}
void fetchWord(register16_t& output) {
output.low = fetchByte();
output.high = fetchByte();
}
virtual void push(uint8_t value) {
m_memory.ADDRESS().word = --SP().word;
m_memory.reference() = value;
@ -150,7 +140,7 @@ namespace EightBit {
}
void fetchWord() {
fetchWord(MEMPTR());
Processor::fetchWord(MEMPTR());
}
//

View File

@ -55,6 +55,8 @@ namespace EightBit {
void reset();
virtual int execute(uint8_t opcode) = 0;
protected:
static void clearFlag(uint8_t& f, int flag) { f &= ~flag; }
static void setFlag(uint8_t& f, int flag) { f |= flag; }
@ -72,6 +74,20 @@ namespace EightBit {
Memory& m_memory;
int cycles;
virtual uint8_t fetchByte() {
m_memory.ADDRESS().word = PC().word++;
return m_memory.reference();
}
virtual void fetchWord(register16_t& output) {
output.low = fetchByte();
output.high = fetchByte();
}
virtual int fetchExecute() {
return execute(fetchByte());
}
private:
register16_t pc;
bool m_halted;