From b877dfacd468a4be8ab51af36126b3ee2040b49a Mon Sep 17 00:00:00 2001 From: Michael McMaster Date: Thu, 19 Aug 2010 21:37:13 +1000 Subject: [PATCH] Still nowhere near finished, 2 years later. --- Core.cc | 83 +++++++++++++++++++++++++++++++++++ Core.hh | 49 +++++++++++++++++++++ Memory.cc | 66 ++++++++++++++++++++++++++++ Memory.hh | 66 ++++++++++++++++++++++++++++ README | 5 +++ Registers.cc | 50 +++++++++++++++++++++ Registers.hh | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++ mm80.hh | 35 +++++++++++++++ 8 files changed, 474 insertions(+) create mode 100755 Core.cc create mode 100755 Core.hh create mode 100644 Memory.cc create mode 100644 Memory.hh create mode 100644 README create mode 100755 Registers.cc create mode 100755 Registers.hh create mode 100644 mm80.hh diff --git a/Core.cc b/Core.cc new file mode 100755 index 0000000..32aa87c --- /dev/null +++ b/Core.cc @@ -0,0 +1,83 @@ +/** + * Emulated Z80 CPU. + * + * Incrementing elapsed cycles is performed separately from emulating the opcodes. + * to simplify the emulation. Cycle info could be embedded in the switch statement? + * eg. case X: doFoo; cycles += 7 + * + * Authors: Michael McMaster + * Copyright: Michael McMaster + */ +#include "bits/Z80.hh" +#include "Core.hh" + +using namespace Z80; + +Z80::Core(std::auto_ptr mem) : + m_iff1(0), + m_iff2(0), + m_mem(mem), +{ +} + + public void run(CycleCount maxCycles) + { + CycleCount elapsedCycles = 0; + + while (elapsedCycles < maxCycles) + { + op8_t opcode = op8(); + switch (opcode) + { + } + } + } + +op8_t Core::op8() +{ + return m_mem.read8(m_reg.PC++); +} + +op16_t Core::op16() +{ + op16_t operand = m_mem.read16(m_reg.PC); + m_reg.PC += 2; + return operand; +} + +void Core::reg8(op8_t opcode, u8_t value) +{ + // 00RRR000 + reg8(opcode >> 3) = value; +} + +reg8_t& Core::reg8(op8_t opcode) const +{ + // 00000RRR + switch (opcode & 0b00000111) + { + case 0: return m_reg.B; + case 1: return m_reg.C; + case 2: return m_reg.D; + case 3: return m_reg.E; + case 4: return m_reg.H; + case 5: return m_reg.L; + case 6: assert(0); FIXME + case 7: return m_reg.A; + } +} + +reg16_t& Core::dd(op8_t opcode) const +{ + // 00dd0000 + u8_t offset = (opcode & 0b00110000) >> 4; + return (offset == 3) ? m_reg.SP : *(m_reg.begin16 + offset); +} + +reg16_t& Core::qq(op8_t opcode) const +{ + // 00qq0000 + u8_t offset = (opcode & 0b00110000) >> 4; + return *(m_reg.begin16 + offset); +} + diff --git a/Core.hh b/Core.hh new file mode 100755 index 0000000..0dbbadb --- /dev/null +++ b/Core.hh @@ -0,0 +1,49 @@ +/** + * Emulated Z80 CPU. + * + * Incrementing elapsed cycles is performed separately from emulating the opcodes. + * to simplify the emulation. Cycle info could be embedded in the switch statement? + * eg. case X: doFoo; cycles += 7 + * + * Authors: Michael McMaster + * Copyright: Michael McMaster + */ +#pragma once + +#include "bits/Z80.hh" +#include "Clock.hh" + +#include + +namespace Z80 +{ + +class Core +{ +public: + Core(std::auto_ptr mem); + + cycle_t run(cycle_t maxCycles); + + bit getIFF2() const { return m_iff2; } + +private: + op8_t op8(); + op16_t op16(); + + void reg8(op8_t opcode, u8_t value); + reg8_t& reg8(op8_t opcode) const; + + reg16_t& dd(op8_t opcode) const; + reg16_t& qq(op8_t opcode) const; + + // The Z80 can switch between register sets. + Registers m_reg; + Registers m_regDash; + + // Interrupt enablement + bit m_iff1; + bit m_iff2; + + std::auto_ptr m_mem; +}; diff --git a/Memory.cc b/Memory.cc new file mode 100644 index 0000000..e54bd2d --- /dev/null +++ b/Memory.cc @@ -0,0 +1,66 @@ +/** + * Flat memory-model emulation. + * + * Authors: Michael McMaster + * Copyright: Michael McMaster + */ + +#include "bits/Z80.hh" +#include "ByteOrder.hh" +#include "Memory.hh" + +#include + +using namespace Z80; + +Memory::Memory(address_t initialSize) +{ + m_mem.resize(initialSize); +} + +u8_t Memory::read8(address_t address) +{ + return m_mem[address]; +} + +u8_t Memory::read8(address_t address, offset_t offset) +{ + return m_mem[address + offset]; +} + +void Memory::write8(address_t address, u8_t value) +{ + m_mem[address] = value; +} + +void Memory::write8(address_t address, offset_t offset, u8_t value) +{ + m_mem[address + offset] = value; +} + +u16_t Memory::read16(address_t address) +{ + return Z80ToHost(&m_mem[address]); +} + +u16_t Memory::read16(address_t address, offset_t offset) +{ + return Z80ToHost(&m_mem[address + offset]); +} + +void Memory::write16(address_t address, u16_t value) +{ + hostToZ80(value, &m_mem[address]); +} + +void Memory::write16(address_t address, offset_t offset, u16_t value) +{ + hostToZ80(value, &m_mem[address + offset]); +} + + +void Memory::copy(address_t src, address_t dst, address_t count) +{ + memcpy(&m_mem[dst], &m_mem[src], count); +} + diff --git a/Memory.hh b/Memory.hh new file mode 100644 index 0000000..e54bd2d --- /dev/null +++ b/Memory.hh @@ -0,0 +1,66 @@ +/** + * Flat memory-model emulation. + * + * Authors: Michael McMaster + * Copyright: Michael McMaster + */ + +#include "bits/Z80.hh" +#include "ByteOrder.hh" +#include "Memory.hh" + +#include + +using namespace Z80; + +Memory::Memory(address_t initialSize) +{ + m_mem.resize(initialSize); +} + +u8_t Memory::read8(address_t address) +{ + return m_mem[address]; +} + +u8_t Memory::read8(address_t address, offset_t offset) +{ + return m_mem[address + offset]; +} + +void Memory::write8(address_t address, u8_t value) +{ + m_mem[address] = value; +} + +void Memory::write8(address_t address, offset_t offset, u8_t value) +{ + m_mem[address + offset] = value; +} + +u16_t Memory::read16(address_t address) +{ + return Z80ToHost(&m_mem[address]); +} + +u16_t Memory::read16(address_t address, offset_t offset) +{ + return Z80ToHost(&m_mem[address + offset]); +} + +void Memory::write16(address_t address, u16_t value) +{ + hostToZ80(value, &m_mem[address]); +} + +void Memory::write16(address_t address, offset_t offset, u16_t value) +{ + hostToZ80(value, &m_mem[address + offset]); +} + + +void Memory::copy(address_t src, address_t dst, address_t count) +{ + memcpy(&m_mem[dst], &m_mem[src], count); +} + diff --git a/README b/README new file mode 100644 index 0000000..034c583 --- /dev/null +++ b/README @@ -0,0 +1,5 @@ +Third attempt at a Z80 CPU emulator. +email@michaelmcmaster.name 2008 + +Language choice: C++ + diff --git a/Registers.cc b/Registers.cc new file mode 100755 index 0000000..775d2af --- /dev/null +++ b/Registers.cc @@ -0,0 +1,50 @@ +/** + * Authors: Michael McMaster + * Copyright: Michael McMaster + */ +#include "bits/Z80.hh" +#include "Registers.hh" + +#include "Core.hh" + +using namespace Z80; + +Flags::Flags() : + C(0), + N(0), + P(0), + U3(0), + H(0), + U5(0), + Z(0), + S(0) +{ + // do nothing +} + +op8_t Flags::operator()(op8_t operand, const Core& context) +{ + S = operand < 0 ? 1 : 0; + Z = operand == 0 ? 1 : 0; + U5 = operand & 0b00010000 >> 4; + H = 0; + U3 = operand & 0b00000100 >> 2; + P = context.getIFF2(); + N = 0; + // C is not affected + return operand; +} + +Registers::Registers() : + AF(0), + BC(0), + DE(0), + HL(0), + I(0), + R(0), + IX(0), + IY(0), + SP(0), + PC(0) +{ +} diff --git a/Registers.hh b/Registers.hh new file mode 100755 index 0000000..6decfff --- /dev/null +++ b/Registers.hh @@ -0,0 +1,120 @@ +/** + * Emulated representation of the Z80 CPU registers. + * + * The Z80 register emulation allows efficient access to both the 8-bit , + * registers an the 16-bit register pairs. This is achieved by modifying the + * structure layout according to the endianess of the host. + * + * The Z80 cpu supports switching between multiple register sets. Each Register + * object only stores the details of a single register set. + * + * Authors: Michael McMaster + * Copyright: Michael McMaster + */ +#pragma once + +#include "bits/Z80.hh" + +namespace Z80 +{ + +class Core; + +/** 8-bit packed representation of the Z80 flags register. + * + * C Carry. LSB of Flag register + * N Add/Subtract + * P (V) Parity/Overflow + * U3 3rd bit of last 8bit op that altered flags + * H Half-Carry (BCD) + * U5 5th bit of last 8bit op that altered flags + * Z Zero Flag + * S Sign Flag. MSB of Flag register + */ +struct Flags +{ + unsigned C:1; /// Carry. LSB of Flag register + unsigned N:1; /// Add/Subtract + unsigned P:1; /// (V) Parity/Overflow + unsigned U3:1; /// 3rd bit of last 8bit op that altered flags + unsigned H:1; /// Half-Carry (BCD) + unsigned U5:1; /// 5th bit of last 8bit op that altered flags + unsigned Z:1; /// Zero Flag + unsigned S:1; /// Sign Flag. MSB of Flag register + + Flags(); + + // Update the flags based on an operands value (eg. for simple assignment) + op8_t operator()(op8_t operand, const Core& context); +}; + +/** Z80 register access. + * + * Registers may be accessed in either 8-bit (eg. A) + * or by their 16-bit pair (eg. AF) + * + */ +struct Registers +{ + Registers(); + + // Storage Order allows for 16 bit pairs to be accessed naturally. + // ie. AF, BC, DE, HL + // eg. AF = 0x1234, A will == 0x12. +#ifdef HOST_LITTLE_ENDIAN +#define Z80_REG_STRUCT(h,l) \ + struct \ + { \ + l; \ + h; \ + }; +#else +#define Z80_REG_STRUCT(h,l) \ + struct \ + { \ + h; \ + l; \ + }; +#endif + + // Note: The order of these unions/structs is important, as + // offsets are taken into the Register struct + reg8_t[0] begin8; + reg16_t[0] begin16; + + union + { + Z80_REG_STRUCT(reg8_t B, reg8_t C); + reg16_t BC; + }; + + union + { + Z80_REG_STRUCT(reg8_t D, reg8_t E); + reg16_t DE; + }; + + union + { + Z80_REG_STRUCT(reg8_t H, reg8_t L); + reg16_t HL; + }; + + union + { + Z80_REG_STRUCT(reg8_t A, Flags F,); + reg16_t AF; + }; + + +#undef Z80_REG_STRUCT + + // Special Purpose + reg8_t I; // Interrupt Vector + reg8_t R; // Memory Refresh + reg16_t IX; // Index Register + reg16_t IY; // Index Register + reg16_t SP; // Stack Pointer + reg16_t PC; // Program Counter +}; + diff --git a/mm80.hh b/mm80.hh new file mode 100644 index 0000000..0733195 --- /dev/null +++ b/mm80.hh @@ -0,0 +1,35 @@ +/** + * Z80 CPU emulator + * + * Authors: Michael McMaster + * Copyright: Michael McMaster + * + * Description goes here + */ +#pragma once + +#include + +namespace Z80 +{ + typedef uint8_t u8_t; + typedef uint16_t u16_t; + + typedef uint8_t op8_t; + typedef uint8_t op16_t; + + typedef uint8_t reg8_t; + typedef uint16_t reg16_t; + + typedef uint16_t address_t; + typedef int16_t offset_t; + + typedef bool bit; + + typedef uint64_t cycle_t; + + #if !(defined(HOST_LITTLE_ENDIAN) || defined(HOST_BIG_ENDIAN)) + #warn Assuming little-endian + #define HOST_LITTLE_ENDIAN + #endif +} -- 2.38.5