From: Michael McMaster Date: Thu, 19 Aug 2010 11:37:13 +0000 (+1000) Subject: Still nowhere near finished, 2 years later. X-Git-Tag: 1.0.0~10 X-Git-Url: http://git.codesrc.com/gitweb.cgi?a=commitdiff_plain;h=b877dfacd468a4be8ab51af36126b3ee2040b49a;p=glBoy.git Still nowhere near finished, 2 years later. --- b877dfacd468a4be8ab51af36126b3ee2040b49a 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 +}