]> localhost Git - glBoy.git/commitdiff
Oops, getting this git thing wrong
authorMichael McMaster <email@michaelmcmaster.name>
Mon, 31 Jan 2011 11:37:36 +0000 (21:37 +1000)
committerMichael McMaster <email@michaelmcmaster.name>
Mon, 31 Jan 2011 11:37:36 +0000 (21:37 +1000)
39 files changed:
ALU.cc
ALU.hh
Core.cc [changed mode: 0755->0644]
Core.hh [changed mode: 0755->0644]
DAA.hh
DWORD.hh
GameboyCart.cc
GameboyCart.hh
GameboyGraphics.cc
GameboyGraphics.hh
GameboyJoypad.cc
GameboyJoypad.hh
GameboySound.cc
GameboySound.hh
GameboyTimer.cc
GameboyTimer.hh
InstructionSet.opcode
Log.cc
Log.hh
Main.cc
Makefile
MemoryMap.cc
MemoryMap.hh
RAM.cc
RAM.hh
README
ROM.cc
ROM.hh
Registers.cc [changed mode: 0755->0644]
Registers.hh [changed mode: 0755->0644]
Resample.cc [deleted file]
Test.cc
glBoy.hh
hq4x.cc
hq4x.hh
makeOpcodes.pl
shaders/scale2x.slf
shaders/scale2x.slv
util.hh

diff --git a/ALU.cc b/ALU.cc
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..b8291f5d2e8d36eb320164d7c027f367c0b652a4 100644 (file)
--- a/ALU.cc
+++ b/ALU.cc
@@ -0,0 +1,42 @@
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of glBoy.
+//
+//     glBoy 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 3 of the License, or
+//     (at your option) any later version.
+//
+//     glBoy 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 glBoy.  If not, see <http://www.gnu.org/licenses/>.
+
+#include "ALU.hh"
+
+using namespace glBoy;
+
+void ALU::load() {}
+
+const uint8_t ALU::ParityFlagLookup[256] =
+{
+0x04, 0, 0, 0x04, 0, 0x04, 0x04, 0, 0, 0x04, 0x04, 0, 0x04, 0, 0, 0x04,
+0, 0x04, 0x04, 0, 0x04, 0, 0, 0x04, 0x04, 0, 0, 0x04, 0, 0x04, 0x04, 0,
+0, 0x04, 0x04, 0, 0x04, 0, 0, 0x04, 0x04, 0, 0, 0x04, 0, 0x04, 0x04, 0,
+0x04, 0, 0, 0x04, 0, 0x04, 0x04, 0, 0, 0x04, 0x04, 0, 0x04, 0, 0, 0x04,
+0, 0x04, 0x04, 0, 0x04, 0, 0, 0x04, 0x04, 0, 0, 0x04, 0, 0x04, 0x04, 0,
+0x04, 0, 0, 0x04, 0, 0x04, 0x04, 0, 0, 0x04, 0x04, 0, 0x04, 0, 0, 0x04,
+0x04, 0, 0, 0x04, 0, 0x04, 0x04, 0, 0, 0x04, 0x04, 0, 0x04, 0, 0, 0x04,
+0, 0x04, 0x04, 0, 0x04, 0, 0, 0x04, 0x04, 0, 0, 0x04, 0, 0x04, 0x04, 0,
+0, 0x04, 0x04, 0, 0x04, 0, 0, 0x04, 0x04, 0, 0, 0x04, 0, 0x04, 0x04, 0,
+0x04, 0, 0, 0x04, 0, 0x04, 0x04, 0, 0, 0x04, 0x04, 0, 0x04, 0, 0, 0x04,
+0x04, 0, 0, 0x04, 0, 0x04, 0x04, 0, 0, 0x04, 0x04, 0, 0x04, 0, 0, 0x04,
+0, 0x04, 0x04, 0, 0x04, 0, 0, 0x04, 0x04, 0, 0, 0x04, 0, 0x04, 0x04, 0,
+0x04, 0, 0, 0x04, 0, 0x04, 0x04, 0, 0, 0x04, 0x04, 0, 0x04, 0, 0, 0x04,
+0, 0x04, 0x04, 0, 0x04, 0, 0, 0x04, 0x04, 0, 0, 0x04, 0, 0x04, 0x04, 0,
+0, 0x04, 0x04, 0, 0x04, 0, 0, 0x04, 0x04, 0, 0, 0x04, 0, 0x04, 0x04, 0,
+0x04, 0, 0, 0x04, 0, 0x04, 0x04, 0, 0, 0x04, 0x04, 0, 0x04, 0, 0, 0x04
+};
diff --git a/ALU.hh b/ALU.hh
index a59402eadff3b7939227f11234f472bb3be21400..aa51fda24e0781f4cdfaa4c68cc032834c1ccde7 100755 (executable)
--- a/ALU.hh
+++ b/ALU.hh
-<?xml version="1.0" encoding="UTF-8" ?>\r
-<opcodes>\r
-<!--\r
-       Z80 Core Emulation\r
-       Copyright 2010, Michael McMaster <email@michaelmcmaster.name>\r
--->\r
-\r
-<!--\r
-code gen expectations:\r
-masks result in each instruction being unrolled and the mask name being replaced\r
-with an actual register\r
-operand = op8();\r
-operand="UPPERCASE" is 16bit operand, in NATIVE(x86) byte order, but read in little-endian order from instruction pointer. N = op16(). ACTUALLY, NO. Return a struct DWORD, and enforce constraints!\r
--->\r
-\r
-<registerMask name="r" bits="3">\r
-       <reg name="A" mask="111" />\r
-       <reg name="B" mask="000" />\r
-       <reg name="C" mask="001" />\r
-       <reg name="D" mask="010" />\r
-       <reg name="E" mask="011" />\r
-       <reg name="H" mask="100" />\r
-       <reg name="L" mask="101" />\r
-</registerMask>\r
-\r
-<registerMask name="d" alias="s" bits="2">\r
-       <reg name="BC" mask="00" />\r
-       <reg name="DE" mask="01" />\r
-       <reg name="HL" mask="10" />\r
-       <reg name="SP" mask="11" />\r
-</registerMask>\r
-\r
-<registerMask name="q" bits="2">\r
-       <reg name="BC" mask="00" />\r
-       <reg name="DE" mask="01" />\r
-       <reg name="HL" mask="10" />\r
-       <reg name="AF" mask="11" />\r
-</registerMask>\r
-\r
-<registerMask name="p" bits="2">\r
-       <reg name="BC" mask="00" />\r
-       <reg name="DE" mask="01" />\r
-       <reg name="IX" mask="10" />\r
-       <reg name="SP" mask="11" />\r
-</registerMask>\r
-\r
-<registerMask name="R" bits="2">\r
-       <reg name="BC" mask="00" />\r
-       <reg name="DE" mask="01" />\r
-       <reg name="IY" mask="10" />\r
-       <reg name="SP" mask="11" />\r
-</registerMask>\r
-\r
-<!--\r
-                               8-BIT LD Instructions\r
--->\r
-\r
-<instruction name="LD r,r'" clock="4">\r
-<opcode mask="01rr" />\r
-r = r2;\r
-</instruction>\r
-\r
-<instruction name="LD r,n" clock="7">\r
-<opcode mask="00r110" operand="n"/>\r
-r = n;\r
-</instruction>\r
-\r
-<instruction name="LD r,(HL)" clock="7">\r
-<opcode mask="01r110" />\r
-r = m_mem.read8(m_reg.HL);\r
-</instruction>\r
-\r
-<instruction name="LD r,(IX+d)" clock="19">\r
-<opcode prefix="0xDD" mask="01r110" signed_operand="d"/>\r
-r = m_mem.read8(m_reg.IX, d);\r
-</instruction>\r
-\r
-<instruction name="LD r,(IY+d)" clock="19">\r
-<opcode prefix="0xFD" mask="01r110" signed_operand="d"/>\r
-r = m_mem.read8(m_reg.IY, d);\r
-</instruction>\r
-\r
-<instruction name="LD (HL),r" clock="7">\r
-<opcode mask="01110r"/>\r
-m_mem.write8(m_reg.HL, r);\r
-</instruction>\r
-\r
-<instruction name="LD (IX+d),r" clock="19">\r
-<opcode prefix="0xDD" mask="01110r" signed_operand="d"/>\r
-m_mem.write8(m_reg.IX, d, r);\r
-</instruction>\r
-\r
-<instruction name="LD (IY+d),r" clock="19">\r
-<opcode prefix="0xFD" mask="01110r" signed_operand="d"/>\r
-m_mem.write8(m_reg.IY, d, r);\r
-</instruction>\r
-\r
-<instruction name="LD (HL),n" clock="10">\r
-<opcode mask="00110110" operand="n"/>\r
-m_mem.write8(m_reg.HL, n);\r
-</instruction>\r
-\r
-<instruction name="LD (IX+d),r" clock="19">\r
-<opcode prefix="0xDD" mask="00110110" signed_operand="d" operand="n"/>\r
-m_mem.write8(m_reg.IX, d, n);\r
-</instruction>\r
-\r
-<instruction name="LD (IY+d),n" clock="19">\r
-<opcode prefix="0xFD" mask="00110110" signed_operand="d" operand="n"/>\r
-m_mem.write8(m_reg.IY, d, n);\r
-</instruction>\r
-\r
-<instruction name="LD A,(BC)" clock="7">\r
-<opcode mask="00001010"/>\r
-m_reg.A = m_mem.read8(m_reg.BC);\r
-</instruction>\r
-\r
-<instruction name="LD A,(DE)" clock="7">\r
-<opcode mask="00011010"/>\r
-m_reg.A = m_mem.read8(m_reg.DE);\r
-</instruction>\r
-\r
-<instruction name="LD A,(nn)" clock="13">\r
-<opcode mask="00111010" operand="N"/>\r
-m_reg.A = m_mem.read8(N);\r
-</instruction>\r
-\r
-<instruction name="LD (BC),A" clock="7">\r
-<opcode mask="00000010"/>\r
-m_mem.write8(m_reg.BC, m_reg.A);\r
-</instruction>\r
-\r
-<instruction name="LD (DE),A" clock="7">\r
-<opcode mask="00010010"/>\r
-m_mem.write8(m_reg.DE, m_reg.A);\r
-</instruction>\r
-\r
-<instruction name="LD (nn),A" clock="13">\r
-<opcode mask="00110010" operand="N"/>\r
-m_mem.write8(N, m_reg.A);\r
-</instruction>\r
-\r
-<instruction name="LD A,I" clock="9">\r
-<opcode prefix="0xED" mask="01010111"/>\r
-m_reg.F.set(m_reg.I, *this);\r
-m_reg.A = m_reg.I;\r
-</instruction>\r
-\r
-<instruction name="LD A,R" clock="9">\r
-<opcode prefix="0xED" mask="01011111"/>\r
-m_reg.F.set(m_reg.R, *this);\r
-m_reg.A = m_reg.R;\r
-</instruction>\r
-\r
-<instruction name="LD I,A" clock="9">\r
-<opcode prefix="0xED" mask="01000111"/>\r
-m_reg.I = m_reg.A;\r
-</instruction>\r
-\r
-<instruction name="LD R,A" clock="9">\r
-<opcode prefix="0xED" mask="01001111"/>\r
-m_reg.R = m_reg.A;\r
-</instruction>\r
-\r
-<!--\r
-                               16-BIT LD Instructions\r
--->\r
-\r
-<instruction name="LD dd,nn" clock="10">\r
-<opcode mask="00d0001" operand="N" />\r
-d = N;\r
-</instruction>\r
-\r
-<instruction name="LD IX,nn" clock="14">\r
-<opcode prefix="0xDD" mask="00100001" operand="N" />\r
-m_reg.IX = N;\r
-</instruction>\r
-\r
-<instruction name="LD IY,nn" clock="14">\r
-<opcode prefix="0xFD" mask="00100001" operand="N" />\r
-m_reg.IY = N;\r
-</instruction>\r
-\r
-<instruction name="LD HL,(nn)" clock="16">\r
-<opcode mask="00101010" operand="N" />\r
-m_reg.HL = m_mem.read16(N);\r
-</instruction>\r
-\r
-<instruction name="LD dd,(nn)" clock="20">\r
-<opcode prefix="0xED" mask="01d1011" operand="N" />\r
-d = m_mem.read16(N);\r
-</instruction>\r
-\r
-<instruction name="LD IX,(nn)" clock="20">\r
-<opcode prefix="0xDD" mask="00101010" operand="N" />\r
-m_reg.IX = m_mem.read16(N);\r
-</instruction>\r
-\r
-<instruction name="LD IY,(nn)" clock="20">\r
-<opcode prefix="0xFD" mask="00101010" operand="N" />\r
-m_reg.IY = m_mem.read16(N);\r
-</instruction>\r
-\r
-<instruction name="LD (nn),HL" clock="16">\r
-<opcode mask="00100010" operand="N" />\r
-m_mem.write16(N, m_reg.HL);\r
-</instruction>\r
-\r
-<instruction name="LD (nn),dd" clock="20">\r
-<opcode prefix="0xED" mask="01d0011" operand="N" />\r
-m_mem.write16(N, d);\r
-</instruction>\r
-\r
-<instruction name="LD (nn),IX" clock="20">\r
-<opcode prefix="0xDD" mask="00100010" operand="N" />\r
-m_mem.write16(N, m_reg.IX);\r
-</instruction>\r
-\r
-<instruction name="LD (nn),IY" clock="20">\r
-<opcode prefix="0xFD" mask="00100010" operand="N" />\r
-m_mem.write16(N, m_reg.IY);\r
-</instruction>\r
-\r
-<instruction name="LD SP,HL" clock="6">\r
-<opcode mask="11111001" />\r
-m_reg.SP = m_reg.HL;\r
-</instruction>\r
-\r
-<instruction name="LD SP,IX" clock="10">\r
-<opcode prefix="0xDD" mask="11111001" />\r
-m_reg.SP = m_reg.IX;\r
-</instruction>\r
-\r
-<instruction name="LD SP,IY" clock="10">\r
-<opcode prefix="0xFD" mask="11111001" />\r
-m_reg.SP = m_reg.IY;\r
-</instruction>\r
-\r
-<instruction name="push qq" clock="11">\r
-<opcode mask="11q0101" />\r
-m_reg.SP.dec(2);\r
-m_mem.write16(m_reg.SP, q);\r
-</instruction>\r
-\r
-<instruction name="push IX" clock="15">\r
-<opcode prefix="0xDD" mask="11100101" />\r
-m_reg.SP.dec(2);\r
-m_mem.write16(m_reg.SP, m_reg.IX);\r
-</instruction>\r
-\r
-<instruction name="push IY" clock="15">\r
-<opcode prefix="0xFD" mask="11100101" />\r
-m_reg.SP.dec(2);\r
-m_mem.write16(m_reg.SP, m_reg.IY);\r
-</instruction>\r
-\r
-<instruction name="pop qq" clock="10">\r
-<opcode mask="11q0001" />\r
-q = m_mem.read16(m_reg.SP);\r
-m_reg.SP.inc(2);\r
-</instruction>\r
-\r
-<instruction name="pop IX" clock="14">\r
-<opcode prefix="0xDD" mask="11100001" />\r
-m_reg.IX = m_mem.read16(m_reg.SP);\r
-m_reg.SP.inc(2);\r
-</instruction>\r
-\r
-<instruction name="pop IY" clock="14">\r
-<opcode prefix="0xDD" mask="11111101" />\r
-m_reg.IY = m_mem.read16(m_reg.SP);\r
-m_reg.SP.inc(2);;\r
-</instruction>\r
-\r
-\r
-<!--\r
-                               Exchange, block transfer, and search\r
--->\r
-\r
-<instruction name="EX DE,HL" clock="4">\r
-<opcode mask="111010111" />\r
-       DWORD tmp(m_reg.DE);\r
-       m_reg.DE = m_reg.HL;\r
-       m_reg.HL = tmp;\r
-</instruction>\r
-\r
-<instruction name="EX AF,AF'" clock="4">\r
-<opcode mask="111010111" />\r
-       DWORD tmp(m_reg.AF);\r
-       m_reg.AF = m_regDash.AF;\r
-       m_regDash.AF = tmp;\r
-</instruction>\r
-\r
-<instruction name="EXX'" clock="4">\r
-<opcode mask="11011000" />\r
-       DWORD tmpBC(m_reg.BC);\r
-       DWORD tmpDE(m_reg.BC);\r
-       DWORD tmpHL(m_reg.BC);\r
-       m_reg.BC = m_regDash.BC;\r
-       m_reg.DE = m_regDash.DE;\r
-       m_reg.HL = m_regDash.HL;\r
-       m_regDash.BC = tmpBC;\r
-       m_regDash.DE = tmpDE;\r
-       m_regDash.HL = tmpHL;\r
-</instruction>\r
-\r
-<instruction name="EX (SP),HL'" clock="19">\r
-<opcode mask="11100011" />\r
-       DWORD tmp(m_reg.HL);\r
-       m_reg.HL = m_mem.read16(m_reg.SP);\r
-       m_mem.write16(m_reg.SP, tmp);\r
-</instruction>\r
-\r
-<instruction name="EX (SP),IX'" clock="23">\r
-<opcode prefix="0xDD" mask="11100011" />\r
-       DWORD tmp(m_reg.IX);\r
-       m_reg.IX = m_mem.read16(m_reg.SP);\r
-       m_mem.write16(m_reg.SP, tmp);\r
-</instruction>\r
-\r
-<instruction name="EX (SP),IY'" clock="23">\r
-<opcode prefix="0xFD" mask="11100011" />\r
-       DWORD tmp(m_reg.IY);\r
-       m_reg.IY = m_mem.read16(m_reg.SP);\r
-       m_mem.write16(m_reg.SP, tmp);\r
-</instruction>\r
-\r
-<instruction name="LDI'" clock="16">\r
-<opcode prefix="0xED" mask="10100000" />\r
-       m_mem.write8(m_reg.DE, m_mem.read8(m_reg.HL));\r
-       m_reg.DE.inc();\r
-       m_reg.HL.inc();\r
-       m_reg.BC.dec();\r
-       m_reg.F.setCounter(m_reg.BC);\r
-</instruction>\r
-\r
-<instruction name="LDIR'" clock="0">\r
-<opcode prefix="0xED" mask="10110000" />\r
-       do\r
-       {\r
-               m_mem.write8(m_reg.DE, m_mem.read8(m_reg.HL));\r
-               m_reg.DE.inc();\r
-               m_reg.HL.inc();\r
-               m_reg.BC.dec();\r
-\r
-               clock += 21; // Increment for each loop iteration\r
-               interrupt(); // Check for interrupts at each iteration\r
-       } while (m_reg.BC.host() != 0);\r
-       m_reg.F.setCounter(m_reg.BC);\r
-       clock += 16; // Increment again when leaving loop.\r
-</instruction>\r
-\r
-<instruction name="LDD'" clock="16">\r
-<opcode prefix="0xED" mask="10101000" />\r
-       m_mem.write8(m_reg.DE, m_mem.read8(m_reg.HL));\r
-       m_reg.DE.dec();\r
-       m_reg.HL.dec();\r
-       m_reg.BC.dec();\r
-       m_reg.F.setCounter(m_reg.BC);\r
-</instruction>\r
-\r
-<instruction name="LDDR'" clock="0">\r
-<opcode prefix="0xED" mask="10111000" />\r
-       do\r
-       {\r
-               m_mem.write8(m_reg.DE, m_mem.read8(m_reg.HL));\r
-               m_reg.DE.dec();\r
-               m_reg.HL.dec();\r
-               m_reg.BC.dec();\r
-\r
-               clock += 21; // Increment for each loop iteration\r
-               interrupt(); // Check for interrupts at each iteration\r
-       } while (m_reg.BC.host() != 0);\r
-       m_reg.F.setCounter(m_reg.BC);\r
-       clock += 16; // Increment again when leaving loop.\r
-</instruction>\r
-\r
-<instruction name="CPI'" clock="16">\r
-<opcode prefix="0xED" mask="10100000" />\r
-       m_reg.BC.dec();\r
-       m_reg.F.setSub(m_reg.A, m_reg.read8(r_reg.HL), m_reg.BC);\r
-       m_reg.HL.inc();\r
-</instruction>\r
-\r
-<instruction name="CPIR'" clock="0">\r
-<opcode prefix="0xED" mask="10110001" />\r
-       bool equal;\r
-       do\r
-       {\r
-               m_reg.BC.dec();\r
-               m_reg.F.setSub(m_reg.A, m_reg.read8(r_reg.HL), m_reg.BC);\r
-               equal = (m_reg.A == m_reg.read8(r_reg.HL));\r
-               m_reg.HL.inc();\r
-\r
-               clock += 21; // Increment for each loop iteration\r
-               interrupt(); // Check for interrupts at each iteration\r
-       } while (m_reg.BC.host() != 0 && !equal);\r
-       clock += 16; // Increment again when leaving loop.\r
-\r
-</instruction>\r
-\r
-<instruction name="CPD'" clock="16">\r
-<opcode prefix="0xED" mask="10101001" />\r
-       m_reg.BC.dec();\r
-       m_reg.F.setSub(m_reg.A, m_reg.read8(r_reg.HL), m_reg.BC);\r
-       m_reg.HL.dec();\r
-</instruction>\r
-\r
-<instruction name="CPDR'" clock="0">\r
-<opcode prefix="0xED" mask="10111001" />\r
-       bool equal;\r
-       do\r
-       {\r
-               m_reg.BC.dec();\r
-               m_reg.F.setSub(m_reg.A, m_reg.read8(r_reg.HL), m_reg.BC);\r
-               equal = (m_reg.A == m_reg.read8(r_reg.HL));\r
-               m_reg.HL.dec();\r
-\r
-               clock += 21; // Increment for each loop iteration\r
-               interrupt(); // Check for interrupts at each iteration\r
-       } while (m_reg.BC.host() != 0 && !equal);\r
-       clock += 16; // Increment again when leaving loop.\r
-\r
-</instruction>\r
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>\r
+//\r
+//     This file is part of glBoy.\r
+//\r
+//     glBoy is free software: you can redistribute it and/or modify\r
+//     it under the terms of the GNU General Public License as published by\r
+//     the Free Software Foundation, either version 3 of the License, or\r
+//     (at your option) any later version.\r
+//\r
+//     glBoy is distributed in the hope that it will be useful,\r
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+//     GNU General Public License for more details.\r
+//\r
+//     You should have received a copy of the GNU General Public License\r
+//     along with glBoy.  If not, see <http://www.gnu.org/licenses/>.\r
+\r
+#ifndef GLBOY_ALU_HH\r
+#define GLBOY_ALU_HH\r
+\r
+#include "glBoy.hh"\r
+#include "DWORD.hh"\r
+#include "Registers.hh"\r
+\r
+namespace glBoy\r
+{\r
+namespace ALU\r
+{\r
+void load();\r
+extern const uint8_t ParityFlagLookup[256];\r
 \r
-op8_t doAdd8(op8_t a, op8_t b, Flags& flags, bool carry)\r
+inline uint8_t\r
+add(uint8_t a, uint8_t b, Flags& flags, bool carry = false)\r
 {\r
-       op8_t result(a + b);\r
+       uint8_t result(a + b);\r
 \r
        if (carry && flags.C)\r
        {\r
@@ -449,39 +56,309 @@ op8_t doAdd8(op8_t a, op8_t b, Flags& flags, bool carry)
                ((((~(a ^ b)) & (result ^ a)) & 0x80) >> 5) |// P\r
 \r
                                                                                                // N\r
-               (((op16_t(a) + op16_t(b)) & 0x100) >> 8) // C\r
+               (((uint16_t(a) + uint16_t(b)) & 0x100) >> 8); // C\r
+\r
+       return result;\r
+}\r
+\r
+inline uint8_t\r
+adc(uint8_t a, uint8_t b, Flags& flags)\r
+{\r
+       return add(a, b, flags, true);\r
+}\r
+\r
+inline DWORD\r
+add(DWORD a, DWORD b, Flags& flags, bool carry = false)\r
+{\r
+       const uint16_t aHost(a.host());\r
+       const uint16_t bHost(b.host());\r
+\r
+       uint16_t result(aHost + bHost);\r
+\r
+       if (carry && flags.C)\r
+       {\r
+               ++result;\r
+       }\r
+\r
+       if (carry)\r
+       {\r
+               // Flags only affected for adc.\r
+               flags.S = (result & 0x8000) ? 1 : 0;\r
+               flags.Z = (result == 0);\r
+               flags.P = (((~(aHost ^ bHost)) & (result ^ aHost)) & 0x8000) ? 1 : 0;\r
+\r
+       }\r
+\r
+       flags.N = 0;\r
+       flags.H = ((aHost ^ bHost ^ result) >> 10) & 0x1;\r
+       flags.C = (((uint32_t(aHost) + uint32_t(bHost)) & 0x10000) >> 16);\r
+\r
+       return glBoy::htoz(result);\r
+}\r
+\r
+inline DWORD\r
+adc(DWORD a, DWORD b, Flags& flags)\r
+{\r
+       return add(a, b, flags, true);\r
+}\r
+\r
+inline uint8_t\r
+sub(uint8_t a, uint8_t b, Flags& flags, bool borrow = false)\r
+{\r
+       uint8_t c = (borrow && flags.C) ? 1 : 0;\r
+       uint8_t result(a - b - c);\r
+\r
+       flags.byte =\r
+               (result & 0x80) |                       // S  MSB\r
+               ((result == 0) ? 0x40 : 0) |            // Z\r
+                                                                                               // U5  TODO\r
+               (((a & 0xf) - (b & 0xf)) & 0x10) |      // H\r
+                                                                                               // U3 TODO\r
+\r
+               // Detected signed overflow\r
+               ((int16_t(static_cast<int8_t>(a)) -\r
+                       int16_t(static_cast<int8_t>(b)) -\r
+                       c\r
+                       ) ==\r
+                       static_cast<int8_t>(result) ? 0 : 0x4) | // P\r
+\r
+               0x2 |                                                                           // N\r
+               (((uint16_t(a) - uint16_t(b)) & 0x100) >> 8); // C\r
+\r
+       return result;\r
+}\r
+\r
+inline uint8_t\r
+sbc(uint8_t a, uint8_t b, Flags& flags)\r
+{\r
+       return sub(a, b, flags, true);\r
+}\r
+\r
+inline DWORD\r
+sub(DWORD a, DWORD b, Flags& flags, bool borrow = false)\r
+{\r
+       const uint16_t aHost(a.host());\r
+       const uint16_t bHost(b.host());\r
+\r
+       uint16_t c = (borrow && flags.C) ? 1 : 0;\r
+       uint16_t result(aHost - bHost - c);\r
+\r
+       flags.byte =\r
+               ((result & 0x8000) >> 8) |        // S  MSB\r
+               ((result == 0) ? 0x40 : 0) |            // Z\r
+                                                                                               // U5  TODO\r
+               ((((aHost & 0xf00) - (bHost & 0xf00)) & 0x1000) >> 8) | // H\r
+                                                                                               // U3 TODO\r
+\r
+               // Detected signed overflow\r
+               ((int32_t(static_cast<int16_t>(aHost)) -\r
+                       int32_t(static_cast<int16_t>(bHost)) -\r
+                       c\r
+                       ) ==\r
+                       static_cast<int16_t>(result) ? 0 : 0x4) | // P\r
+\r
+               0x2 |                                                                           // N\r
+               (((uint32_t(aHost) - uint32_t(bHost)) & 0x10000) >> 16); // C\r
+\r
+       return htoz(result);\r
+}\r
+\r
+inline DWORD\r
+sbc(DWORD a, DWORD b, Flags& flags)\r
+{\r
+       return sub(a, b, flags, true);\r
 }\r
-<!--\r
-                               8-bit arithmetic\r
--->\r
-<instruction name="ADD A,r'" clock="4">\r
-<opcode mask="100000r" />\r
-       m_reg.A = doAdd8(m_reg.A, r, m_reg.F, false);\r
-</instruction>\r
-\r
-<instruction name="ADD A,n'" clock="7">\r
-<opcode mask="11000110" operand="n" />\r
-       m_reg.A = doAdd8(m_reg.A, n, m_reg.F, false);\r
-</instruction>\r
-\r
-<instruction name="ADD A,(HL)'" clock="7">\r
-<opcode mask="11000110" />\r
-       m_reg.A = doAdd8(m_reg.A, m_mem.read8(m_reg.HL), m_reg.F, false);\r
-</instruction>\r
-\r
-<instruction name="ADD A,(IX+d)'" clock="19">\r
-<opcode prefix="0xDD" mask="11011101" signed_operand="d" />\r
-       m_reg.A = doAdd8(m_reg.A, m_mem.read8(m_reg.IX, d), m_reg.F, false);\r
-</instruction>\r
-\r
-<instruction name="ADD A,(IY+d)'" clock="19">\r
-<opcode prefix="0xFD" mask="10000110" signed_operand="d" />\r
-       m_reg.A = doAdd8(m_reg.A, m_mem.read8(m_reg.IY, d), m_reg.F, false);\r
-</instruction>\r
-\r
-<instruction name="ADC A,r'" clock="4">\r
-<opcode mask="10001r" />\r
-       m_reg.A = doAdc8(m_reg.A, r, m_reg.F, true);\r
-</instruction>\r
-\r
-</opcodes>\r
+\r
+inline uint8_t\r
+bitwiseAnd(uint8_t a, uint8_t b, Flags& flags)\r
+{\r
+       uint8_t result(a & b);\r
+       flags.byte =\r
+               (result & 0x80) | // S\r
+               ((result == 0) ? 0x40 : 0) |            // Z\r
+                               // U5\r
+               0x10 | // H\r
+                               // U3\r
+               ParityFlagLookup[result]; // P\r
+                       // N\r
+                       // C\r
+       return result;\r
+}\r
+\r
+inline uint8_t\r
+bitwiseOr(uint8_t a, uint8_t b, Flags& flags)\r
+{\r
+       uint8_t result(a | b);\r
+       flags.byte =\r
+               (result & 0x80) | // S\r
+               ((result == 0) ? 0x40 : 0) |            // Z\r
+                               // U5\r
+                               // H\r
+                               // U3\r
+               ParityFlagLookup[result]; // P\r
+                       // N\r
+                       // C\r
+       return result;\r
+}\r
+\r
+inline uint8_t\r
+bitwiseXor(uint8_t a, uint8_t b, Flags& flags)\r
+{\r
+       uint8_t result(a ^ b);\r
+       flags.byte =\r
+               (result & 0x80) | // S\r
+               ((result == 0) ? 0x40 : 0) |            // Z\r
+                               // U5\r
+                               // H\r
+                               // U3\r
+               ParityFlagLookup[result]; // P\r
+                       // N\r
+                       // C\r
+       return result;\r
+}\r
+\r
+inline uint8_t\r
+rotateLeft(uint8_t a, Flags& flags, bool throughCarry, bool setAllFlags = false)\r
+{\r
+       uint8_t msb(a >> 7);\r
+       a <<= 1;\r
+\r
+       if (throughCarry)\r
+       {\r
+               a |= flags.C;\r
+       }\r
+       else\r
+       {\r
+               a |= msb;\r
+       }\r
+       flags.H = 0;\r
+       flags.N = 0;\r
+       flags.C = msb;\r
+\r
+       if (setAllFlags)\r
+       {\r
+               flags.byte |= (a & 0x80);\r
+               flags.Z = (a == 0) ? 1 : 0;\r
+               flags.byte |= ParityFlagLookup[a];\r
+       }\r
+// TODO U3 U5\r
+\r
+       return a;\r
+}\r
+\r
+inline uint8_t\r
+shiftLeft(uint8_t a, Flags& flags)\r
+{\r
+       uint8_t msb(a >> 7);\r
+       a <<= 1;\r
+\r
+       flags.byte =\r
+               (a & 0x80) | // S\r
+               ((a == 0) ? 0x40 : 0) | // Z\r
+               // U5\r
+               // H\r
+               // U3\r
+               ParityFlagLookup[a] | // P\r
+               // N\r
+               msb; // C\r
+\r
+       return a;\r
+}\r
+\r
+inline uint8_t\r
+rotateRight(uint8_t a, Flags& flags, bool throughCarry, bool setAllFlags = false)\r
+{\r
+       uint8_t lsb(a & 0x1);\r
+       a >>= 1;\r
+\r
+       if (throughCarry)\r
+       {\r
+               a |= flags.C ? 0x80 : 0;\r
+       }\r
+       else\r
+       {\r
+               a |= lsb ? 0x80 : 0;\r
+       }\r
+       flags.H = 0;\r
+       flags.N = 0;\r
+       flags.C = lsb;\r
+\r
+       if (setAllFlags)\r
+       {\r
+               flags.byte |= (a & 0x80);\r
+               flags.Z = (a == 0) ? 1 : 0;\r
+               flags.byte |= ParityFlagLookup[a];\r
+       }\r
+// TODO U3 U5\r
+\r
+       return a;\r
+}\r
+\r
+inline uint8_t\r
+shiftRight(uint8_t a, Flags& flags, bool arithmatic)\r
+{\r
+       uint8_t lsb(a & 0x1);\r
+       uint8_t msb(a & 0x80);\r
+       a >>= 1;\r
+\r
+       // Restore sign bit\r
+       if (arithmatic) a |= msb;\r
+\r
+       flags.byte =\r
+               (a & 0x80) | // S\r
+               ((a == 0) ? 0x40 : 0) | // Z\r
+               // U5\r
+               // H\r
+               // U3\r
+               ParityFlagLookup[a] | // P\r
+               // N\r
+               lsb; // C\r
+\r
+       return a;\r
+}\r
+\r
+inline void\r
+RLD(uint8_t& a, uint8_t& m, Flags& f)\r
+{\r
+       // Whoever came up with RLD was on crack.\r
+\r
+       uint8_t newM = (a & 0xf) | (m << 4);\r
+       a = (a & 0xF0) | (m >> 4);\r
+       m = newM;\r
+\r
+       f.byte =\r
+               (a & 0x80) | // S\r
+               ((a == 0) ? 0x40 : 0) | // Z\r
+               // U5\r
+               // H\r
+               // U3\r
+               ParityFlagLookup[a]; // P\r
+               // N\r
+               // C\r
+}\r
+\r
+inline void\r
+RRD(uint8_t& a, uint8_t& m, Flags& f)\r
+{\r
+       // Whoever came up with RRD was on crack.\r
+\r
+       uint8_t newM = (a << 4) | (m >> 4);\r
+       a = (a & 0xF0) | (m & 0xf);\r
+       m = newM;\r
+\r
+       f.byte =\r
+               (a & 0x80) | // S\r
+               ((a == 0) ? 0x40 : 0) | // Z\r
+               // U5\r
+               // H\r
+               // U3\r
+               ParityFlagLookup[a]; // P\r
+               // N\r
+               // C\r
+}\r
+\r
+} // namespace ALU\r
+\r
+} // namespace m80\r
+#endif\r
+\r
diff --git a/Core.cc b/Core.cc
old mode 100755 (executable)
new mode 100644 (file)
index 32aa87c..9e8224e
--- a/Core.cc
+++ b/Core.cc
-/** \r
- * Emulated Z80 CPU.\r
- *\r
- * Incrementing elapsed cycles is performed separately from emulating the opcodes.\r
- * to simplify the emulation.  Cycle info could be embedded in the switch statement?\r
- * eg. case X: doFoo; cycles += 7\r
- *\r
- * Authors: Michael McMaster <email@michaelmcmaster.name>\r
- * Copyright: Michael McMaster <email@michaelmcmaster.name>\r
- */ \r
-#include "bits/Z80.hh"\r
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>\r
+//\r
+//     This file is part of glBoy.\r
+//\r
+//     glBoy is free software: you can redistribute it and/or modify\r
+//     it under the terms of the GNU General Public License as published by\r
+//     the Free Software Foundation, either version 3 of the License, or\r
+//     (at your option) any later version.\r
+//\r
+//     glBoy is distributed in the hope that it will be useful,\r
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+//     GNU General Public License for more details.\r
+//\r
+//     You should have received a copy of the GNU General Public License\r
+//     along with glBoy.  If not, see <http://www.gnu.org/licenses/>.\r
+\r
+#include "glBoy.hh"\r
 #include "Core.hh"\r
+#include "ALU.hh"\r
+#include "DAA.hh"\r
+#include "Log.hh"\r
+#include "util.hh"\r
+\r
+#include <cassert>\r
+#include <limits>\r
+#include <sstream>\r
 \r
-using namespace Z80;\r
+using namespace glBoy;\r
 \r
-Z80::Core(std::auto_ptr<Memory> mem) :\r
+Core::Core() :\r
        m_iff1(0),\r
        m_iff2(0),\r
-       m_mem(mem),\r
+       m_halted(false),\r
+       m_clock(0),\r
+       m_nextPeriodicCallback(std::numeric_limits<cycle_t>::max())\r
 {\r
+       m_reg.reset();\r
+       ALU::load();\r
 }\r
 \r
-  public void run(CycleCount maxCycles)\r
-  {\r
-    CycleCount elapsedCycles = 0;\r
+void\r
+Core::raiseInterrupt(int interrupt)\r
+{\r
+       if (m_iff1 != 0) // Are interrupts enabled ?\r
+       {\r
+               // TODO this interrupt handling is GB80 specific\r
+               bool enabled(false);\r
 \r
-    while (elapsedCycles < maxCycles)\r
-    {\r
-      op8_t opcode = op8();\r
-      switch (opcode)\r
-      {\r
-      }\r
-    }\r
-  }\r
+               uint8_t flags(m_mem.read8(htoz(0xFFFF)));\r
+               uint8_t outFlags(m_mem.read8(htoz(0xFF0F)));\r
 \r
-op8_t Core::op8()\r
-{\r
-       return m_mem.read8(m_reg.PC++);\r
+               switch (interrupt)\r
+               {\r
+               case 0x40: enabled = flags & 0x1; outFlags |= 0x1; break;\r
+               case 0x48: enabled = flags & 0x2; outFlags |= 0x2; break;\r
+               case 0x50: enabled = flags & 0x4; outFlags |= 0x4; break;\r
+               case 0x58: enabled = flags & 0x8; outFlags |= 0x8; break;\r
+               case 0x60: enabled = flags & 0x10; outFlags += 0x10; break;\r
+               }\r
+               if (enabled)\r
+               {\r
+                       m_interruptQueue.push_back(interrupt);\r
+               }\r
+       }\r
 }\r
 \r
-op16_t Core::op16()\r
+void\r
+Core::run()\r
 {\r
-       op16_t operand = m_mem.read16(m_reg.PC);\r
-       m_reg.PC += 2;\r
-       return operand;\r
-}\r
+#include "InstructionSet_Tables.cc"\r
 \r
-void Core::reg8(op8_t opcode, u8_t value)\r
-{\r
-       // 00RRR000\r
-       reg8(opcode >> 3) = value;\r
+       // Instruction decoding occurs in an odd order for the\r
+       // double-prefix instructions. PREFIX PREFIX OPERAND OPCODE\r
+       // This operand value is set in the dispatch method of the double-opcode\r
+       // handler\r
+       // TODO z80 only. Unused for gb80 uint8_t doublePrefixOperand(0);\r
+\r
+       Log& log(Log::Instance());\r
+\r
+       while (true)\r
+       {\r
+               cycle_t clock = 0;\r
+\r
+               uint16_t prefix(0); // Used for debug logging only.\r
+\r
+               // Check for interrupts.\r
+               if (!m_interruptQueue.empty())\r
+               {\r
+                       int interrupt = m_interruptQueue.front();\r
+                       m_interruptQueue.pop_front();\r
+\r
+                       m_iff1 = 0;\r
+                       m_iff2 = 0;\r
+\r
+                       // TODO interrupt modes!\r
+                       m_reg.SP.dec(2);\r
+                       m_mem.write16(m_reg.SP, m_reg.PC);\r
+\r
+                       m_reg.PC = htoz(interrupt);\r
+\r
+                       m_halted = false;\r
+               }\r
+               else if (!m_halted)\r
+               {\r
+                       uint16_t opcodePC = m_reg.PC.host(); // For debugging\r
+                       uint8_t opcode = op8();\r
+\r
+                       goto *(OpcodeTable[opcode]);\r
+\r
+#include "InstructionSet_Opcodes.cc"\r
+\r
+BAD_OPCODE:\r
+                       log(Log::DBG_ERROR) <<\r
+                               "Unknown Opcode: " << glBoy::hex(opcode) << " (PC=" << opcodePC << ")" <<\r
+                               std::endl;\r
+                       assert(false);\r
+\r
+END_INSTRUCTIONS: ;\r
+               }\r
+               else\r
+               {\r
+                       clock = 4;\r
+               }\r
+\r
+               m_clock += clock;\r
+\r
+               if (m_halted && m_interruptQueue.empty())\r
+               {\r
+                       m_clock = std::max(m_clock, m_nextPeriodicCallback);\r
+               }\r
+\r
+               if (m_clock >= m_nextPeriodicCallback)\r
+               {\r
+                       m_nextPeriodicCallback = std::numeric_limits<cycle_t>::max();\r
+                       for (size_t i = 0; i < m_periodicCallbacks.size(); ++i)\r
+                       {\r
+                               if (m_clock >= m_periodicCallbacks[i].nextPeriod)\r
+                               {\r
+                                       m_periodicCallbacks[i].callback(i);\r
+\r
+                                       while (m_periodicCallbacks[i].nextPeriod <= m_clock)\r
+                                       {\r
+                                               m_periodicCallbacks[i].nextPeriod +=\r
+                                                       m_periodicCallbacks[i].clocks;\r
+                                       }\r
+                               }\r
+\r
+                               if (m_periodicCallbacks[i].nextPeriod < m_nextPeriodicCallback)\r
+                               {\r
+                                       m_nextPeriodicCallback = m_periodicCallbacks[i].nextPeriod;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
 }\r
 \r
-reg8_t& Core::reg8(op8_t opcode) const\r
+Core::CallbackId\r
+Core::registerPeriodic(\r
+       int clockPeriod, int offset, const PeriodicCallbackFn& callback\r
+       )\r
 {\r
-       // 00000RRR\r
-       switch (opcode & 0b00000111)\r
+       assert(m_clock == 0);\r
+       PeriodicCallback periodic = { clockPeriod, offset, callback };\r
+\r
+       if (periodic.nextPeriod < m_nextPeriodicCallback)\r
        {\r
-               case 0: return m_reg.B;\r
-               case 1: return m_reg.C;\r
-               case 2: return m_reg.D;\r
-               case 3: return m_reg.E;\r
-               case 4: return m_reg.H;\r
-               case 5: return m_reg.L;\r
-               case 6: assert(0); FIXME\r
-               case 7: return m_reg.A;\r
+               m_nextPeriodicCallback = periodic.nextPeriod;\r
        }\r
+\r
+       m_periodicCallbacks.push_back(periodic);\r
+       return m_periodicCallbacks.size() - 1;\r
 }\r
-  \r
-reg16_t& Core::dd(op8_t opcode) const\r
+\r
+uint8_t Core::op8()\r
 {\r
-       // 00dd0000\r
-       u8_t offset = (opcode & 0b00110000) >> 4;\r
-       return (offset == 3) ? m_reg.SP : *(m_reg.begin16 + offset);\r
+       uint8_t result(m_mem.read8(m_reg.PC));\r
+       m_reg.PC.inc();\r
+       return result;\r
 }\r
 \r
-reg16_t& Core::qq(op8_t opcode) const\r
+DWORD Core::op16()\r
 {\r
-       // 00qq0000\r
-       u8_t offset = (opcode & 0b00110000) >> 4;\r
-       return *(m_reg.begin16 + offset);\r
+       DWORD operand(m_mem.read16(m_reg.PC));\r
+       m_reg.PC.inc(2);\r
+       return operand;\r
 }\r
 \r
diff --git a/Core.hh b/Core.hh
old mode 100755 (executable)
new mode 100644 (file)
index 0dbbadb..167fbe5
--- a/Core.hh
+++ b/Core.hh
@@ -1,41 +1,64 @@
-/** \r
- * Emulated Z80 CPU.\r
- *\r
- * Incrementing elapsed cycles is performed separately from emulating the opcodes.\r
- * to simplify the emulation.  Cycle info could be embedded in the switch statement?\r
- * eg. case X: doFoo; cycles += 7\r
- *\r
- * Authors: Michael McMaster <email@michaelmcmaster.name>\r
- * Copyright: Michael McMaster <email@michaelmcmaster.name>\r
- */ \r
-#pragma once\r
-\r
-#include "bits/Z80.hh"\r
-#include "Clock.hh"\r
-\r
-#include <memory>\r
-\r
-namespace Z80\r
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>\r
+//\r
+//     This file is part of glBoy.\r
+//\r
+//     glBoy is free software: you can redistribute it and/or modify\r
+//     it under the terms of the GNU General Public License as published by\r
+//     the Free Software Foundation, either version 3 of the License, or\r
+//     (at your option) any later version.\r
+//\r
+//     glBoy is distributed in the hope that it will be useful,\r
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+//     GNU General Public License for more details.\r
+//\r
+//     You should have received a copy of the GNU General Public License\r
+//     along with glBoy.  If not, see <http://www.gnu.org/licenses/>.\r
+\r
+#ifndef GLBOY_CORE_HH\r
+#define GLBOY_CORE_HH\r
+\r
+#include "glBoy.hh"\r
+#include "DWORD.hh"\r
+#include "MemoryMap.hh"\r
+#include "Registers.hh"\r
+\r
+#include <deque>\r
+#include <tr1/functional>\r
+#include <functional>\r
+#include <vector>\r
+\r
+namespace glBoy\r
 {\r
 \r
 class Core\r
 {\r
 public:\r
-       Core(std::auto_ptr<Memory> mem);\r
+       Core();\r
 \r
-       cycle_t run(cycle_t maxCycles);\r
+       MemoryMap& getMemoryMap() { return m_mem; }\r
+\r
+       Registers& getRegisters() { return m_reg; }\r
+\r
+       void run();\r
+\r
+       cycle_t getClockCount() const { return m_clock; }\r
 \r
        bit getIFF2() const { return m_iff2; }\r
 \r
-private:\r
-       op8_t op8();\r
-       op16_t op16();\r
+       void raiseInterrupt(int interrupt);\r
+       void halt() { m_halted = true; }\r
 \r
-       void reg8(op8_t opcode, u8_t value);\r
-       reg8_t& reg8(op8_t opcode) const;\r
+       typedef int CallbackId;\r
+       typedef std::function<void (CallbackId)> PeriodicCallbackFn;\r
 \r
-       reg16_t& dd(op8_t opcode) const;\r
-       reg16_t& qq(op8_t opcode) const;\r
+       CallbackId registerPeriodic(\r
+               int clockPeriod, int offset, const PeriodicCallbackFn& callback\r
+               );\r
+\r
+private:\r
+       uint8_t op8();\r
+       DWORD op16();\r
 \r
        // The Z80 can switch between register sets.\r
        Registers m_reg;\r
@@ -45,5 +68,26 @@ private:
        bit m_iff1;\r
        bit m_iff2;\r
 \r
-       std::auto_ptr<Memory> m_mem;\r
+       enum InterruptMode { IM_0, IM_1, IM_2 };\r
+       InterruptMode m_interruptMode;\r
+\r
+       bool m_halted;\r
+       std::deque<uint8_t> m_interruptQueue;\r
+\r
+       MemoryMap m_mem;\r
+\r
+       cycle_t m_clock;\r
+\r
+       struct PeriodicCallback\r
+       {\r
+               int clocks;\r
+               cycle_t nextPeriod;\r
+               PeriodicCallbackFn callback;\r
+       };\r
+       std::vector<PeriodicCallback> m_periodicCallbacks;\r
+       cycle_t m_nextPeriodicCallback;\r
 };\r
+\r
+} // namespace glBoy\r
+\r
+#endif\r
diff --git a/DAA.hh b/DAA.hh
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6e1c3bf6a25caafd58a89a52e29d5837d257aa0c 100644 (file)
--- a/DAA.hh
+++ b/DAA.hh
@@ -0,0 +1,62 @@
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of glBoy.
+//
+//     glBoy 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 3 of the License, or
+//     (at your option) any later version.
+//
+//     glBoy 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 glBoy.  If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef GLBOY_DAA_HH
+#define GLBOY_DAA_HH
+
+#include "glBoy.hh"
+#include "ALU.hh"
+#include "Registers.hh"
+
+namespace glBoy
+{
+
+// Algorithm taken from
+// http://wikiti.brandonw.net/?title=Z80_Instruction_Set
+inline uint8_t DAA(uint8_t a, Flags& flags)
+{
+       uint8_t result(a);
+
+       if (flags.N)
+       {
+               if (flags.H || ((a & 0xf) > 9)) result -= 6;
+               if (flags.C || (a > 0x99)) result -= 0x60;
+       }
+       else
+       {
+               if (flags.H || ((a & 0xf) > 9)) result += 6;
+               if (flags.C || (a > 0x99)) result += 0x60;
+       }
+
+       flags.byte = // S Z U5 H U3 P N C
+               (result & 0x80) |
+               ((result == 0) ? 0x40 : 0) |
+               // U5
+               ((a ^ result) & 0x10) |
+               // U3
+               flags.P |
+               (flags.N ? 0x2 : 0) |
+               ((flags.C || (a > 0x99)) ? 1 : 0);
+
+       // TODO U3 U5
+
+       return result;
+}
+
+} // namespace
+
+#endif
index 0c23bd537ac3a0c3629d491adb56d4ce9e7a4860..abf665a7fb074870cddad06a04c872f997b098d4 100644 (file)
--- a/DWORD.hh
+++ b/DWORD.hh
@@ -1,71 +1,37 @@
-/** \r
- * Emulated representation of the Z80 CPU registers.\r
- *\r
- * The Z80 cpu supports switching between multiple register sets.  Each Register\r
- * object only stores the details of a single register set.\r
-*\r
-* 16bit Register Pairs are represented in a Struct to enable simple byte-order\r
-* conversions where required.\r
- *\r
- * Authors: Michael McMaster <email@michaelmcmaster.name>\r
- * Copyright: Michael McMaster <email@michaelmcmaster.name>\r
- */ \r
-#pragma once\r
-\r
-#include "mm80.hh"\r
-\r
-namespace mm80\r
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>\r
+//\r
+//     This file is part of glBoy.\r
+//\r
+//     glBoy is free software: you can redistribute it and/or modify\r
+//     it under the terms of the GNU General Public License as published by\r
+//     the Free Software Foundation, either version 3 of the License, or\r
+//     (at your option) any later version.\r
+//\r
+//     glBoy is distributed in the hope that it will be useful,\r
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+//     GNU General Public License for more details.\r
+//\r
+//     You should have received a copy of the GNU General Public License\r
+//     along with glBoy.  If not, see <http://www.gnu.org/licenses/>.\r
+\r
+#ifndef GLBOY_DWORD_HH\r
+#define GLBOY_DWORD_HH\r
+\r
+#include "glBoy.hh"\r
+#include "util.hh"\r
+\r
+#include <iostream>\r
+\r
+namespace glBoy\r
 {\r
 \r
-class Core;\r
-\r
-/** 8-bit packed representation of the Z80 flags register.\r
- *\r
- *   C    Carry.  LSB of Flag register\r
- *   N    Add/Subtract\r
- *   P    (V) Parity/Overflow\r
- *   U3   3rd bit of last 8bit op that altered flags\r
- *   H    Half-Carry (BCD)\r
- *   U5   5th bit of last 8bit op that altered flags\r
- *   Z    Zero Flag\r
- *   S    Sign Flag. MSB of Flag register\r
- */\r
-struct Flags\r
-{\r
-       union\r
-       {\r
-               struct\r
-               {\r
-                       unsigned C:1; /// Carry.  LSB of Flag register\r
-                       unsigned N:1; /// Add/Subtract\r
-                       unsigned P:1; /// (V) Parity/Overflow\r
-                       unsigned U3:1; /// 3rd bit of last 8bit op that altered flags\r
-                       unsigned H:1; /// Half-Carry (BCD)\r
-                       unsigned U5:1; /// 5th bit of last 8bit op that altered flags\r
-                       unsigned Z:1; /// Zero Flag\r
-                       unsigned S:1; /// Sign Flag. MSB of Flag register\r
-               };\r
-               reg8_t byte;\r
-       };\r
-\r
-       Flags();\r
-\r
-       // Update the flags based on an operands value (eg. for simple assignment)\r
-       void set(op8_t operand, const Core& context);\r
-\r
-       // Update the flags when changing BC\r
-       void setCounter(op16_t BC);\r
-\r
-       // Set from A - B\r
-       void setSub(op8_t A, op8_t B, reg16_t BC);\r
-};\r
-\r
-struct DWORD\r
+struct __attribute__ ((__packed__)) DWORD\r
 {\r
        // Always constructed, and stored, in Z80 byte order.\r
        union\r
        {\r
-               reg16_t dword;\r
+               uint16_t dword;\r
                struct\r
                {\r
                        reg8_t l;\r
@@ -73,41 +39,47 @@ struct DWORD
                };\r
        };\r
 \r
-       reg16_t z80() const { return dword; }\r
-       reg16_t host() const\r
+       uint16_t z80() const { return dword; }\r
+       uint16_t host() const\r
        {\r
 #ifdef HOST_LITTLE_ENDIAN\r
                return dword;\r
 #else\r
-               return (reg16_t(h) << 8) | l;\r
+               return (uint16_t(h) << 8) | l;\r
 #endif\r
        }\r
 \r
-       void inc(u8_t val = 1) const\r
+       void inc(uint8_t val = 1)\r
        {\r
 #ifdef HOST_LITTLE_ENDIAN\r
-               ++dword;\r
+               dword += val;\r
 #else\r
-               reg16_t tmp(host() + val);\r
+               uint16_t tmp(host() + val);\r
                l = tmp & 0xf;\r
                h = tmp >> 8;\r
 #endif\r
        }\r
 \r
-       void dec(u8_t val = 1) const\r
+       void dec(uint8_t val = 1)\r
        {\r
 #ifdef HOST_LITTLE_ENDIAN\r
-               --dword;\r
+               dword -= val;\r
 #else\r
-               reg16_t tmp(host() - val);\r
+               uint16_t tmp(host() - val);\r
                l = tmp & 0xf;\r
                h = tmp >> 8;\r
 #endif\r
        }\r
 };\r
 \r
+inline std::ostream& operator<<(std::ostream& out, const glBoy::DWORD& val)\r
+{\r
+       out << glBoy::hex(val.host());\r
+       return out;\r
+}\r
+\r
 // Create a dword from z80-ordered mem.\r
-static DWORD CreateDWORD(u8_t* in)\r
+inline DWORD CreateDWORD(uint8_t* in)\r
 {\r
        DWORD result;\r
        result.l = in[0];\r
@@ -115,64 +87,23 @@ static DWORD CreateDWORD(u8_t* in)
        return result;\r
 }\r
 \r
-/** Z80 register access.\r
- *\r
- * Registers may be accessed in either 8-bit (eg. A) \r
- * or by their 16-bit pair (eg. AF)\r
- *\r
- */\r
-struct Registers\r
+#if defined(HOST_LITTLE_ENDIAN)\r
+inline DWORD htoz(uint16_t host)\r
 {\r
-       Registers();\r
-\r
-#define Z80_REG_STRUCT(h,l) \\r
-       struct \\r
-       { \\r
-               l; \\r
-               h; \\r
-       };\r
+       DWORD result;\r
+       result.dword = host;\r
+       return result;\r
+}\r
+#else\r
+inline DWORD htoz(uint16_t host)\r
+{\r
+       DWORD result;\r
+       result.dword = (host << 8) | (host >> 8);\r
+       return result;\r
+}\r
 #endif\r
+} // namespace glBoy\r
 \r
-       // Note: The order of these unions/structs is important, as\r
-       // offsets are taken into the Register struct\r
-       reg8_t[0] begin8;\r
-       reg16_t[0] begin16;\r
-\r
-       union\r
-       {\r
-               Z80_REG_STRUCT(reg8_t B, reg8_t C);\r
-               DWORD BC;\r
-       };\r
-\r
-       union\r
-       {\r
-               Z80_REG_STRUCT(reg8_t D, reg8_t E);\r
-               DWORD DE;\r
-       };\r
-\r
-       union\r
-       {\r
-               Z80_REG_STRUCT(reg8_t H, reg8_t L);\r
-               DWORD HL;\r
-       };\r
-\r
-       union\r
-       {\r
-               Z80_REG_STRUCT(reg8_t A, Flags F,);\r
-               DWORD AF;\r
-       };\r
-\r
-\r
-#undef Z80_REG_STRUCT\r
-\r
-       // Special Purpose\r
-       reg8_t I;      // Interrupt Vector\r
-       reg8_t R;      // Memory Refresh\r
-       DWORD IX;     // Index Register\r
-       DWORD IY;     // Index Register\r
-       DWORD SP;     // Stack Pointer\r
-       DWORD PC;     // Program Counter\r
-};\r
+#endif\r
 \r
-}\r
 \r
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f0566ec87137c6930aa045e76816b7454722d887 100644 (file)
@@ -0,0 +1,235 @@
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of glBoy.
+//
+//     glBoy 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 3 of the License, or
+//     (at your option) any later version.
+//
+//     glBoy 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 glBoy.  If not, see <http://www.gnu.org/licenses/>.
+
+#include "glBoy.hh"
+#include "GameboyCart.hh"
+#include "RAM.hh"
+#include "ROM.hh"
+
+#include <cassert>
+
+using namespace glBoy;
+using namespace std;
+
+namespace
+{
+       class MBC1 : public MemoryMap::Memory
+       {
+       public:
+               MBC1(GameboyCart* parent, uint8_t* begin, uint8_t* end) :
+                       m_parent(parent),
+                       m_rom(begin, end),
+                       m_mode(ROM_SELECT),
+                       m_romLow(1),
+                       m_romHigh(0),
+                       m_romOffset(0)
+               {
+               }
+
+               virtual uint8_t read8(uint16_t address)
+               {
+                       if (address < 0x4000)
+                       {
+                               return m_rom[address];
+                       }
+                       else if ((m_romOffset + address) < m_rom.size())
+                       {
+                               return m_rom[m_romOffset + address];
+                       }
+                       else
+                       {
+std::cerr << address << "  " << m_rom.size() << " " << m_romOffset << " " << (int) m_romLow << " " << (int) m_romHigh << std::endl;
+assert(false);
+                               return 0;
+                       }
+               }
+
+               virtual void write8(uint16_t address, uint8_t value)
+               {
+                       if (address < 0x1FFF)
+                       {
+                               // TODO enable/disable ram
+                       }
+                       else if (address < 0x4000)
+                       {
+                               m_romLow = (value == 0) ? 1 : value;
+
+                               m_romOffset =
+                                       ((((m_romHigh & 0x3) << 4) | (m_romLow & 0x1F)) - 1) * 0x4000;
+assert(m_romOffset < m_rom.size());
+                       }
+                       else if (address < 0x6000)
+                       {
+                               if (m_mode == RAM_SELECT)
+                               {
+                                       m_parent->setRamBank(value & 0x3);
+                               }
+                               else
+                               {
+                                       m_romHigh = value;
+
+                                       m_romOffset =
+                                               ((((m_romHigh & 0x3) << 4) | (m_romLow & 0x1F)) - 1) * 0x4000;
+assert(m_romOffset < m_rom.size());
+                               }
+                       }
+                       else
+                       {
+                               if (value & 1)
+                               {
+                                       m_mode = RAM_SELECT;
+                               }
+                               else
+                               {
+                                       m_mode = ROM_SELECT;
+                                       m_parent->setRamBank(0);
+                               }
+                       }
+               }
+
+       private:
+               GameboyCart* m_parent;
+
+               std::vector<uint16_t> m_rom;
+
+               enum Mode { ROM_SELECT, RAM_SELECT };
+               Mode m_mode;
+
+               uint8_t m_romLow;
+               uint8_t m_romHigh;
+
+               uint32_t m_romOffset;
+       };
+}
+
+GameboyCart::GameboyCart(
+       Core& core, uint8_t* rom, size_t size
+       ) :
+       m_core(core)
+{
+       if (size < 16384)
+       {
+               std::cerr << "Invalid ROM (too small)" << std::endl;
+               abort();
+       }
+
+       bool error(false);
+
+       string mbc;
+       switch (rom[0x147])
+       {
+       case 0:
+       {
+               mbc = "None";
+
+               shared_ptr<MemoryMap::Memory> mem(
+                       new MBC1(this, rom, rom + size)
+                       );
+/*             shared_ptr<MemoryMap::Memory> mem(
+                       new ROM(rom, size)
+                       );*/
+               m_core.getMemoryMap().map(0, 0x8000, mem);
+               m_romBanks.push_back(mem);
+       }; break;
+
+       case 1:
+       case 2:
+       case 3:
+       {
+               mbc = "MBC1";
+               shared_ptr<MemoryMap::Memory> mem(
+                       new MBC1(this, rom, rom + size)
+                       );
+               m_core.getMemoryMap().map(0, 0x8000, mem);
+               m_romBanks.push_back(mem);
+       };
+       break;
+       default:
+               mbc = "Unsupported";
+               error = true;
+       };
+
+       int cartRam;
+       switch (rom[0x149])
+       {
+       case 0:
+       {
+               // Mis-reported on some carts.
+               cartRam = 0;
+               shared_ptr<MemoryMap::Memory> mem(new RAM(8192));
+               m_core.getMemoryMap().map(0xA000, 8192, mem);
+               m_ramBanks.push_back(mem);
+       }; break;
+       case 1:
+       {
+               cartRam = 2048;;
+
+               // Fill out the entire 8k, just in case.
+               shared_ptr<MemoryMap::Memory> mem(new RAM(8192));
+               m_core.getMemoryMap().map(0xA000, cartRam, mem);
+               m_ramBanks.push_back(mem);
+       }; break;
+
+       case 2:
+       {
+               cartRam = 8192;
+               shared_ptr<MemoryMap::Memory> mem(new RAM(cartRam));
+               m_core.getMemoryMap().map(0xA000, cartRam, mem);
+               m_ramBanks.push_back(mem);
+       }; break;
+
+       case 3:
+       {
+               cartRam = 32768;
+               for (int i = 0; i < 4; ++i)
+               {
+                       shared_ptr<MemoryMap::Memory> mem(new RAM(8192));
+                       m_ramBanks.push_back(mem);
+               }
+               m_core.getMemoryMap().map(0xA000, 8192, m_ramBanks.front());
+       }; break;
+
+       default:
+               error = true;
+               cartRam = 0;
+               cerr << "Invalid RAM size" << endl;
+       }
+
+       string title(rom + 0x134, rom + 0x134 + 11);
+       cout << "Loading Cart" << endl;
+       cout << "\tTitle:\t\t" << title << endl;
+       cout << "\tRegion:\t\t" << (rom[0x14A] ? "Worldwide" : "Japan") << endl;
+       cout << "\tVersion:\t" << int(rom[0x14D]) << endl;
+       cout << "\tMBC:\t\t" << mbc << endl;
+       cout << "\tROM size:\t" << (32 << rom[0x148]) << "KB" << endl;
+       cout << "\tExt RAM size:\t" << (cartRam / 1024) << "KB" << endl;
+
+       if (error)
+       {
+               abort();
+       }
+}
+
+void
+GameboyCart::setRamBank(size_t bank)
+{
+       if (bank < m_ramBanks.size())
+       {
+               m_core.getMemoryMap().remove(0xA000);
+               m_core.getMemoryMap().map(0xA000, 8192, m_ramBanks[bank]);
+       }
+}
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d67493195f16fae2a224ceab252173f01b2a20cc 100644 (file)
@@ -0,0 +1,42 @@
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of glBoy.
+//
+//     glBoy 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 3 of the License, or
+//     (at your option) any later version.
+//
+//     glBoy 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 glBoy.  If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef GLBOY_GAMEBOYCART_HH
+#define GLBOY_GAMEBOYCART_HH
+
+#include "glBoy.hh"
+#include "Core.hh"
+
+namespace glBoy
+{
+
+class GameboyCart
+{
+public:
+       GameboyCart(Core& core, uint8_t* rom, size_t size);
+
+       void setRamBank(size_t bank);
+private:
+       Core& m_core;
+
+       std::vector<std::shared_ptr<MemoryMap::Memory> > m_romBanks;
+       std::vector<std::shared_ptr<MemoryMap::Memory> > m_ramBanks;
+};
+
+} // namespace glBoy
+
+#endif
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..57cae8a0a8076a26a3db54252c3281e54d94f144 100644 (file)
@@ -0,0 +1,758 @@
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of glBoy.
+//
+//     glBoy 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 3 of the License, or
+//     (at your option) any later version.
+//
+//     glBoy 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 glBoy.  If not, see <http://www.gnu.org/licenses/>.
+
+#define GL_GLEXT_PROTOTYPES
+
+#include "glBoy.hh"
+#include "GameboyGraphics.hh"
+#include "Log.hh"
+#include "MemoryMap.hh"
+
+#include <GL/gl.h>
+#include <GL/glext.h>
+
+#include <SDL/SDL_opengl.h>
+
+#include <cassert>
+#include <limits>
+
+#include <sys/time.h>
+
+
+using namespace glBoy;
+
+namespace
+{
+       class VRAM : public MemoryMap::Memory
+       {
+       public:
+               VRAM(uint8_t* mem,
+                       const std::function<void ()>& changed
+                       ) :
+                       m_mem(mem),
+                       m_changed(changed)
+               {}
+
+               virtual uint8_t read8(uint16_t address) { return m_mem[address]; }
+               virtual void write8(uint16_t address, uint8_t value)
+               {
+                       m_mem[address] = value;
+                       m_changed();
+               }
+
+               virtual void copy(uint8_t* dest, uint16_t address, int bytes)
+               {
+                       memcpy(dest, m_mem + address, bytes);
+               }
+
+       private:
+               uint8_t* m_mem;
+               std::function<void ()> m_changed;
+       };
+
+}
+
+class GameboyGraphics::GraphicsRegisters : public MemoryMap::Memory
+{
+public:
+       uint8_t LCDC;
+       uint8_t STAT;
+       uint8_t SCY;
+       uint8_t SCX;
+       uint8_t LY;
+       uint8_t LYC;
+       // uint8_t DMA; No need to store this - write-only.
+       uint8_t BGP;
+       uint8_t OBP0;
+       uint8_t OBP1;
+       uint8_t WY;
+       uint8_t WX;
+
+       GraphicsRegisters(
+               Core& core,
+               uint8_t* oam,
+               const std::function<void ()>& backgroundChanged
+               ) :
+               LCDC(0),
+               STAT(0),
+               SCY(0),
+               SCX(0),
+               LY(0),
+               LYC(0),
+               BGP(0),
+               OBP0(0),
+               OBP1(0),
+               WY(0),
+               WX(0),
+               m_core(core),
+               m_mapping(core.getMemoryMap()),
+               m_oam(oam),
+               m_backgroundChanged(backgroundChanged)
+       {
+       }
+
+       virtual uint8_t read8(uint16_t address)
+       {
+               assert(address < 12);
+
+               switch (address)
+               {
+                       case 0: // LCDC
+                       {
+                               return LCDC;
+                       } break;
+                       case 1: // STAT
+                       {
+                               return STAT;
+                       } break;
+                       case 2: // SCY
+                       {
+                               return SCY;
+                       } break;
+                       case 3: // SCX
+                       {
+                               return SCX;
+                       } break;
+                       case 4: // LY
+                       {
+                               return LY;
+                       } break;
+                       case 5: // LYC
+                       {
+                               return LYC;
+                       } break;
+                       case 6: // DMA
+                       {
+                               return 0;
+                       } break;
+                       case 7: // BGP
+                       {
+                               return BGP;
+                       } break;
+                       case 8: // OBP0
+                       {
+                               return OBP0;
+                       } break;
+                       case 9: // OBP1
+                       {
+                               return OBP1;
+                       } break;
+                       case 10: // WY
+                       {
+                               return WY;
+                       } break;
+                       case 11: // WX
+                       {
+                               return WX;
+                       } break;
+               }
+               return 0; // make g++ happy.
+       }
+
+       virtual void write8(uint16_t address, uint8_t value)
+       {
+               assert(address < 12);
+
+               switch (address)
+               {
+                       case 0: // LCDC
+                       {
+                               LCDC = value;
+                               m_backgroundChanged();
+                       } break;
+                       case 1: // STAT
+                       {
+                               // bits 0-2 are read only.
+                               uint8_t tmp(STAT & 0x7);
+                               STAT = value & 0xF8;
+                               STAT |= tmp;
+                       } break;
+                       case 2: // SCY
+                       {
+                               SCY = value;
+                       } break;
+                       case 3: // SCX
+                       {
+                               SCX = value;
+                       } break;
+                       case 4: // LY
+                       {
+                               // Readonly.
+                       } break;
+                       case 5: // LYC
+                       {
+                               LYC = value;
+                       } break;
+                       case 6: // DMA
+                       {
+                               // Start a DMA transfer to the OAM memory area. This should
+                               // take 160 microseconds, but there's no real need to simulate
+                               // this.  CPU may only access zero-page mem during the
+                               // transfer, and will spin the required number of cycles.
+                               uint16_t dmaStart(static_cast<uint16_t>(value) << 8);
+                               m_mapping.copy(m_oam, dmaStart, 0x9F);
+                       } break;
+                       case 7: // BGP
+                       {
+                               BGP = value;
+                               m_backgroundChanged();
+                       } break;
+                       case 8: // OBP0
+                       {
+                               OBP0 = value;
+                       } break;
+                       case 9: // OBP1
+                       {
+                               OBP1 = value;
+                       } break;
+                       case 10: // WY
+                       {
+                               WY = value;
+                       } break;
+                       case 11: // WX
+                       {
+                               WX = value;
+                       } break;
+               }
+       }
+
+       private:
+               Core& m_core;
+               MemoryMap& m_mapping; // For DMA transfers.
+               uint8_t* m_oam;
+               std::function<void ()> m_backgroundChanged;
+};
+
+
+GameboyGraphics::GameboyGraphics(
+       Core& core,
+       SDL_Surface* surface,
+       PixelScaler scaler,
+       ResampleMethod resampler
+       ) :
+       m_core(core),
+       m_scaler(scaler),
+       m_resampler(resampler),
+       m_hq4x(160, 144),
+       m_vram(new uint8_t[0x2000]),
+       m_oam(new uint8_t[0x100]), // Should be 0x9F, but many access to 100
+       m_reg(
+               new GraphicsRegisters(
+                       core,
+                       m_oam,
+                       std::bind(&GameboyGraphics::backgroundChanged, this)
+                       )
+               ),
+       m_backgroundRedraw(true),
+       m_surface(surface),
+       m_frameSkip(false),
+       m_lastSkipped(false),
+       m_stat_frames(0),
+       m_stat_skipped(0),
+       m_stat_redraw(0)
+{
+       MemoryMap& mapping(core.getMemoryMap());
+       mapping.map(
+               0x8000,
+               0x2000,
+               std::shared_ptr<VRAM>(
+                       new VRAM(
+                               m_vram,
+                               std::bind(&GameboyGraphics::backgroundChanged, this)
+                               )
+                       )
+               );
+       mapping.map(
+               0xFE00,
+               0x100,
+               std::shared_ptr<VRAM>(
+                       new VRAM(
+                               m_oam,
+                               std::bind(&GameboyGraphics::oamChanged, this)
+                               )
+                       )
+               );
+       mapping.map(0xFF40, 12, m_reg);
+
+       using namespace std::placeholders;
+       m_mode0 = core.registerPeriodic(
+               456, 252, std::bind(&glBoy::GameboyGraphics::periodicCallback, this, _1)
+               );
+       m_mode2 = core.registerPeriodic(
+               456, 0, std::bind(&glBoy::GameboyGraphics::periodicCallback, this, _1)
+               );
+       m_mode3 = core.registerPeriodic(
+               456, 80, std::bind(&glBoy::GameboyGraphics::periodicCallback, this, _1)
+               );
+
+       gettimeofday(&m_stat_fpsTimer, NULL);
+}
+
+GameboyGraphics::~GameboyGraphics()
+{
+       delete[] m_vram;
+       delete[] m_oam;
+}
+
+void
+GameboyGraphics::periodicCallback(Core::CallbackId id)
+{
+       // Mode 0: H-Blank
+       // Mode 1: V-Blank
+       // Mode 2: Reading from OAM
+       // Mode 3: Reading from OAM and VRAM
+       uint8_t mode;
+
+       if (id == m_mode0)
+       {
+               renderScanLine();
+
+               mode = (m_reg->LY > 143) ? 1 : 0;
+       }
+       else if (id == m_mode2)
+       {
+               startScanLine();
+               mode = (m_reg->LY > 143) ? 1 : 2;
+       }
+       else // mode3
+       {
+               mode = (m_reg->LY > 143) ? 1 : 3;
+       }
+
+       m_reg->STAT = (m_reg->STAT & 0xF8) | mode;
+}
+
+void
+GameboyGraphics::startScanLine()
+{
+       ++m_reg->LY;
+       if (m_reg->LY > 153) m_reg->LY = 0;
+
+       m_lineScrollX[m_reg->LY] = m_reg->SCX;
+       m_lineScrollY[m_reg->LY] = m_reg->SCY;
+
+       bool equal(m_reg->LY == m_reg->LYC);
+       if (equal)
+       {
+               m_reg->STAT |= 0x4;
+       }
+       else
+       {
+               m_reg->STAT &= 0xFB;
+       }
+
+       if ((m_reg->LY == 144))
+       {
+               // Draw the entire screen when entering vblank. This will be done just
+               // under 60 times per second.
+               if (!m_frameSkip || m_lastSkipped)
+               {
+                       renderScreen();
+                       m_lastSkipped = false;
+               }
+               else
+               {
+                       ++m_stat_skipped;
+                       m_lastSkipped = true;
+               }
+               m_frameSkip = false;
+               displayStats();
+
+               //if (m_reg->STAT & 0x10) Seems to need to trigger regardless ?
+               // Maybe we need to trigger 0x40 as well, depending on STAT ?
+               {
+                       m_core.raiseInterrupt(0x40); // Start of vblank
+               }
+       }
+       else if (
+               (equal && (m_reg->STAT & 0x40)) || // Coincidence Interrupt
+               (m_reg->STAT & 0x20) // OAM interrupt
+               )
+       {
+               m_core.raiseInterrupt(0x48);
+       }
+}
+
+void
+GameboyGraphics::renderScanLine()
+{
+       // Entering H-Blank
+       if (m_reg->STAT & 0x08 && (m_reg->LY <= 143))
+       {
+               m_core.raiseInterrupt(0x48);
+       }
+
+       // Don't bother drawing individual scanlines - just draw the entire screen
+       // when entering VBLANK (which is actually done in the startScanLine method)
+}
+
+// Render the given tile to the screen at the given coordinates.
+// The x and y may be outside the bounds of the screen, and may be negative.
+// Only the visible part of the tile will be rendered.
+void GameboyGraphics::renderTile(
+       uint8_t tile,
+       int x,
+       int y,
+       const FrameBuffer& frameBuffer
+       )
+{
+       uint16_t tileAddr;
+       if (m_reg->LCDC & 0x10)
+       {
+               tileAddr = 0x8000 + static_cast<uint16_t>(tile) * 16;
+       }
+       else
+       {
+               tileAddr = 0x9000 +
+                       (static_cast<int8_t>(tile) * static_cast<uint16_t>(16));
+       }
+
+
+       renderTile(tileAddr, x, y, frameBuffer, RENDER_BLIT, m_reg->BGP);
+}
+
+void GameboyGraphics::renderTile(
+       uint16_t tileAddr,
+       int x,
+       int y,
+       const FrameBuffer& frameBuffer,
+       RenderMode mode,
+       uint8_t palette,
+       uint8_t sprite, // 0x80 -> 0xA8, 0 for background/window
+       bool horizFlip,
+       bool vertFlip,
+       int tileHeight // 8 or 16
+       )
+{
+       // Render an 8x8 or 8x16 tile.
+
+       tileAddr -= 0x8000; // Use vram directly.
+
+       if (vertFlip) tileAddr += (tileHeight - 1) * 2;
+       int yStep = vertFlip ? -2 : 2; // Each row takes 2 bytes.
+       if (y < 0) tileAddr += yStep * (-y);
+
+       for (int posY = (y < 0 ? -y : 0);
+               (posY < tileHeight) && (y + posY < frameBuffer.height);
+               ++posY, tileAddr += yStep
+               )
+       {
+               uint8_t rowLSB(m_vram[tileAddr]);
+               uint8_t rowMSB(m_vram[tileAddr + 1]);
+
+               size_t pixelRowAddr((y + posY) * frameBuffer.width + x);
+
+               int xStep = horizFlip ? 1 : -1;
+               int xBit = horizFlip ? 0 : 7;
+               if (x < 0) xBit += xStep * (-x);
+
+               for (int posX = (x < 0 ? -x : 0);
+                       (posX < 8) && (x + posX < frameBuffer.width);
+                       ++posX, xBit += xStep
+                       )
+               {
+                       uint8_t col =
+                               (rowMSB >> xBit) & 0x01;
+                       col <<= 1;
+                       col |=
+                               (rowLSB >> xBit) & 0x01;
+
+                       Pixel& pixel = frameBuffer.frame[pixelRowAddr + posX];
+
+                       bool display;
+                       if (!sprite)
+                       {
+                               // Always display background and window data.
+                               // window data overwrites background data
+                               display = true;
+                       }
+                       else if (pixel.tileX != std::numeric_limits<int16_t>::max())
+                       {
+                               if (col)
+                               {
+                                       if (x < pixel.tileX)
+                                       {
+                                               display = true;
+                                       }
+                                       else
+                                       {
+                                               // Existing pixel has priority. Note that transparent
+                                               // areas of sprites are never output (ie. it remains
+                                               // background), so there is no need to check of the
+                                               // oldSprite was actually transparent.
+                                               // Also note that we draw sprites in order, so this
+                                               // sprite number can never be less than the already-
+                                               // drawn sprite (and thus will not have priority)
+                                               display = false;
+                                       }
+                               }
+                               else
+                               {
+                                       // Ignore transparent areas of the sprite.
+                                       display = false;
+                               }
+                       }
+                       else
+                       {
+                               // Should this sprite pixel overwrite the background ?
+                               // Sprite colour 0 is transparent (shows background)
+                               // Background colour 0 is always "behind" the sprite.
+                               display =
+                                       col &&
+                                       ((mode == RENDER_BLIT) || (pixel.colourIndex == 0));
+                       }
+
+                       if (display)
+                       {
+                               pixel.tileX = sprite ? x : std::numeric_limits<int16_t>::max();
+                               pixel.palette = palette;
+                               pixel.colourIndex = col;
+                       }
+               }
+       }
+}
+
+void
+GameboyGraphics::renderScreen()
+{
+       FrameBuffer screenBuffer = { 160, 144, &m_screen[0][0]};
+
+       if (m_reg->LCDC & 0x1)
+       {
+               // Draw background. Background tile map is a 256x256 virtual screen,
+               // that is scrolled via SCX/SCY.
+
+               if (m_backgroundRedraw)
+               {
+                       m_backgroundRedraw = false;
+                       ++m_stat_redraw;
+
+                       // Select one of two maps at 0x9800/0x9C00
+                       uint16_t tileMapAddr(m_reg->LCDC & 0x08 ? 0x1C00 : 0x1800);
+
+                       // Render entire tile map, then copy to the screen.
+                       memset(m_background, 0, sizeof(m_background));
+                       FrameBuffer buffer = { 256, 256, &m_background[0][0] };
+
+                       for (int y = 0; y < 32; ++y)
+                       {
+                               uint16_t yAddr(tileMapAddr + (y * 32));
+                               for (int x = 0; x < 32; ++x)
+                               {
+                                       renderTile(
+                                               m_vram[yAddr + x],
+                                               x * 8,
+                                               y * 8,
+                                               buffer
+                                               );
+                               }
+                       }
+               }
+
+               for (int y = 0; y < 144; ++y)
+               {
+                       int scrollY = y + m_lineScrollY[y];
+                       if (scrollY > 255) scrollY -= 256;
+
+                       for (int x = 0; x < 160; ++x)
+                       {
+                               int scrollX = x + m_lineScrollX[y];
+                               if (scrollX > 255) scrollX -= 256;
+
+                               m_screen[y][x] = m_background[scrollY][scrollX];
+                       }
+               }
+       }
+       else
+       {
+               // clear screen.
+               memset(m_screen, 0, sizeof(Pixel) * 144 * 160);
+       }
+
+
+       if (m_reg->LCDC & 0x20)
+       {
+               // Draw window over top of existing background.
+
+               // Draw background. Window tile map is a screen,
+               // that is positioned via WX/WY.
+
+               // Select one of two maps at 0x9800/0x9C00
+               uint16_t tileMapAddr(m_reg->LCDC & 0x40 ? 0x1C00 : 0x1800);
+
+               for (int y = 0; y < 18; ++y)
+               {
+                       uint16_t yAddr(tileMapAddr + (y * 32));
+
+                       for (int x = 0; x < 20; ++x)
+                       {
+                               renderTile(
+                                       m_vram[yAddr + x],
+                                       x * 8 + m_reg->WX - 7,
+                                       y * 8 + m_reg->WY,
+                                       screenBuffer // Render directly to the screen.
+                                       );
+                       }
+               }
+       }
+
+       std::vector<uint8_t> sprite;
+       sprite.resize(160);
+       if (m_reg->LCDC & 0x2)
+       {
+               // Draw sprites
+
+               for (int spriteNum = 0; spriteNum < 40; ++spriteNum)
+               {
+                       Sprite sprite(getSprite(spriteNum));
+
+                       renderTile(
+                               0x8000 + static_cast<uint16_t>(sprite.tile) * 16,
+                               sprite.x,
+                               sprite.y,
+                               screenBuffer,
+                               (sprite.flags & 0x80) ? RENDER_SPRITE : RENDER_BLIT,
+                               (sprite.flags & 0x10) ? m_reg->OBP1 : m_reg->OBP0,
+                               static_cast<uint8_t>(spriteNum) | 0x80,
+                               (sprite.flags & 0x20) ? true : false, // horizontal flip
+                               (sprite.flags & 0x40) ? true : false, // vertical flip
+                               (m_reg->LCDC & 0x04) ? 16 : 8 // 8x8 or 8x16
+                               );
+               }
+       }
+       updateOutputDevice();
+
+
+}
+
+GameboyGraphics::Sprite
+GameboyGraphics::getSprite(int spriteNum) const
+{
+       Sprite result;
+
+       result.y = static_cast<int>(m_oam[spriteNum * 4]) - 16;
+       result.x = static_cast<int>(m_oam[spriteNum * 4 + 1]) - 8;
+       result.tile = m_oam[spriteNum * 4 + 2];
+       result.flags = m_oam[spriteNum * 4 + 3];
+
+       return result;
+}
+
+void
+GameboyGraphics::displayStats()
+{
+       m_stat_frames++;
+       if (m_stat_frames == 300)
+       {
+               timeval startTime(m_stat_fpsTimer);
+               gettimeofday(&m_stat_fpsTimer, 0);
+
+               const double usec(1000000.0);
+               double fps = static_cast<double>(m_stat_frames - m_stat_skipped) /
+                       (
+                               (m_stat_fpsTimer.tv_sec + m_stat_fpsTimer.tv_usec / usec) -
+                               (startTime.tv_sec + startTime.tv_usec / usec)
+                       );
+
+               Log::Instance()(Log::DBG_STAT) <<
+                       "FPS = " << fps << " (error=" << (100 - (fps * 100 / 59.73)) << "%) "
+                       "backround redraw=" << m_stat_redraw << std::endl;
+               m_stat_frames = 0;
+               m_stat_skipped = 0;
+               m_stat_redraw = 0;
+       }
+}
+
+void
+GameboyGraphics::blitGreyscale(uint8_t* dest)
+{
+       for (int y = 0; y < 144; ++y)
+       {
+               uint8_t* row(dest + y*160);
+               for (int x = 0; x < 160; ++x)
+               {
+                       int index(m_screen[y][x].colourIndex);
+                       int colour((m_screen[y][x].palette >> (index * 2)) & 0x3);
+                       row[x] = 255 - ( colour * 85);
+               }
+       }
+}
+
+void
+GameboyGraphics:: updateOutputDevice()
+{
+       if (SDL_MUSTLOCK(m_surface))
+       {
+               if (SDL_LockSurface(m_surface) < 0)
+               {
+                       return;
+               }
+       }
+
+       // Use static buffers to reduce stack usage
+       uint8_t textureData[144*160];
+       blitGreyscale(&textureData[0]);
+
+       // Allocate texture
+       GLuint texture;
+       glGenTextures(1, &texture);
+       glActiveTexture(GL_TEXTURE0);
+       glBindTexture(GL_TEXTURE_2D, texture);
+
+       // Set the texture's stretching properties
+       // When using opengl shader scalers, we need to use nearest.
+       uint32_t resample(
+               (m_scaler == Scaler_None && m_resampler == Resample_Bilinear) ?
+                       GL_LINEAR : GL_NEAREST
+               );
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, resample);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, resample);
+
+       // Create the texture image
+       glTexImage2D(
+               GL_TEXTURE_2D,
+               0,
+               GL_RGBA,
+               160,
+               144,
+               0,
+               GL_LUMINANCE,
+               GL_UNSIGNED_BYTE,
+               &textureData
+               );
+
+       m_hq4x.prepareShader(texture);
+
+       // Output texture.
+
+       glBegin(GL_QUADS);
+               glTexCoord2i( 0, 0 ); glVertex2i( 0, 0 );
+               glTexCoord2i( 1, 0 ); glVertex2i( 160, 0 );
+               glTexCoord2i( 1, 1 ); glVertex2i( 160, 144 );
+               glTexCoord2i( 0, 1 ); glVertex2i( 0, 144);
+       glEnd();
+
+       glLoadIdentity();
+       SDL_GL_SwapBuffers();
+       glDeleteTextures( 1, &texture );
+
+       if (SDL_MUSTLOCK(m_surface))
+       {
+               SDL_UnlockSurface(m_surface);
+       }
+}
+
index 18afd1cc5ce979ce5f9cb43783dd3a98ac446734..1795a6b200c5dec420fb7a4f76feefbdddb357d3 100644 (file)
-#ifndef MM80_GAMEBOYGRAPHICS_HH
-#define MM80_GAMEBOYGRAPHICS_HH
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of glBoy.
+//
+//     glBoy 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 3 of the License, or
+//     (at your option) any later version.
+//
+//     glBoy 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 glBoy.  If not, see <http://www.gnu.org/licenses/>.
 
-#include "mm80.hh"
+#ifndef GLBOY_GAMEBOYGRAPHICS_HH
+#define GLBOY_GAMEBOYGRAPHICS_HH
+
+#include "glBoy.hh"
+#include "Core.hh"
 
 #include "MemoryMap.hh"
 
+#include "hq4x.hh"
+
+#include <SDL/SDL.h>
+#include <SDL/SDL_opengl.h>
+
+#include <vector>
+
+namespace glBoy
+{
 
 class GameboyGraphics
 {
 public:
+       enum PixelScaler { Scaler_None, Scaler_Scale2x, Scaler_hq4x };
+       enum ResampleMethod { Resample_Nearest, Resample_Bilinear };
+
+       GameboyGraphics(
+               Core& core,
+               SDL_Surface* surface,
+               PixelScaler scaler,
+               ResampleMethod resampler
+               );
+       ~GameboyGraphics();
+
+       void startScanLine();
        void renderScanLine();
 
+       void setFrameSkip() { m_frameSkip = true; }
+       void clearFrameSkip() { m_frameSkip = false; }
+
 private:
+       void periodicCallback(Core::CallbackId);
+
+       void backgroundChanged() { m_backgroundRedraw = true; }
+       void oamChanged() {}
+
+       struct Pixel // Must be POD - used with memset.
+       {
+               int16_t tileX;  // Max for background/window
+               uint8_t palette;
+               uint8_t colourIndex; // Index into pallete.
+       };
+
+       struct FrameBuffer
+       {
+               int width;
+               int height;
+               Pixel* frame;
+       };
+
+       // RENDER_BLIT = overwrite background.
+       // RENDER_SPRITE = only overwrite background colour 0 (ie. send
+       // behind background)
+       enum RenderMode{ RENDER_BLIT, RENDER_SPRITE };
+
+       void renderScreen();
+       void displayStats();
+
+       void renderTile(
+               uint8_t tile,
+               int x,
+               int y,
+               const FrameBuffer& frameBuffer
+               );
+
+       void renderTile(
+               uint16_t tileAddr,
+               int x,
+               int y,
+               const FrameBuffer& frameBuffer,
+               RenderMode mode,
+               uint8_t pallete,
+               uint8_t sprite = 0, // 0x80 -> 0xA8, 0 for background/window
+               bool horizFlip = false,
+               bool vertFlip = false,
+               int tileHeight = 8 // 8 or 16
+               );
+
+       struct Sprite
+       {
+               int x;
+               int y;
+               uint8_t tile;
+               uint8_t flags;
+       };
+       Sprite getSprite(int spriteNum) const;
+
+       void blitGreyscale(uint8_t* dest);
+       void updateOutputDevice();
+
+       Core& m_core;
+
+       PixelScaler m_scaler;
+       ResampleMethod m_resampler;
+
+       Hq4x m_hq4x;
+
        // Graphics system operates with 8x8 pixel tiles. There is space for 384
        // tiles. 256 tiles map be used in a map. Map 1 uses tile numbers 0 to 255,
        // other uses -128 to 127.
@@ -21,46 +129,44 @@ private:
        // 0x9000 - 0x97FF: Tile Set #0: tiles 0-127
        // 0x9800 - 0x9BFF: Tile map #0
        // 0x9C00 - 0x9FFF: Tile map #1
-       std::tr1::shared_ptr<MemoryMap::Memory> m_vram;
+       uint8_t* m_vram;
 
-       enum SpriteOptions
-       {
-               SPRITE_PALETTE = 0x10, // 0 = Pallete #0, 1 = Pallete #2
-               SPRITE_XFLIP = 0x20, // 0 = Normal, 1 = flip.
-               SPRITE_YFLIP = 0x40, // 0 = Normal, 1 = flip
-               SPRITE_PRIORITY = 0x80 // 0 = Above Background, 1 = Below background
-                                                               // (background colour 0 == transparent)
-       };
+       // Object Attribute Memory. Up to 40 sprites, which reference first Tile Set
+       uint8_t* m_oam; // 0xFE00 - 0xFE9F
 
-       struct __attribute__ ((packed)) Sprite
-       {
-               uint8_t y; // Y-coordinate - 16
-               uint8_t x; // X-coordinate - 8
-               uint8_t tile; // Tile number
-               uint8_t options;
-       }
+       // Memory-mapped IO Registers
+       class GraphicsRegisters;
+       std::shared_ptr<GraphicsRegisters> m_reg;
 
-       // Object Attribute Memory (Up to 40 sprites), as per Sprite struct.
-       std::tr1::shared_ptr<MemoryMap::Memory> m_oam; // 0xFE00 - 0xFE9F
+       // Raster display
+       bool m_backgroundRedraw;
+       Pixel m_background[256][256];
+       Pixel m_screen[144][160];
 
-       struct __attribute__((packed)) Registers
-       {
-               uint8_t LCDC;
-               uint8_t STAT;
-               uint8_t SCY;
-               uint8_t SCX;
-               uint8_t LY;
-               uint8_t LYC;
-               uint8_t DMA;
-               uint8_t OBP0;
-               uint8_t OBP1;
-               uint8_t WY;
-               uint8_t WX;
-       };
+       // Output device
+       SDL_Surface* m_surface;
 
-       // Memory-mapped IO Registers
-       std::tr1::shared_ptr<MemoryMap::Memory> m_reg;
+       // Shader program
+       GLint m_shaderProgram;
+
+       Core::CallbackId m_mode0;
+       Core::CallbackId m_mode2;
+       Core::CallbackId m_mode3;
+
+       int m_lineScrollX[154];
+       int m_lineScrollY[154];
+
+
+       bool m_frameSkip;
+       bool m_lastSkipped;
+
+       timeval m_stat_fpsTimer;
+       int m_stat_frames;
+       int m_stat_skipped;
+       int m_stat_redraw;
 };
 
+} // namespace glBoy
+
 #endif
 
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..96cb6813339240ab228b00f151c12a07712edf63 100644 (file)
@@ -0,0 +1,138 @@
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of glBoy.
+//
+//     glBoy 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 3 of the License, or
+//     (at your option) any later version.
+//
+//     glBoy 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 glBoy.  If not, see <http://www.gnu.org/licenses/>.
+
+#include "glBoy.hh"
+#include "GameboyJoypad.hh"
+#include "Core.hh"
+
+#include <algorithm>
+#include <cassert>
+#include <cstdlib>
+
+using namespace glBoy;
+
+// TODO implement interrupt! 
+
+
+GameboyJoypad::GameboyJoypad(Core& core) :
+       m_state(Joypad_Off)
+{
+       m_inputs[DOWN].sdlKey = SDLK_DOWN;
+       m_inputs[UP].sdlKey = SDLK_UP;
+       m_inputs[LEFT].sdlKey = SDLK_LEFT;
+       m_inputs[RIGHT].sdlKey = SDLK_RIGHT;
+       m_inputs[START].sdlKey = SDLK_RETURN;
+       m_inputs[SELECT].sdlKey = SDLK_BACKSPACE;
+       m_inputs[A].sdlKey = SDLK_a;
+       m_inputs[B].sdlKey = SDLK_s;
+
+       pollInput(0);
+
+       using namespace std::placeholders;
+       core.registerPeriodic(
+               32768, // 128Hz
+               0,
+               std::bind(&glBoy::GameboyJoypad::pollInput, this, _1)
+               );
+}
+
+uint8_t
+GameboyJoypad::read8(uint16_t)
+{
+       uint8_t result(0xff);
+
+       switch (m_state)
+       {
+       case Joypad_Off:
+               result = 0xff;
+               break;
+
+       case Joypad_Directional:
+               result = m_directionalState;
+               break;
+
+       case Joypad_Button:
+               result = m_buttonState;
+               break;
+       };
+       return result;
+}
+
+
+void
+GameboyJoypad::write8(uint16_t, uint8_t value)
+{
+       switch (value)
+       {
+       case 0x20:
+               m_state = Joypad_Directional;
+               break;
+       case 0x10:
+               m_state = Joypad_Button;
+               break;
+       case 0x30:
+               m_state = Joypad_Off;
+               break;
+       default:
+               assert(false);
+       }
+}
+
+void
+GameboyJoypad::pollInput(Core::CallbackId)
+{
+       SDL_Event event;
+
+       while (SDL_PollEvent(&event))
+       {
+               switch(event.type)
+               {
+                       case SDL_KEYUP:
+                       case SDL_KEYDOWN:
+                       {
+                               for (int i = 0; i < 8; ++i)
+                               {
+                                       if (event.key.keysym.sym == m_inputs[i].sdlKey)
+                                       {
+                                               m_inputs[i].depressed = (event.key.type == SDL_KEYDOWN);
+                                               break;
+                                       }
+                               }
+                       }; break;
+
+                       case SDL_QUIT:
+                               exit(0);
+                               break;
+
+                       default:
+                               break;
+               }
+       }
+
+       m_directionalState = 0xff;
+       if (m_inputs[DOWN].depressed) m_directionalState ^= 0x8;
+       if (m_inputs[UP].depressed) m_directionalState ^= 0x4;
+       if (m_inputs[LEFT].depressed) m_directionalState ^= 0x2;
+       if (m_inputs[RIGHT].depressed) m_directionalState ^= 0x1;
+
+       m_buttonState = 0xff;
+       if (m_inputs[START].depressed) m_buttonState ^= 0x8;
+       if (m_inputs[SELECT].depressed) m_buttonState ^= 0x4;
+       if (m_inputs[B].depressed) m_buttonState ^= 0x2;
+       if (m_inputs[A].depressed) m_buttonState ^= 0x1;
+}
+
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..8fafbfd3fb1a7df60d274da10c29c60a8363fccf 100644 (file)
@@ -0,0 +1,61 @@
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of glBoy.
+//
+//     glBoy 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 3 of the License, or
+//     (at your option) any later version.
+//
+//     glBoy 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 glBoy.  If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef GLBOY_GAMEBOYJOYPAD_HH
+#define GLBOY_GAMEBOYJOYPAD_HH
+
+#include "glBoy.hh"
+#include "Core.hh"
+
+#include "MemoryMap.hh"
+
+#include <SDL/SDL.h>
+
+namespace glBoy
+{
+
+class GameboyJoypad : public MemoryMap::Memory
+{
+public:
+       GameboyJoypad(Core& core);
+
+       void pollInput(Core::CallbackId id);
+
+       virtual uint8_t read8(uint16_t address);
+       virtual void write8(uint16_t address, uint8_t value);
+
+private:
+       enum State { Joypad_Off, Joypad_Directional, Joypad_Button };
+       State m_state;
+
+       struct Input
+       {
+               Input() : sdlKey(0), depressed(false) {}
+               int sdlKey;
+               bool depressed;
+       };
+
+       enum Inputs { UP = 0, DOWN, LEFT, RIGHT, A, B, START, SELECT };
+       Input m_inputs[8];
+
+       uint8_t m_buttonState;
+       uint8_t m_directionalState;
+};
+
+} // namespace glBoy
+
+#endif
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..606db98e343173e505b190a57cff36c4080a9aed 100644 (file)
@@ -0,0 +1,492 @@
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of glBoy.
+//
+//     glBoy 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 3 of the License, or
+//     (at your option) any later version.
+//
+//     glBoy 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 glBoy.  If not, see <http://www.gnu.org/licenses/>.
+
+#include "glBoy.hh"
+#include "GameboySound.hh"
+
+#include <cassert>
+#include <cstdlib>
+#include <cmath>
+
+using namespace glBoy;
+
+enum { SAMPLE_RATE = 48000 };
+
+extern "C"
+{
+       void mixaudio(void *userData, Uint8 *stream, int len)
+       {
+               GameboySound* ourObject(reinterpret_cast<GameboySound*>(userData));
+               ourObject->mixSounds(stream, len);
+       }
+}
+
+GameboySound::GameboySound(Core& core)
+{
+       extern void mixaudio(void *unused, Uint8 *stream, int len);
+       SDL_AudioSpec fmt;
+
+       fmt.freq = SAMPLE_RATE;
+       fmt.format = AUDIO_S16SYS;
+       fmt.channels = 1;
+       fmt.samples = 512;
+       fmt.callback = mixaudio;
+       fmt.userdata = this;
+
+       if ( SDL_OpenAudio(&fmt, NULL) < 0 )
+       {
+               assert(false);
+       }
+       SDL_PauseAudio(0);
+
+       using namespace std::placeholders;
+       core.registerPeriodic(
+               32768, // 128 Hz
+               0,
+               std::bind(&glBoy::GameboySound::periodicUpdate, this, _1)
+               );
+}
+
+uint8_t
+GameboySound::read8(uint16_t address)
+{
+       uint8_t result(0);
+
+       switch (address)
+       {
+       case (0x0):
+               result = m_channel1.sweep;
+               break;
+       case (0x1):
+               result = m_channel1.duty;
+               break;
+       case (0x2):
+               result = m_channel1.volume;
+               break;
+       case (0x3):
+               result = m_channel1.freqLow;
+               break;
+       case (0x4):
+               result = m_channel1.freqHigh;
+               break;
+
+       case (0x6):
+               result = m_channel2.duty;
+               break;
+       case (0x7):
+               result = m_channel2.volume;
+               break;
+       case (0x8):
+               result = m_channel2.freqLow;
+               break;
+       case (0x9):
+               result = m_channel2.freqHigh;
+               break;
+
+       case (0xA):
+               result = m_channel3.enabled;
+               break;
+       case (0xB):
+               result = m_channel3.length;
+               break;
+       case (0xC):
+               result = m_channel3.volume;
+               break;
+       case (0xD):
+               result = m_channel3.freqLow;
+               break;
+       case (0xE):
+               result = m_channel3.freqHigh;
+               break;
+
+       case (0x10):
+               result = m_channel4.length;
+               break;
+       case (0x11):
+               result = m_channel4.volume;
+               break;
+       case (0x12):
+               result = m_channel4.poly;
+               break;
+       case (0x13):
+               result = m_channel4.counter;
+               break;
+       // TODO REMOVE THIS AND IMPLEMENT EVERYTHING.
+       };
+
+       if (address >= 0x20 && address < 0x30)
+       {
+               result = m_channel3.waveTable[address - 0x20];
+       }
+       return result;
+}
+
+void
+GameboySound::write8(uint16_t address, uint8_t value)
+{
+       switch (address)
+       {
+       case (0x0):
+               m_channel1.sweep = value;
+               m_channel1.sweepDelay = 0;
+               m_channel1.sweepCount = 0;
+               break;
+       case (0x1):
+               m_channel1.duty = value;
+               break;
+       case (0x2):
+               m_channel1.volume = value;
+               break;
+       case (0x3):
+               m_channel1.freqLow = value;
+               break;
+       case (0x4):
+               m_channel1.freqHigh = value;
+
+               if (value & 0x80)
+               {
+                       m_channel1.reset();
+               }
+               break;
+
+       case (0x6):
+               m_channel2.duty = value;
+               break;
+       case (0x7):
+               m_channel2.volume = value;
+               break;
+       case (0x8):
+               m_channel2.freqLow = value;
+               break;
+       case (0x9):
+               m_channel2.freqHigh = value;
+               if (value & 0x80)
+               {
+                       m_channel2.reset();
+               }
+               break;
+
+       case (0xA):
+               m_channel3.enabled = value;
+               break;
+       case (0xB):
+               m_channel3.length= value;
+               break;
+       case (0xC):
+               m_channel3.volume = value;
+               break;
+       case (0xD):
+               m_channel3.freqLow = value;
+               break;
+       case (0xE):
+               m_channel3.freqHigh = value;
+               if (value & 0x80)
+               {
+                       m_channel3.reset();
+               }
+               break;
+
+
+       case (0x10):
+               m_channel4.length= value;
+               break;
+       case (0x11):
+               m_channel4.volume = value;
+               break;
+       case (0x12):
+               m_channel4.poly= value;
+               break;
+       case (0x13):
+               m_channel4.counter = value;
+               if (value & 0x80)
+               {
+                       m_channel4.reset();
+               }
+               break;
+
+       // TODO REMOVE THIS AND IMPLEMENT EVERYTHING.
+       };
+
+       if (address >= 0x20 && address < 0x30)
+       {
+               m_channel3.waveTable[address - 0x20] = value;
+       }
+}
+
+void GameboySound::periodicUpdate(Core::CallbackId)
+{
+       prepChannel(m_channel1);
+       prepChannel(m_channel2);
+       prepChannel3();
+       prepChannel4();
+}
+
+void
+GameboySound::mixSounds(Uint8 *stream, int len)
+{
+       for (int i = 0; i < len / 2; ++i)
+       {
+               int32_t val(0);
+               val += sample(m_channel1);
+               val += sample(m_channel2);
+               val += sampleChannel3();
+               val += sampleChannel4();
+               val >>= 2;
+               if (val > 32767) val = 32767;
+               if (val < -32768) val = -32768;
+
+//TODO ENDIAN FIAL HERE
+               stream[i * 2] = val & 0xff;
+               stream[i * 2 + 1] = val >> 8;
+       }
+
+}
+
+int16_t
+GameboySound::sample(SquareWave& channel)
+{
+       int16_t sample(0);
+
+       if (channel.currentVolume && channel.remainingLength > 0)
+       {
+               int pos = channel.squareStep / (channel.samplesPerDutyBit);
+
+               sample = channel.currentVolume * channel.currentVolume;
+               sample = sample * sample;
+               if (!((channel.dutyMask >> (7 - pos)) & 1))
+               {
+                       sample = -sample;
+               }
+
+               ++channel.squareStep;
+               if (channel.squareStep > (channel.samplesPerDutyBit * 8))
+               {
+                       channel.squareStep = 0;
+               }
+       }
+       return sample;
+}
+
+void
+GameboySound::prepChannel(SquareWave& channel)
+{
+       uint16_t freqCode(channel.freqHigh & 0x7);
+       freqCode <<= 8;
+       freqCode |= channel.freqLow;
+
+       double freq = 131072 / (2048.0 - freqCode);
+
+       if (channel.sweep & 0x3)
+       {
+               if (channel.sweepDelay >= (channel.sweep & 0x3))
+               {
+                       channel.sweepDelay = 0;
+                       ++channel.sweepCount;
+               }
+               else
+               {
+                       ++channel.sweepDelay;
+               }
+
+               double freqMod = freq / (1 << channel.sweepCount);
+
+               if (channel.sweep & 0x8)
+               {
+                       freq -= freqMod;
+               }
+               else
+               {
+                       freq += freqMod;
+               }
+       }
+
+       if (channel.volume & 0x3)
+       {
+               // volumeDelay in 1/64 of a sec. x 2, as this callback occurs every
+               // 1/128sec.
+               if (channel.volumeDelay >= ((channel.volume & 0x3) * 2))
+               {
+                       channel.volumeDelay = 0;
+                       if (channel.volume & 0x8)
+                       {
+                               if (channel.currentVolume < 0xf)
+                               {
+                                       ++channel.currentVolume;
+                               }
+                       }
+                       else
+                       {
+                               if (channel.currentVolume != 0)
+                               {
+                                       --channel.currentVolume;
+                               }
+
+                       }
+               }
+               else
+               {
+                       ++channel.volumeDelay;
+               }
+       }
+
+       if (channel.freqHigh & 0x40)
+       {
+               if (channel.remainingLength > 0) --channel.remainingLength;
+       }
+
+       channel.samplesPerDutyBit = (SAMPLE_RATE/freq) / 8;
+}
+
+int16_t
+GameboySound::sampleChannel4()
+{
+       int16_t sample(0);
+
+       if (m_channel4.currentVolume && m_channel4.remainingLength > 0)
+       {
+               sample = m_channel4.currentVolume * m_channel4.currentVolume;
+               sample = sample * sample;
+               if (!m_channel4.high)
+               {
+                       sample = -sample;
+               }
+
+               ++m_channel4.currentCounter;
+               if (m_channel4.currentCounter > m_channel4.samplesPerStep)
+               {
+                       m_channel4.high = rand() & 0x1;
+               }
+       }
+       return sample;
+}
+
+
+void
+GameboySound::prepChannel4()
+{
+       Noise& channel(m_channel4);
+
+       if (channel.volume & 0x3)
+       {
+               // volumeDelay in 1/64 of a sec. x 2, as this callback occurs every
+               // 1/128sec.
+               if (channel.volumeDelay >= ((channel.volume & 0x3) * 2))
+               {
+                       channel.volumeDelay = 0;
+                       if (channel.volume & 0x8)
+                       {
+                               if (channel.currentVolume < 0xf)
+                               {
+                                       ++channel.currentVolume;
+                               }
+                       }
+                       else
+                       {
+                               if (channel.currentVolume != 0)
+                               {
+                                       --channel.currentVolume;
+                               }
+
+                       }
+               }
+               else
+               {
+                       ++channel.volumeDelay;
+               }
+       }
+
+       if (channel.counter & 0x40)
+       {
+               if (channel.remainingLength > 0) --channel.remainingLength;
+       }
+
+       double ratio = channel.poly & 0x3;
+       if (ratio == 0) ratio = 0.5;
+       double freq = 524288 / ratio / std::pow(2.0, (channel.poly >> 4) + 1);
+       channel.samplesPerStep = (SAMPLE_RATE/freq);
+}
+
+
+void
+GameboySound::prepChannel3()
+{
+       uint16_t freqCode(m_channel3.freqHigh & 0x7);
+       freqCode <<= 8;
+       freqCode |= m_channel3.freqLow;
+
+       double freq = 65536 / (2048.0 - freqCode);
+
+       if (m_channel3.freqHigh & 0x40)
+       {
+               if (m_channel3.remainingLength > 0) --m_channel3.remainingLength;
+       }
+
+       m_channel3.samplesPerNibble = (SAMPLE_RATE/freq) / 32;
+}
+
+int16_t
+GameboySound::sampleChannel3()
+{
+       int16_t sample(0);
+
+       if      (m_channel3.remainingLength > 0 &&
+               (m_channel3.enabled & 0x80)
+               )
+       {
+               int volume((m_channel3.volume >> 5) & 0x3);
+
+               uint8_t nibble = m_channel3.waveTable[m_channel3.wavePos / 2];
+
+               if (m_channel3.wavePos & 0x1)
+               {
+                       nibble &= 0xf;
+               }
+               else
+               {
+                       nibble >>= 4;
+               }
+
+               if (volume == 0)
+               {
+                       nibble = 0;
+               }
+               else if (volume == 2)
+               {
+                       nibble >>= 1;
+               }
+               else if (volume == 3)
+               {
+                       nibble >>= 3;
+               }
+
+               // Ok, nibble is now between 0 and 0xf. Map this to full range.
+               sample = (static_cast<int16_t>(nibble) - 8) * 4096;
+
+               ++m_channel3.waveSamples;
+               if (m_channel3.waveSamples > m_channel3.samplesPerNibble * (m_channel3.wavePos + 1))
+               {
+                       m_channel3.wavePos++;
+                       if (m_channel3.wavePos >= 32)
+                       {
+                               m_channel3.waveSamples = 0;
+                               m_channel3.wavePos = 0;
+                       }
+               }
+
+       }
+       return sample;
+}
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..8a01d39fdd737c4656a32e23041c56c0f7b2320d 100644 (file)
@@ -0,0 +1,190 @@
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of glBoy.
+//
+//     glBoy 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 3 of the License, or
+//     (at your option) any later version.
+//
+//     glBoy 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 glBoy.  If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef GLBOY_GAMEBOYSOUND_HH
+#define GLBOY_GAMEBOYSOUND_HH
+
+#include "glBoy.hh"
+#include "Core.hh"
+
+#include "MemoryMap.hh"
+
+#include <SDL/SDL.h>
+
+namespace glBoy
+{
+
+class GameboySound : public MemoryMap::Memory
+{
+public:
+       GameboySound(Core& core);
+
+       void periodicUpdate(Core::CallbackId id);
+
+       virtual uint8_t read8(uint16_t address);
+       virtual void write8(uint16_t address, uint8_t value);
+
+       void mixSounds(Uint8* stream, int len);
+private:
+       int16_t getSample(uint8_t volume);
+
+       struct SquareWave
+       {
+               SquareWave() : sweep(0), duty(0), volume(0), freqLow(0), freqHigh(0)
+               {
+                       reset();
+               }
+
+               void reset()
+               {
+                       sweepDelay = 0;
+                       sweepCount = 0;
+                       volumeDelay = 0;
+                       currentVolume = volume >> 4;
+                       squareStep = 0;
+
+                       switch (duty >> 6)
+                       {
+                       case 0: dutyMask = 0x8; break;
+                       case 1: dutyMask = 0xC0; break;
+                       case 2: dutyMask = 0xF0; break;
+                       case 3: dutyMask = 0xFC; break;
+                       };
+
+                       // x 2, since callback @ 128Hz, not 256Hz.
+                       if (freqHigh & 0x40)
+                       {
+                               // Add 1, as we immediately decrement in prep method
+                               // without actually playing anything.
+                               remainingLength = ((64 - (duty & 0x3f)) / 2) + 1;
+                       }
+                       else
+                       {
+                               remainingLength = 255;
+                       }
+               }
+               // Regs.
+               uint8_t sweep;
+               uint8_t duty;
+               uint8_t volume;
+               uint8_t freqLow;
+               uint8_t freqHigh;
+
+               // Runtime
+               int sweepDelay;
+               uint32_t sweepCount;
+
+               int volumeDelay;
+               int currentVolume;
+
+               uint8_t dutyMask;
+               double samplesPerDutyBit;
+               int squareStep;
+
+               int remainingLength;
+       };
+       int16_t sample(SquareWave& channel);
+       void prepChannel(SquareWave& channel);
+
+       SquareWave m_channel1;
+       SquareWave m_channel2;
+
+       struct Noise
+       {
+               Noise() : length(0), volume(0), poly(0), counter(0) {reset(); }
+               uint8_t length;
+               uint8_t volume;
+               uint8_t poly;
+               uint8_t counter;
+
+               int currentCounter;
+               int samplesPerStep;
+               int volumeDelay;
+               int currentVolume;
+
+               int remainingLength;
+
+               bool high;
+
+               void reset()
+               {
+                       volumeDelay = 0;
+                       currentVolume = volume >> 4;
+
+                       currentCounter = 0;
+
+                       remainingLength = (64 - (length & 0x3f)) / 2;
+
+                       high = false;
+               }
+       };
+
+       Noise m_channel4;
+       int16_t sampleChannel4();
+       void prepChannel4();
+
+       struct Wave
+       {
+               Wave() : enabled(0), length(0), volume(0), freqLow(0), freqHigh(0)
+               {
+                       reset();
+               }
+
+               void reset()
+               {
+                       wavePos = 0;
+                       waveSamples = 0;
+
+                       // x 2, since callback @ 128Hz, not 256Hz.
+                       if (freqHigh & 0x40)
+                       {
+                               // Add 1, as we immediately decrement in prep method
+                               // without actually playing anything.
+                               remainingLength = ((256 - length) / 2) + 1;
+                       }
+                       else
+                       {
+                               remainingLength = 255;
+                       }
+               }
+               // Regs.
+               uint8_t enabled;
+               uint8_t length;
+               uint8_t volume;
+               uint8_t freqLow;
+               uint8_t freqHigh;
+
+               uint8_t waveTable[16];
+
+               // Runtime
+               int wavePos;
+               int waveSamples;
+               int remainingLength;
+               float samplesPerNibble;
+       };
+
+       Wave m_channel3;
+       int16_t sampleChannel3();
+       void prepChannel3();
+
+       uint8_t m_channelControl;
+       uint8_t m_outputTerminal;
+};
+
+} // namespace glBoy
+
+#endif
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..889eb2a25529217a860235b91ce0d133ea6185dc 100644 (file)
@@ -0,0 +1,160 @@
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of glBoy.
+//
+//     glBoy 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 3 of the License, or
+//     (at your option) any later version.
+//
+//     glBoy 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 glBoy.  If not, see <http://www.gnu.org/licenses/>.
+
+#include "glBoy.hh"
+#include "GameboyTimer.hh"
+#include "Log.hh"
+
+#include <algorithm>
+#include <cassert>
+
+#include <time.h>
+
+using namespace glBoy;
+
+GameboyTimer::GameboyTimer(
+       Core& core,
+       const std::function<void(void)>& setFrameSkip,
+       const std::function<void(void)>& clearFrameSkip
+       ) :
+       m_core(core),
+       m_setSkipFrame(setFrameSkip),
+       m_clearSkipFrame(clearFrameSkip)
+{
+       memset(m_reg, 0, 4);
+
+       using namespace std::placeholders;
+       core.registerPeriodic(
+               256,
+               0,
+               std::bind(&glBoy::GameboyTimer::tick, this, _1)
+               );
+
+       timespec res;
+       clock_getres(CLOCK_REALTIME, &res);
+
+       // Make sure we can perform high-resolution sleeps
+// TODO resolve this
+// meh. high resolution not all that necessary.
+       m_clockSleepDelay = std::min(res.tv_nsec / 60000, 10l);
+       //m_clockSleepDelay = res.tv_nsec / 60000;
+       m_clockSleepCounter = m_clockSleepDelay;
+
+/*
+       if (m_clockSleepDelay)
+       {
+               Log::Instance()(Log::DBG_WARNING) <<
+                       "Timer delay set to " << m_clockSleepDelay <<
+                       " nanoseconds. Enable HR timers for improved emulation" << std::endl;
+       }
+*/
+
+       clock_gettime(CLOCK_REALTIME, &m_lastSlept);
+}
+
+// Call this every 256 clock cycles. (16384Hz)
+
+void
+GameboyTimer::tick(Core::CallbackId)
+{
+       ++m_reg[DIV];
+
+       if (m_reg[TAC] & 0x4) // Timer enabled
+       {
+               int counter(m_reg[TIMA]);
+               switch (m_reg[TAC] & 0x3)
+               {
+               case 0: // 4096Hz
+                       if (m_reg[DIV] % 4 == 0) ++counter;
+                       break;
+               case 1: // 268400Hz
+                       counter += 16;
+                       break;
+               case 2: // 65536Hz
+                       counter += 4;
+                       break;
+               case 3: // 16384Hz
+                       ++counter;
+                       break;
+               }
+
+               if (counter > 255)
+               {
+                       m_reg[TIMA] = m_reg[TMA];
+                       m_core.raiseInterrupt(0x50);
+               }
+               else
+               {
+                       m_reg[TIMA] = counter;
+               }
+       }
+
+       m_lastSlept.tv_nsec += 61035;
+       if (m_lastSlept.tv_nsec > 1e9)
+       {
+               m_lastSlept.tv_nsec -= 1e9;
+               m_lastSlept.tv_sec++;
+       }
+
+       if (m_clockSleepCounter == 0)
+       {
+               timespec now;
+               clock_gettime(CLOCK_REALTIME, &now);
+               if (
+                       (m_lastSlept.tv_sec > now.tv_sec) ||
+                               (
+                                       (m_lastSlept.tv_sec == now.tv_sec) &&
+                                       (m_lastSlept.tv_nsec > now.tv_nsec)
+                               )
+                       )
+               {
+                       m_clearSkipFrame();
+                       clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &m_lastSlept, NULL);
+               }
+               else
+               {
+                       m_setSkipFrame();
+               }
+               m_clockSleepCounter = m_clockSleepDelay;
+       }
+       else
+       {
+               m_clockSleepCounter--;
+       }
+
+}
+
+uint8_t
+GameboyTimer::read8(uint16_t address)
+{
+       return m_reg[address];
+}
+
+void
+GameboyTimer::write8(uint16_t address, uint8_t value)
+{
+       if (!address)
+       {
+               // Writing any value to DIV resets it to zero.
+               m_reg[DIV] = 0;
+       }
+       else
+       {
+               m_reg[address] = value;
+       }
+}
+
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..2d5df43ebfe72c276d3e2cc259352ea40a8d21b1 100644 (file)
@@ -0,0 +1,64 @@
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of glBoy.
+//
+//     glBoy 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 3 of the License, or
+//     (at your option) any later version.
+//
+//     glBoy 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 glBoy.  If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef GLBOY_GAMEBOYTIMER_HH
+#define GLBOY_GAMEBOYTIMER_HH
+
+#include "glBoy.hh"
+#include "Core.hh"
+
+#include "MemoryMap.hh"
+
+#include <time.h>
+
+#include <SDL/SDL.h>
+
+namespace glBoy
+{
+
+class GameboyTimer : public MemoryMap::Memory
+{
+public:
+       GameboyTimer(
+               Core& core,
+               const std::function<void(void)>& setFrameSkip,
+               const std::function<void(void)>& clearFrameSkip
+               );
+
+       void tick(Core::CallbackId id);
+
+       virtual uint8_t read8(uint16_t address);
+       virtual void write8(uint16_t address, uint8_t value);
+
+private:
+       enum Reg { DIV = 0, TIMA, TMA, TAC };
+
+       Core& m_core;
+       std::function<void(void)> m_setSkipFrame;
+       std::function<void(void)> m_clearSkipFrame;
+
+       uint8_t m_reg[4];
+
+       timespec m_lastSlept;
+
+       int m_clockSleepDelay;
+       int m_clockSleepCounter;
+};
+
+} // namespace glBoy
+
+#endif
index 10c0b76a7dc4aeeabf54886a41218520cd39df92..e4acc1f759884d6c6f1883bfd58b1628236f2621 100755 (executable)
 <?xml version="1.0" encoding="UTF-8" ?>\r
 <opcodes>\r
 <!--\r
-       Z80 Core Emulation\r
-       8 bit LD instructions\r
-       Copyright 2010, Michael McMaster <email@michaelmcmaster.name>\r
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>\r
+//\r
+//     This file is part of glBoy.\r
+//\r
+//     glBoy is free software: you can redistribute it and/or modify\r
+//     it under the terms of the GNU General Public License as published by\r
+//     the Free Software Foundation, either version 3 of the License, or\r
+//     (at your option) any later version.\r
+//\r
+//     glBoy is distributed in the hope that it will be useful,\r
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+//     GNU General Public License for more details.\r
+//\r
+//     You should have received a copy of the GNU General Public License\r
+//     along with glBoy.  If not, see <http://www.gnu.org/licenses/>.\r
 -->\r
 \r
-code gen expectations:\r
-masks result in each instruction being unrolled and the mask name being replaced\r
-with an actual register\r
-operand = op8();\r
-operand="UPPERCASE" is 16bit operand. (first 8 bits = low bytes) N = op16()\r
-\r
-<registerMask name="r" bits="3">\r
-       <reg name="A" mask="111">\r
-       <reg name="B" mask="000">\r
-       <reg name="C" mask="001">\r
-       <reg name="D" mask="010">\r
-       <reg name="E" mask="011">\r
-       <reg name="H" mask="100">\r
-       <reg name="L" mask="101">\r
+<registerMask name="r" maskBits="3" regBytes="1" >\r
+       <reg name="A" mask="111" />\r
+       <reg name="B" mask="000" />\r
+       <reg name="C" mask="001" />\r
+       <reg name="D" mask="010" />\r
+       <reg name="E" mask="011" />\r
+       <reg name="H" mask="100" />\r
+       <reg name="L" mask="101" />\r
 </registerMask>\r
 \r
-<registerMask name="d" alias="s" bits="2">\r
-       <reg name="BC" mask="00">\r
-       <reg name="DE" mask="01">\r
-       <reg name="HL" mask="10">\r
-       <reg name="SP" mask="11">\r
+<registerMask name="d" maskBits="2" regBytes="2" >\r
+       <reg name="BC" mask="00" />\r
+       <reg name="DE" mask="01" />\r
+       <reg name="HL" mask="10" />\r
+       <reg name="SP" mask="11" />\r
 </registerMask>\r
 \r
-<registerMask name="q" bits="2">\r
-       <reg name="BC" mask="00">\r
-       <reg name="DE" mask="01">\r
-       <reg name="HL" mask="10">\r
-       <reg name="AF" mask="11">\r
+<registerMask name="s" maskBits="2" regBytes="2" >\r
+       <reg name="BC" mask="00" />\r
+       <reg name="DE" mask="01" />\r
+       <reg name="HL" mask="10" />\r
+       <reg name="SP" mask="11" />\r
 </registerMask>\r
 \r
-<registerMask name="p" bits="2">\r
-       <reg name="BC" mask="00">\r
-       <reg name="DE" mask="01">\r
-       <reg name="IX" mask="10">\r
-       <reg name="SP" mask="11">\r
+<registerMask name="q" maskBits="2" regBytes="2" >\r
+       <reg name="BC" mask="00" />\r
+       <reg name="DE" mask="01" />\r
+       <reg name="HL" mask="10" />\r
+       <reg name="AF" mask="11" />\r
 </registerMask>\r
 \r
-<registerMask name="R" bits="2">\r
-       <reg name="BC" mask="00">\r
-       <reg name="DE" mask="01">\r
-       <reg name="IY" mask="10">\r
-       <reg name="SP" mask="11">\r
+<registerMask name="p" maskBits="2" regBytes="2" variant="z80">\r
+       <reg name="BC" mask="00" />\r
+       <reg name="DE" mask="01" />\r
+       <reg name="IX" mask="10" />\r
+       <reg name="SP" mask="11" />\r
 </registerMask>\r
 \r
+<registerMask name="R" maskBits="2" regBytes="2" variant="z80">\r
+       <reg name="BC" mask="00" />\r
+       <reg name="DE" mask="01" />\r
+       <reg name="IY" mask="10" />\r
+       <reg name="SP" mask="11" />\r
+</registerMask>\r
+\r
+<bitMask name="b" maskBits="3"/>\r
+\r
+<!--\r
+                               8-BIT LD Instructions\r
+-->\r
+\r
 <instruction name="LD r,r'" clock="4">\r
 <opcode mask="01rr" />\r
-r = r2;\r
+       r = r2;\r
 </instruction>\r
 \r
 <instruction name="LD r,n" clock="7">\r
 <opcode mask="00r110" operand="n"/>\r
-r = n;\r
+       r = n;\r
 </instruction>\r
 \r
 <instruction name="LD r,(HL)" clock="7">\r
 <opcode mask="01r110" />\r
-r = m_mem.read8(m_reg.HL);\r
+       r = m_mem.read8(m_reg.HL);\r
 </instruction>\r
 \r
 <instruction name="LD r,(IX+d)" clock="19">\r
-<opcode prefix="0xDD" mask="01r110" operand="d"/>\r
-r = m_mem.read8(m_reg.IX, d);\r
+<opcode prefix="0xDD" mask="01r110" signed_operand="d"/>\r
+       r = m_mem.read8(m_reg.IX, d);\r
 </instruction>\r
 \r
 <instruction name="LD r,(IY+d)" clock="19">\r
-<opcode prefix="0xFD" mask="01r110" operand="d"/>\r
-r = m_mem.read8(m_reg.IY, d);\r
+<opcode prefix="0xFD" mask="01r110" signed_operand="d"/>\r
+       r = m_mem.read8(m_reg.IY, d);\r
 </instruction>\r
 \r
 <instruction name="LD (HL),r" clock="7">\r
 <opcode mask="01110r"/>\r
-m_mem.write8(m_reg.HL, r);\r
+       m_mem.write8(m_reg.HL, r);\r
 </instruction>\r
 \r
 <instruction name="LD (IX+d),r" clock="19">\r
-<opcode prefix="0xDD" mask="01110r" operand="d"/>\r
-m_mem.write8(m_reg.IX, d, r);\r
+<opcode prefix="0xDD" mask="01110r" signed_operand="d"/>\r
+       m_mem.write8(m_reg.IX, d, r);\r
 </instruction>\r
 \r
 <instruction name="LD (IY+d),r" clock="19">\r
-<opcode prefix="0xFD" mask="01110r" operand="d"/>\r
-m_mem.write8(m_reg.IY, d, r);\r
+<opcode prefix="0xFD" mask="01110r" signed_operand="d"/>\r
+       m_mem.write8(m_reg.IY, d, r);\r
 </instruction>\r
 \r
 <instruction name="LD (HL),n" clock="10">\r
 <opcode mask="00110110" operand="n"/>\r
-m_mem.write8(m_reg.HL, n);\r
+       m_mem.write8(m_reg.HL, n);\r
 </instruction>\r
 \r
-<instruction name="LD (IX+d),r" clock="19">\r
-<opcode prefix="0xDD" mask="00110110" operand="dn"/>\r
-m_mem.write8(m_reg.IX, d, n);\r
+<instruction name="LD (IX+d),n" clock="19">\r
+<opcode prefix="0xDD" mask="00110110" signed_operand="d" operand="n"/>\r
+       m_mem.write8(m_reg.IX, d, n);\r
 </instruction>\r
 \r
 <instruction name="LD (IY+d),n" clock="19">\r
-<opcode prefix="0xFD" mask="00110110" operand="dn"/>\r
-m_mem.write8(m_reg.IY, d, n);\r
+<opcode prefix="0xFD" mask="00110110" signed_operand="d" operand="n"/>\r
+       m_mem.write8(m_reg.IY, d, n);\r
 </instruction>\r
 \r
 <instruction name="LD A,(BC)" clock="7">\r
 <opcode mask="00001010"/>\r
-m_reg.A = m_mem.read8(m_reg.BC);\r
+       m_reg.A = m_mem.read8(m_reg.BC);\r
 </instruction>\r
 \r
 <instruction name="LD A,(DE)" clock="7">\r
 <opcode mask="00011010"/>\r
-m_reg.A = m_mem.read8(m_reg.DE);\r
+       m_reg.A = m_mem.read8(m_reg.DE);\r
 </instruction>\r
 \r
-<instruction name="LD A,(nn)" clock="13">\r
+<instruction name="LD A,(nn)" clock="13" variant="z80">\r
 <opcode mask="00111010" operand="N"/>\r
-m_reg.A = m_mem.read8(N);\r
+       m_reg.A = m_mem.read8(N);\r
 </instruction>\r
 \r
 <instruction name="LD (BC),A" clock="7">\r
 <opcode mask="00000010"/>\r
-m_mem.write8(m_reg.BC, m_reg.A);\r
+       m_mem.write8(m_reg.BC, m_reg.A);\r
 </instruction>\r
 \r
 <instruction name="LD (DE),A" clock="7">\r
 <opcode mask="00010010"/>\r
-m_mem.write8(m_reg.DE, m_reg.A);\r
+       m_mem.write8(m_reg.DE, m_reg.A);\r
 </instruction>\r
 \r
-<instruction name="LD (nn),A" clock="13">\r
+<instruction name="LD (nn),A" clock="13" variant="z80">\r
 <opcode mask="00110010" operand="N"/>\r
-m_mem.write8(N, m_reg.A);\r
+       m_mem.write8(N, m_reg.A);\r
 </instruction>\r
 \r
 <instruction name="LD A,I" clock="9">\r
 <opcode prefix="0xED" mask="01010111"/>\r
-m_reg.F.set(m_reg.I, *this);\r
-m_reg.A = m_reg.I;\r
+       m_reg.F.set(m_reg.I, *this);\r
+       m_reg.A = m_reg.I;\r
 </instruction>\r
 \r
 <instruction name="LD A,R" clock="9">\r
 <opcode prefix="0xED" mask="01011111"/>\r
-m_reg.F.set(m_reg.R, *this);\r
-m_reg.A = m_reg.R;\r
+       m_reg.F.set(m_reg.R, *this);\r
+       m_reg.A = m_reg.R;\r
 </instruction>\r
 \r
 <instruction name="LD I,A" clock="9">\r
 <opcode prefix="0xED" mask="01000111"/>\r
-m_reg.I = m_reg.A;\r
+       m_reg.I = m_reg.A;\r
 </instruction>\r
 \r
 <instruction name="LD R,A" clock="9">\r
 <opcode prefix="0xED" mask="01001111"/>\r
-m_reg.R = m_reg.A;\r
+       m_reg.R = m_reg.A;\r
+</instruction>\r
+\r
+<!--\r
+                               16-BIT LD Instructions\r
+-->\r
+\r
+<instruction name="LD dd,nn" clock="10">\r
+<opcode mask="00d0001" operand="N" />\r
+       d = N;\r
+</instruction>\r
+\r
+<instruction name="LD IX,nn" clock="14">\r
+<opcode prefix="0xDD" mask="00100001" operand="N" />\r
+       m_reg.IX = N;\r
+</instruction>\r
+\r
+<instruction name="LD IY,nn" clock="14">\r
+<opcode prefix="0xFD" mask="00100001" operand="N" />\r
+       m_reg.IY = N;\r
+</instruction>\r
+\r
+<instruction name="LD HL,(nn)" clock="16" variant="z80" >\r
+<opcode mask="00101010" operand="N" />\r
+       m_reg.HL = m_mem.read16(N);\r
+</instruction>\r
+\r
+<instruction name="LD dd,(nn)" clock="20">\r
+<opcode prefix="0xED" mask="01d1011" operand="N" />\r
+       d = m_mem.read16(N);\r
+</instruction>\r
+\r
+<instruction name="LD IX,(nn)" clock="20">\r
+<opcode prefix="0xDD" mask="00101010" operand="N" />\r
+       m_reg.IX = m_mem.read16(N);\r
+</instruction>\r
+\r
+<instruction name="LD IY,(nn)" clock="20">\r
+<opcode prefix="0xFD" mask="00101010" operand="N" />\r
+       m_reg.IY = m_mem.read16(N);\r
+</instruction>\r
+\r
+<instruction name="LD (nn),HL" clock="16" variant="z80">\r
+<opcode mask="00100010" operand="N" />\r
+       m_mem.write16(N, m_reg.HL);\r
+</instruction>\r
+\r
+<instruction name="LD (nn),dd" clock="20">\r
+<opcode prefix="0xED" mask="01d0011" operand="N" />\r
+       m_mem.write16(N, d);\r
+</instruction>\r
+\r
+<instruction name="LD (nn),IX" clock="20">\r
+<opcode prefix="0xDD" mask="00100010" operand="N" />\r
+       m_mem.write16(N, m_reg.IX);\r
+</instruction>\r
+\r
+<instruction name="LD (nn),IY" clock="20">\r
+<opcode prefix="0xFD" mask="00100010" operand="N" />\r
+       m_mem.write16(N, m_reg.IY);\r
+</instruction>\r
+\r
+\r
+<instruction name="LD SP,HL" clock="6">\r
+<opcode mask="11111001" />\r
+       m_reg.SP = m_reg.HL;\r
+</instruction>\r
+\r
+<instruction name="LD SP,IX" clock="10">\r
+<opcode prefix="0xDD" mask="11111001" />\r
+       m_reg.SP = m_reg.IX;\r
+</instruction>\r
+\r
+<instruction name="LD SP,IY" clock="10">\r
+<opcode prefix="0xFD" mask="11111001" />\r
+       m_reg.SP = m_reg.IY;\r
+</instruction>\r
+\r
+<instruction name="push qq" clock="11">\r
+<opcode mask="11q0101" />\r
+       m_reg.SP.dec(2);\r
+       m_mem.write16(m_reg.SP, q);\r
+</instruction>\r
+\r
+<instruction name="push IX" clock="15">\r
+<opcode prefix="0xDD" mask="11100101" />\r
+       m_reg.SP.dec(2);\r
+       m_mem.write16(m_reg.SP, m_reg.IX);\r
+</instruction>\r
+\r
+<instruction name="push IY" clock="15">\r
+<opcode prefix="0xFD" mask="11100101" />\r
+       m_reg.SP.dec(2);\r
+       m_mem.write16(m_reg.SP, m_reg.IY);\r
+</instruction>\r
+\r
+<instruction name="pop qq" clock="10">\r
+<opcode mask="11q0001" />\r
+       q = m_mem.read16(m_reg.SP);\r
+       m_reg.SP.inc(2);\r
+</instruction>\r
+\r
+<instruction name="pop IX" clock="14">\r
+<opcode prefix="0xDD" mask="11100001" />\r
+       m_reg.IX = m_mem.read16(m_reg.SP);\r
+       m_reg.SP.inc(2);\r
+</instruction>\r
+\r
+<instruction name="pop IY" clock="14">\r
+<opcode prefix="0xDD" mask="11111101" />\r
+       m_reg.IY = m_mem.read16(m_reg.SP);\r
+       m_reg.SP.inc(2);;\r
+</instruction>\r
+\r
+\r
+<!--\r
+                               Exchange, block transfer, and search\r
+-->\r
+\r
+<instruction name="EX DE,HL" clock="4" variant="z80">\r
+<opcode mask="11101011" />\r
+       DWORD tmp(m_reg.DE);\r
+       m_reg.DE = m_reg.HL;\r
+       m_reg.HL = tmp;\r
+</instruction>\r
+\r
+<instruction name="EX AF,AF'" clock="4" variant="z80">\r
+<opcode mask="00001000" />\r
+       DWORD tmp(m_reg.AF);\r
+       m_reg.AF = m_regDash.AF;\r
+       m_regDash.AF = tmp;\r
+</instruction>\r
+\r
+<instruction name="EXX" clock="4" variant="z80">\r
+<opcode mask="11011001" />\r
+       DWORD tmpBC(m_reg.BC);\r
+       DWORD tmpDE(m_reg.BC);\r
+       DWORD tmpHL(m_reg.BC);\r
+       m_reg.BC = m_regDash.BC;\r
+       m_reg.DE = m_regDash.DE;\r
+       m_reg.HL = m_regDash.HL;\r
+       m_regDash.BC = tmpBC;\r
+       m_regDash.DE = tmpDE;\r
+       m_regDash.HL = tmpHL;\r
+</instruction>\r
+\r
+<instruction name="EX (SP),HL" clock="19" variant="z80">\r
+<opcode mask="11100011" />\r
+       DWORD tmp(m_reg.HL);\r
+       m_reg.HL = m_mem.read16(m_reg.SP);\r
+       m_mem.write16(m_reg.SP, tmp);\r
+</instruction>\r
+\r
+<instruction name="EX (SP),IX" clock="23">\r
+<opcode prefix="0xDD" mask="11100011" />\r
+       DWORD tmp(m_reg.IX);\r
+       m_reg.IX = m_mem.read16(m_reg.SP);\r
+       m_mem.write16(m_reg.SP, tmp);\r
+</instruction>\r
+\r
+<instruction name="EX (SP),IY" clock="23">\r
+<opcode prefix="0xFD" mask="11100011" />\r
+       DWORD tmp(m_reg.IY);\r
+       m_reg.IY = m_mem.read16(m_reg.SP);\r
+       m_mem.write16(m_reg.SP, tmp);\r
+</instruction>\r
+\r
+<instruction name="LDI" clock="16">\r
+<opcode prefix="0xED" mask="10100000" />\r
+       m_mem.write8(m_reg.DE, m_mem.read8(m_reg.HL));\r
+       m_reg.DE.inc();\r
+       m_reg.HL.inc();\r
+       m_reg.BC.dec();\r
+       m_reg.F.setCounter(m_reg.BC);\r
+</instruction>\r
+\r
+<instruction name="LDIR" clock="0">\r
+<opcode prefix="0xED" mask="10110000" />\r
+       do\r
+       {\r
+               m_mem.write8(m_reg.DE, m_mem.read8(m_reg.HL));\r
+               m_reg.DE.inc();\r
+               m_reg.HL.inc();\r
+               m_reg.BC.dec();\r
+\r
+               clock += 21; // Increment for each loop iteration\r
+               // TODO interrupt(); // Check for interrupts at each iteration\r
+       } while (m_reg.BC.host() != 0);\r
+       m_reg.F.setCounter(m_reg.BC);\r
+       clock += 16; // Increment again when leaving loop.\r
+</instruction>\r
+\r
+<instruction name="LDD" clock="16">\r
+<opcode prefix="0xED" mask="10101000" />\r
+       m_mem.write8(m_reg.DE, m_mem.read8(m_reg.HL));\r
+       m_reg.DE.dec();\r
+       m_reg.HL.dec();\r
+       m_reg.BC.dec();\r
+       m_reg.F.setCounter(m_reg.BC);\r
+</instruction>\r
+\r
+<instruction name="LDDR" clock="0">\r
+<opcode prefix="0xED" mask="10111000" />\r
+       do\r
+       {\r
+               m_mem.write8(m_reg.DE, m_mem.read8(m_reg.HL));\r
+               m_reg.DE.dec();\r
+               m_reg.HL.dec();\r
+               m_reg.BC.dec();\r
+\r
+               clock += 21; // Increment for each loop iteration\r
+               // TODO interrupt(); // Check for interrupts at each iteration\r
+       } while (m_reg.BC.host() != 0);\r
+       m_reg.F.setCounter(m_reg.BC);\r
+       clock += 16; // Increment again when leaving loop.\r
+</instruction>\r
+\r
+<instruction name="CPI" clock="16">\r
+<opcode prefix="0xED" mask="10100001" />\r
+       m_reg.BC.dec();\r
+       m_reg.F.setSub(m_reg.A, m_mem.read8(m_reg.HL), m_reg.BC);\r
+       m_reg.HL.inc();\r
+</instruction>\r
+\r
+<instruction name="CPIR" clock="0">\r
+<opcode prefix="0xED" mask="10110001" />\r
+       bool equal;\r
+       do\r
+       {\r
+               m_reg.BC.dec();\r
+               m_reg.F.setSub(m_reg.A, m_mem.read8(m_reg.HL), m_reg.BC);\r
+               equal = (m_reg.A == m_mem.read8(m_reg.HL));\r
+               m_reg.HL.inc();\r
+\r
+               clock += 21; // Increment for each loop iteration\r
+               // TODO interrupt(); // Check for interrupts at each iteration\r
+       } while (m_reg.BC.host() != 0 &amp;&amp; !equal);\r
+       clock += 16; // Increment again when leaving loop.\r
+\r
+</instruction>\r
+\r
+<instruction name="CPD" clock="16">\r
+<opcode prefix="0xED" mask="10101001" />\r
+       m_reg.BC.dec();\r
+       m_reg.F.setSub(m_reg.A, m_mem.read8(m_reg.HL), m_reg.BC);\r
+       m_reg.HL.dec();\r
+</instruction>\r
+\r
+<instruction name="CPDR" clock="0">\r
+<opcode prefix="0xED" mask="10111001" />\r
+       bool equal;\r
+       do\r
+       {\r
+               m_reg.BC.dec();\r
+               m_reg.F.setSub(m_reg.A, m_mem.read8(m_reg.HL), m_reg.BC);\r
+               equal = (m_reg.A == m_mem.read8(m_reg.HL));\r
+               m_reg.HL.dec();\r
+\r
+               clock += 21; // Increment for each loop iteration\r
+               // TODO interrupt(); // Check for interrupts at each iteration\r
+       } while (m_reg.BC.host() != 0 &amp;&amp; !equal);\r
+       clock += 16; // Increment again when leaving loop.\r
+\r
+</instruction>\r
+\r
+<!--\r
+                               8-bit arithmetic\r
+-->\r
+<instruction name="ADD A,r" clock="4">\r
+<opcode mask="10000r" />\r
+       m_reg.A = ALU::add(m_reg.A, r, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="ADD A,n" clock="7">\r
+<opcode mask="11000110" operand="n" />\r
+       m_reg.A = ALU::add(m_reg.A, n, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="ADD A,(HL)" clock="7">\r
+<opcode mask="10000110" />\r
+       m_reg.A = ALU::add(m_reg.A, m_mem.read8(m_reg.HL), m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="ADD A,(IX+d)" clock="19">\r
+<opcode prefix="0xDD" mask="11011101" signed_operand="d" />\r
+       m_reg.A = ALU::add(m_reg.A, m_mem.read8(m_reg.IX, d), m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="ADD A,(IY+d)" clock="19">\r
+<opcode prefix="0xFD" mask="10000110" signed_operand="d" />\r
+       m_reg.A = ALU::add(m_reg.A, m_mem.read8(m_reg.IY, d), m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="ADC A,r" clock="4">\r
+<opcode mask="10001r" />\r
+       m_reg.A = ALU::adc(m_reg.A, r, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="ADC A,n" clock="7">\r
+<opcode mask="11001110" operand="n" />\r
+       m_reg.A = ALU::adc(m_reg.A, n, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="ADC A,(HL)" clock="7">\r
+<opcode mask="10001110" />\r
+       m_reg.A = ALU::adc(m_reg.A, m_mem.read8(m_reg.HL), m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="ADC A,(IX+d)" clock="19">\r
+<opcode prefix="0xDD" mask="10001110" signed_operand="d" />\r
+       m_reg.A = ALU::adc(m_reg.A, m_mem.read8(m_reg.IX, d), m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="ADC A,(IY+d)" clock="19">\r
+<opcode prefix="0xFD" mask="10001110" signed_operand="d" />\r
+       m_reg.A = ALU::adc(m_reg.A, m_mem.read8(m_reg.IY, d), m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="SUB A,r" clock="4">\r
+<opcode mask="10010r" />\r
+       m_reg.A = ALU::sub(m_reg.A, r, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="SUB A,n" clock="7">\r
+<opcode mask="11010110" operand="n" />\r
+       m_reg.A = ALU::sub(m_reg.A, n, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="SUB A,(HL)" clock="7">\r
+<opcode mask="10010110" />\r
+       m_reg.A = ALU::sub(m_reg.A, m_mem.read8(m_reg.HL), m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="SUB A,(IX+d)" clock="19">\r
+<opcode prefix="0xDD" mask="10010110" signed_operand="d" />\r
+       m_reg.A = ALU::sub(m_reg.A, m_mem.read8(m_reg.IX, d), m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="SUB A,(IY+d)" clock="19">\r
+<opcode prefix="0xFD" mask="10010110" signed_operand="d" />\r
+       m_reg.A = ALU::sub(m_reg.A, m_mem.read8(m_reg.IY, d), m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="SBC A,r" clock="4">\r
+<opcode mask="10011r" />\r
+       m_reg.A = ALU::sbc(m_reg.A, r, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="SBC A,n" clock="7">\r
+<opcode mask="11011110" operand="n" />\r
+       m_reg.A = ALU::sbc(m_reg.A, n, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="SBC A,(HL)" clock="7">\r
+<opcode mask="10011110" />\r
+       m_reg.A = ALU::sbc(m_reg.A, m_mem.read8(m_reg.HL), m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="SBC A,(IX+d)" clock="19">\r
+<opcode prefix="0xDD" mask="10011110" signed_operand="d" />\r
+       m_reg.A = ALU::sbc(m_reg.A, m_mem.read8(m_reg.IX, d), m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="SBC A,(IY+d)" clock="19">\r
+<opcode prefix="0xFD" mask="10011110" signed_operand="d" />\r
+       m_reg.A = ALU::sbc(m_reg.A, m_mem.read8(m_reg.IY, d), m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="AND A,r" clock="4">\r
+<opcode mask="10100r" />\r
+       m_reg.A = ALU::bitwiseAnd(m_reg.A, r, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="AND A,n" clock="7">\r
+<opcode mask="11100110" operand="n" />\r
+       m_reg.A = ALU::bitwiseAnd(m_reg.A, n, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="AND A,(HL)" clock="7">\r
+<opcode mask="10100110" />\r
+       m_reg.A = ALU::bitwiseAnd(m_reg.A, m_mem.read8(m_reg.HL), m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="AND A,(IX+d)" clock="19">\r
+<opcode prefix="0xDD" mask="10100110" signed_operand="d" />\r
+       m_reg.A = ALU::bitwiseAnd(m_reg.A, m_mem.read8(m_reg.IX, d), m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="AND A,(IY+d)" clock="19">\r
+<opcode prefix="0xFD" mask="10100110" signed_operand="d" />\r
+       m_reg.A = ALU::bitwiseAnd(m_reg.A, m_mem.read8(m_reg.IY, d), m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="OR A,r" clock="4">\r
+<opcode mask="10110r" />\r
+       m_reg.A = ALU::bitwiseOr(m_reg.A, r, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="OR A,n" clock="7">\r
+<opcode mask="11110110" operand="n" />\r
+       m_reg.A = ALU::bitwiseOr(m_reg.A, n, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="OR A,(HL)" clock="7">\r
+<opcode mask="10110110" />\r
+       m_reg.A = ALU::bitwiseOr(m_reg.A, m_mem.read8(m_reg.HL), m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="OR A,(IX+d)" clock="19">\r
+<opcode prefix="0xDD" mask="10110110" signed_operand="d" />\r
+       m_reg.A = ALU::bitwiseOr(m_reg.A, m_mem.read8(m_reg.IX, d), m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="OR A,(IY+d)" clock="19">\r
+<opcode prefix="0xFD" mask="10110110" signed_operand="d" />\r
+       m_reg.A = ALU::bitwiseOr(m_reg.A, m_mem.read8(m_reg.IY, d), m_reg.F);\r
+</instruction>\r
+\r
+<!-- xor opcodes are wrong - they match the or opcodes in the Z80 manual! -->\r
+<instruction name="XOR A,r" clock="4">\r
+<opcode mask="10101r" />\r
+       m_reg.A = ALU::bitwiseXor(m_reg.A, r, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="XOR A,n" clock="7">\r
+<opcode mask="11101110" operand="n" />\r
+       m_reg.A = ALU::bitwiseXor(m_reg.A, n, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="XOR A,(HL)" clock="7">\r
+<opcode mask="10101110" />\r
+       m_reg.A = ALU::bitwiseXor(m_reg.A, m_mem.read8(m_reg.HL), m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="XOR A,(IX+d)" clock="19">\r
+<opcode prefix="0xDD" mask="10101110" signed_operand="d" />\r
+       m_reg.A = ALU::bitwiseXor(m_reg.A, m_mem.read8(m_reg.IX, d), m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="XOR A,(IY+d)" clock="19">\r
+<opcode prefix="0xFD" mask="10101110" signed_operand="d" />\r
+       m_reg.A = ALU::bitwiseXor(m_reg.A, m_mem.read8(m_reg.IY, d), m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="CP r" clock="4">\r
+<opcode mask="10111r" />\r
+       ALU::sub(m_reg.A, r, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="CP n" clock="7">\r
+<opcode mask="11111110" operand="n" />\r
+       ALU::sub(m_reg.A, n, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="CP (HL)" clock="7">\r
+<opcode mask="10111110" />\r
+       ALU::sub(m_reg.A, m_mem.read8(m_reg.HL), m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="CP (IX+d)" clock="19">\r
+<opcode prefix="0xDD" mask="10111110" signed_operand="d" />\r
+       ALU::sub(m_reg.A, m_mem.read8(m_reg.IX, d), m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="CP (IY+d)" clock="19">\r
+<opcode prefix="0xFD" mask="10111110" signed_operand="d" />\r
+       ALU::sub(m_reg.A, m_mem.read8(m_reg.IY, d), m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="INC r" clock="4">\r
+<opcode mask="00r100" />\r
+       r = ALU::add(r, 1, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="INC (HL)" clock="11">\r
+<opcode mask="00110100" />\r
+       m_mem.write8(m_reg.HL, ALU::add(m_mem.read8(m_reg.HL), 1, m_reg.F));\r
+</instruction>\r
+\r
+<instruction name="INC (IX+d)" clock="23">\r
+<opcode prefix="0xDD" mask="00110100" signed_operand="d" />\r
+       m_mem.write8(m_reg.IX, d, ALU::add(m_mem.read8(m_reg.IX, d), 1, m_reg.F));\r
+</instruction>\r
+\r
+<instruction name="INC (IY+d)" clock="23">\r
+<opcode prefix="0xFD" mask="00110100" signed_operand="d" />\r
+       m_mem.write8(m_reg.IY, d, ALU::add(m_mem.read8(m_reg.IY, d), 1, m_reg.F));\r
+</instruction>\r
+\r
+<instruction name="DEC r" clock="4">\r
+<opcode mask="00r101" />\r
+       r = ALU::sub(r, 1, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="DEC (HL)" clock="11">\r
+<opcode mask="00110101" />\r
+       m_mem.write8(m_reg.HL, ALU::sub(m_mem.read8(m_reg.HL), 1, m_reg.F));\r
+</instruction>\r
+\r
+<instruction name="DEC (IX+d)" clock="23">\r
+<opcode prefix="0xDD" mask="00110101" signed_operand="d" />\r
+       m_mem.write8(m_reg.IX, d, ALU::sub(m_mem.read8(m_reg.IX, d), 1, m_reg.F));\r
+</instruction>\r
+\r
+<instruction name="DEC (IY+d)" clock="23">\r
+<opcode prefix="0xFD" mask="00110101" signed_operand="d" />\r
+       m_mem.write8(m_reg.IY, d, ALU::sub(m_mem.read8(m_reg.IY, d), 1, m_reg.F));\r
+</instruction>\r
+\r
+\r
+<!--\r
+                               General Purpose Arithmetic and CPU Control Instructions\r
+-->\r
+\r
+<instruction name="DAA" clock="4">\r
+<opcode mask="00100111"/>\r
+       m_reg.A = DAA(m_reg.A, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="CPL" clock="4">\r
+<opcode mask="00101111"/>\r
+       m_reg.A = ~m_reg.A;\r
+       m_reg.F.H = 1;\r
+       m_reg.F.N = 1;\r
+</instruction>\r
+\r
+<instruction name="NEG" clock="8">\r
+<opcode prefix="0xED" mask="01000100"/>\r
+       m_reg.A = ALU::sub(0, m_reg.A, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="CCF" clock="4">\r
+<opcode mask="00111111"/>\r
+       m_reg.F.C = (m_reg.F.C) ? 0 : 1;\r
+       m_reg.F.N = 0;\r
+</instruction>\r
+\r
+<instruction name="SCF" clock="4">\r
+<opcode mask="00110111"/>\r
+       m_reg.F.H = 0;\r
+       m_reg.F.N = 0;\r
+       m_reg.F.C = 1;\r
+</instruction>\r
+\r
+<instruction name="NOP" clock="4">\r
+<opcode mask="00000000"/>\r
+       // Do nothing\r
+</instruction>\r
+\r
+<instruction name="HALT" clock="4">\r
+<opcode mask="01110110"/>\r
+       halt();\r
+</instruction>\r
+\r
+<instruction name="DI" clock="4">\r
+<opcode mask="11110011"/>\r
+       m_iff1 = 0;\r
+       m_iff2 = 0;\r
+</instruction>\r
+\r
+<instruction name="EI" clock="4">\r
+<opcode mask="11111011"/>\r
+       m_iff1 = 1;\r
+       m_iff2 = 1;\r
+</instruction>\r
+\r
+<instruction name="IM 0" clock="8">\r
+<opcode prefix="0xED" mask="01000110"/>\r
+       m_interruptMode = IM_0;\r
+</instruction>\r
+\r
+<instruction name="IM 1" clock="8">\r
+<opcode prefix="0xED" mask="01010110"/>\r
+       m_interruptMode = IM_1;\r
+</instruction>\r
+\r
+<instruction name="IM 2" clock="8">\r
+<opcode prefix="0xED" mask="01011110"/>\r
+       m_interruptMode = IM_2;\r
+</instruction>\r
+\r
+<!--\r
+                               16-bit Arithmetic Instructions\r
+-->\r
+\r
+<instruction name="ADD HL, ss" clock="11">\r
+<opcode mask="00s1001"/>\r
+       m_reg.HL = ALU::add(m_reg.HL, s, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="ADC HL, ss" clock="15">\r
+<opcode prefix="0xED" mask="01s1010"/>\r
+       m_reg.HL = ALU::adc(m_reg.HL, s, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="SBC HL, ss" clock="15">\r
+<opcode prefix="0xED" mask="01s0010"/>\r
+       m_reg.HL = ALU::sbc(m_reg.HL, s, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="ADD IX, pp" clock="15">\r
+<opcode prefix="0xDD" mask="00p1001"/>\r
+       m_reg.IX = ALU::add(m_reg.IX, p, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="ADD IY, pp" clock="15">\r
+<opcode prefix="0xFD" mask="00R1001"/>\r
+       m_reg.IY = ALU::add(m_reg.IY, R, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="INC ss" clock="6">\r
+<opcode mask="00s0011"/>\r
+       s.inc();\r
+</instruction>\r
+\r
+<instruction name="INC IX" clock="10">\r
+<opcode prefix="0xDD" mask="00100011"/>\r
+       m_reg.IX.inc();\r
+</instruction>\r
+\r
+<instruction name="INC IY" clock="10">\r
+<opcode prefix="0xFD" mask="00100011"/>\r
+       m_reg.IY.inc();\r
+</instruction>\r
+\r
+<instruction name="DEC ss" clock="6">\r
+<opcode mask="00s1011"/>\r
+       s.dec();\r
+</instruction>\r
+\r
+<instruction name="DEC IX" clock="10">\r
+<opcode prefix="0xDD" mask="00101011"/>\r
+       m_reg.IX.dec();\r
+</instruction>\r
+\r
+<instruction name="DEC IY" clock="10">\r
+<opcode prefix="0xFD" mask="00101011"/>\r
+       m_reg.IY.dec();\r
+</instruction>\r
+\r
+<!--\r
+                               Rotate and Shift Instructions\r
+-->\r
+\r
+<instruction name="RLCA" clock="4">\r
+<opcode mask="00000111"/>\r
+       m_reg.A = ALU::rotateLeft(m_reg.A, m_reg.F, false);\r
+</instruction>\r
+\r
+<instruction name="RLA" clock="4">\r
+<opcode mask="00010111"/>\r
+       m_reg.A = ALU::rotateLeft(m_reg.A, m_reg.F, true);\r
+</instruction>\r
+\r
+<instruction name="RRCA" clock="4">\r
+<opcode mask="00001111"/>\r
+       m_reg.A = ALU::rotateRight(m_reg.A, m_reg.F, false);\r
+</instruction>\r
+\r
+<instruction name="RRA" clock="4">\r
+<opcode mask="00011111"/>\r
+       m_reg.A = ALU::rotateRight(m_reg.A, m_reg.F, true);\r
+</instruction>\r
+\r
+<instruction name="RLC r" clock="8">\r
+<opcode prefix="0xCB" mask="00000r"/>\r
+       r = ALU::rotateLeft(r, m_reg.F, false, true);\r
+</instruction>\r
+\r
+<instruction name="RLC (HL)" clock="15">\r
+<opcode prefix="0xCB" mask="00000110"/>\r
+       m_mem.write8(\r
+               m_reg.HL,\r
+               ALU::rotateLeft(m_mem.read8(m_reg.HL), m_reg.F, false, true)\r
+               );\r
+</instruction>\r
+\r
+<instruction name="RLC (IX + d)" clock="23">\r
+<opcode prefix="0xDDCB" mask="00000110" signed_operand="d"/>\r
+       m_mem.write8(\r
+               m_reg.IX,\r
+               d,\r
+               ALU::rotateLeft(m_mem.read8(m_reg.IX, d), m_reg.F, false, true)\r
+               );\r
+</instruction>\r
+\r
+<instruction name="RLC (IY + d)" clock="23">\r
+<opcode prefix="0xFDCB" mask="00000110" signed_operand="d"/>\r
+       m_mem.write8(\r
+               m_reg.IY,\r
+               d,\r
+               ALU::rotateLeft(m_mem.read8(m_reg.IY, d), m_reg.F, false, true)\r
+               );\r
+</instruction>\r
+\r
+<instruction name="RL r" clock="8">\r
+<opcode prefix="0xCB" mask="00010r"/>\r
+       r = ALU::rotateLeft(r, m_reg.F, true, true);\r
+</instruction>\r
+\r
+<instruction name="RL (HL)" clock="15">\r
+<opcode prefix="0xCB" mask="00010110"/>\r
+       m_mem.write8(\r
+               m_reg.HL,\r
+               ALU::rotateLeft(m_mem.read8(m_reg.HL), m_reg.F, true, true)\r
+               );\r
+</instruction>\r
+\r
+<instruction name="RL (IX + d)" clock="23">\r
+<opcode prefix="0xDDCB" mask="00010110" signed_operand="d"/>\r
+       m_mem.write8(\r
+               m_reg.IX,\r
+               d,\r
+               ALU::rotateLeft(m_mem.read8(m_reg.IX, d), m_reg.F, true, true)\r
+               );\r
+</instruction>\r
+\r
+<instruction name="RL (IY + d)" clock="23">\r
+<opcode prefix="0xFDCB" mask="00010110" signed_operand="d"/>\r
+       m_mem.write8(\r
+               m_reg.IY,\r
+               d,\r
+               ALU::rotateLeft(m_mem.read8(m_reg.IY, d), m_reg.F, true, true)\r
+               );\r
+</instruction>\r
+\r
+<instruction name="RRC r" clock="8">\r
+<opcode prefix="0xCB" mask="00001r"/>\r
+       r = ALU::rotateRight(r, m_reg.F, false, true);\r
+</instruction>\r
+\r
+<instruction name="RRC (HL)" clock="15">\r
+<opcode prefix="0xCB" mask="00001110"/>\r
+       m_mem.write8(\r
+               m_reg.HL,\r
+               ALU::rotateRight(m_mem.read8(m_reg.HL), m_reg.F, false, true)\r
+               );\r
+</instruction>\r
+\r
+<instruction name="RRC (IX + d)" clock="23">\r
+<opcode prefix="0xDDCB" mask="00001110" signed_operand="d"/>\r
+       m_mem.write8(\r
+               m_reg.IX,\r
+               d,\r
+               ALU::rotateRight(m_mem.read8(m_reg.IX, d), m_reg.F, false, true)\r
+               );\r
+</instruction>\r
+\r
+<instruction name="RRC (IY + d)" clock="23">\r
+<opcode prefix="0xFDCB" mask="00001110" signed_operand="d"/>\r
+       m_mem.write8(\r
+               m_reg.IY,\r
+               d,\r
+               ALU::rotateRight(m_mem.read8(m_reg.IY, d), m_reg.F, false, true)\r
+               );\r
+</instruction>\r
+\r
+<instruction name="RR r" clock="8">\r
+<opcode prefix="0xCB" mask="00011r"/>\r
+       r = ALU::rotateRight(r, m_reg.F, true, true);\r
+</instruction>\r
+\r
+<instruction name="RR (HL)" clock="15">\r
+<opcode prefix="0xCB" mask="00011110"/>\r
+       m_mem.write8(\r
+               m_reg.HL,\r
+               ALU::rotateRight(m_mem.read8(m_reg.HL), m_reg.F, true, true)\r
+               );\r
+</instruction>\r
+\r
+<instruction name="RR (IX + d)" clock="23">\r
+<opcode prefix="0xDDCB" mask="00011110" signed_operand="d"/>\r
+       m_mem.write8(\r
+               m_reg.IX,\r
+               d,\r
+               ALU::rotateRight(m_mem.read8(m_reg.IX, d), m_reg.F, true, true)\r
+               );\r
+</instruction>\r
+\r
+<instruction name="RR (IY + d)" clock="23">\r
+<opcode prefix="0xFDCB" mask="00011110" signed_operand="d"/>\r
+       m_mem.write8(\r
+               m_reg.IY,\r
+               d,\r
+               ALU::rotateRight(m_mem.read8(m_reg.IY, d), m_reg.F, true, true)\r
+               );\r
+</instruction>\r
+\r
+<instruction name="SLA r" clock="8">\r
+<opcode prefix="0xCB" mask="00100r"/>\r
+       r = ALU::shiftLeft(r, m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="SLA (HL)" clock="15">\r
+<opcode prefix="0xCB" mask="00100110"/>\r
+       m_mem.write8(\r
+               m_reg.HL,\r
+               ALU::shiftLeft(m_mem.read8(m_reg.HL), m_reg.F)\r
+               );\r
+</instruction>\r
+\r
+<instruction name="SLA (IX + d)" clock="23">\r
+<opcode prefix="0xDDCB" mask="00100110" signed_operand="d"/>\r
+       m_mem.write8(\r
+               m_reg.IX,\r
+               d,\r
+               ALU::shiftLeft(m_mem.read8(m_reg.IX, d), m_reg.F)\r
+               );\r
+</instruction>\r
+\r
+<instruction name="SLA (IY + d)" clock="23">\r
+<opcode prefix="0xFDCB" mask="00100110" signed_operand="d"/>\r
+       m_mem.write8(\r
+               m_reg.IY,\r
+               d,\r
+               ALU::shiftLeft(m_mem.read8(m_reg.IY, d), m_reg.F)\r
+               );\r
+</instruction>\r
+\r
+<instruction name="SRA r" clock="8">\r
+<opcode prefix="0xCB" mask="00101r"/>\r
+       r = ALU::shiftRight(r, m_reg.F, true);\r
+</instruction>\r
+\r
+<instruction name="SRA (HL)" clock="15">\r
+<opcode prefix="0xCB" mask="00101110"/>\r
+       m_mem.write8(\r
+               m_reg.HL,\r
+               ALU::shiftRight(m_mem.read8(m_reg.HL), m_reg.F, true)\r
+               );\r
+</instruction>\r
+\r
+<instruction name="SRA (IX + d)" clock="23">\r
+<opcode prefix="0xDDCB" mask="00101110" signed_operand="d"/>\r
+       m_mem.write8(\r
+               m_reg.IX,\r
+               d,\r
+               ALU::shiftRight(m_mem.read8(m_reg.IX, d), m_reg.F, true)\r
+               );\r
+</instruction>\r
+\r
+<instruction name="SRA (IY + d)" clock="23">\r
+<opcode prefix="0xFDCB" mask="00101110" signed_operand="d"/>\r
+       m_mem.write8(\r
+               m_reg.IY,\r
+               d,\r
+               ALU::shiftRight(m_mem.read8(m_reg.IY, d), m_reg.F, true)\r
+               );\r
+</instruction>\r
+\r
+<instruction name="SRL r" clock="8">\r
+<opcode prefix="0xCB" mask="00111r"/>\r
+       r = ALU::shiftRight(r, m_reg.F, false);\r
+</instruction>\r
+\r
+<instruction name="SRL (HL)" clock="15">\r
+<opcode prefix="0xCB" mask="00111110"/>\r
+       m_mem.write8(\r
+               m_reg.HL,\r
+               ALU::shiftRight(m_mem.read8(m_reg.HL), m_reg.F, false)\r
+               );\r
+</instruction>\r
+\r
+<instruction name="SRL (IX + d)" clock="23">\r
+<opcode prefix="0xDDCB" mask="00111110" signed_operand="d"/>\r
+       m_mem.write8(\r
+               m_reg.IX,\r
+               d,\r
+               ALU::shiftRight(m_mem.read8(m_reg.IX, d), m_reg.F, false)\r
+               );\r
+</instruction>\r
+\r
+<instruction name="SRL (IY + d)" clock="23">\r
+<opcode prefix="0xFDCB" mask="00111110" signed_operand="d"/>\r
+       m_mem.write8(\r
+               m_reg.IY,\r
+               d,\r
+               ALU::shiftRight(m_mem.read8(m_reg.IY, d), m_reg.F, false)\r
+               );\r
+</instruction>\r
+\r
+<instruction name="RLD" clock="18">\r
+<opcode prefix="0xED" mask="01101111"/>\r
+       uint8_t m(m_mem.read8(m_reg.HL));\r
+       ALU::RLD(m_reg.A, m, m_reg.F);\r
+       m_mem.write8(m_reg.HL, m);\r
+</instruction>\r
+\r
+<instruction name="RRD" clock="18">\r
+<opcode prefix="0xED" mask="01100111"/>\r
+       uint8_t m(m_mem.read8(m_reg.HL));\r
+       ALU::RRD(m_reg.A, m, m_reg.F);\r
+       m_mem.write8(m_reg.HL, m);\r
+</instruction>\r
+\r
+<!--\r
+                               Bit set, reset, and test instructions\r
+-->\r
+\r
+<instruction name="BIT b,r" clock="8">\r
+<opcode prefix="0xCB" mask="01br"/>\r
+       m_reg.F.Z = ((~r) &gt;&gt; b) &amp; 0x1;\r
+       m_reg.F.H = 1;\r
+       m_reg.F.N = 0;\r
+</instruction>\r
+\r
+<instruction name="BIT b,(HL)" clock="12">\r
+<opcode prefix="0xCB" mask="01b110"/>\r
+       m_reg.F.Z = ((~m_mem.read8(m_reg.HL)) &gt;&gt; b) &amp; 0x1;\r
+       m_reg.F.H = 1;\r
+       m_reg.F.N = 0;\r
+</instruction>\r
+\r
+<instruction name="BIT b,(IX+d)" clock="20">\r
+<opcode prefix="0xDDCB" mask="01b110" signed_operand="d"/>\r
+       m_reg.F.Z = ((~m_mem.read8(m_reg.IX, d)) &gt;&gt; b) &amp; 0x1;\r
+       m_reg.F.H = 1;\r
+       m_reg.F.N = 0;\r
+</instruction>\r
+\r
+<instruction name="BIT b,(IY+d)" clock="20">\r
+<opcode prefix="0xFDCB" mask="01b110" signed_operand="d"/>\r
+       m_reg.F.Z = ((~m_mem.read8(m_reg.IY, d)) &gt;&gt; b) &amp; 0x1;\r
+       m_reg.F.H = 1;\r
+       m_reg.F.N = 0;\r
+</instruction>\r
+\r
+<instruction name="SET b,r" clock="8">\r
+<opcode prefix="0xCB" mask="11br"/>\r
+       r |= (0x1 &lt;&lt; b);\r
+</instruction>\r
+\r
+<instruction name="SET b,(HL)" clock="15">\r
+<opcode prefix="0xCB" mask="11b110"/>\r
+       uint8_t tmp(m_mem.read8(m_reg.HL));\r
+       tmp |= (0x1 &lt;&lt; b);\r
+       m_mem.write8(m_reg.HL, tmp);\r
+</instruction>\r
+\r
+<instruction name="SET b,(IX+d)" clock="23">\r
+<opcode prefix="0xDDCB" mask="11b110" signed_operand="d"/>\r
+       uint8_t tmp(m_mem.read8(m_reg.IX, d));\r
+       tmp |= (0x1 &lt;&lt; b);\r
+       m_mem.write8(m_reg.IX, d, tmp);\r
+</instruction>\r
+\r
+<instruction name="SET b,(IY+d)" clock="23">\r
+<opcode prefix="0xFDCB" mask="11b110" signed_operand="d"/>\r
+       uint8_t tmp(m_mem.read8(m_reg.IY, d));\r
+       tmp |= (0x1 &lt;&lt; b);\r
+       m_mem.write8(m_reg.IY, d, tmp);\r
+</instruction>\r
+\r
+<instruction name="RES b,r" clock="8">\r
+<opcode prefix="0xCB" mask="10br"/>\r
+       r &amp;= 0xFF ^ (0x1 &lt;&lt; b);\r
+</instruction>\r
+\r
+<instruction name="RES b,(HL)" clock="15">\r
+<opcode prefix="0xCB" mask="10b110"/>\r
+       uint8_t tmp(m_mem.read8(m_reg.HL));\r
+       tmp &amp;= 0xFF ^ (0x1 &lt;&lt; b);\r
+       m_mem.write8(m_reg.HL, tmp);\r
+</instruction>\r
+\r
+<instruction name="RES b,(IX+d)" clock="23">\r
+<opcode prefix="0xDDCB" mask="10b110" signed_operand="d"/>\r
+       uint8_t tmp(m_mem.read8(m_reg.IX, d));\r
+       tmp &amp;= 0xFF ^ (0x1 &lt;&lt; b);\r
+       m_mem.write8(m_reg.IX, d, tmp);\r
+</instruction>\r
+\r
+<instruction name="RES b,(IY+d)" clock="23">\r
+<opcode prefix="0xFDCB" mask="10b110" signed_operand="d"/>\r
+       uint8_t tmp(m_mem.read8(m_reg.IY, d));\r
+       tmp &amp;= 0xFF ^ (0x1 &lt;&lt; b);\r
+       m_mem.write8(m_reg.IY, d, tmp);\r
+</instruction>\r
+\r
+<!--\r
+                               Jump Instructions\r
+-->\r
+<instruction name="JP nn" clock="10">\r
+<opcode mask="11000011" operand="N"/>\r
+       m_reg.PC = N;\r
+</instruction>\r
+\r
+<instruction name="JP cc,nn" clock="10" variant="z80">\r
+<opcode mask="11b010" operand="N"/>\r
+       switch (b)\r
+       {\r
+       case 0: if (!m_reg.F.Z) m_reg.PC = N; break;\r
+       case 1: if (m_reg.F.Z) m_reg.PC = N; break;\r
+       case 2: if (!m_reg.F.C) m_reg.PC = N; break;\r
+       case 3: if (m_reg.F.C) m_reg.PC = N; break;\r
+       case 4: if (!m_reg.F.P) m_reg.PC = N; break;\r
+       case 5: if (m_reg.F.P) m_reg.PC = N; break;\r
+       case 6: if (!m_reg.F.S) m_reg.PC = N; break;\r
+       case 7: if (m_reg.F.S) m_reg.PC = N; break;\r
+       }\r
+</instruction>\r
+\r
+<instruction name="JR e" clock="12">\r
+<opcode mask="00011000" signed_operand="e"/>\r
+       uint16_t pc = m_reg.PC.host();\r
+       pc = pc + e;\r
+       m_reg.PC = htoz(pc);\r
+</instruction>\r
+\r
+<instruction name="JR C, e" clock="7">\r
+<opcode mask="00111000" signed_operand="e"/>\r
+       if (m_reg.F.C)\r
+       {\r
+               uint16_t pc = m_reg.PC.host();\r
+               pc = pc + e;\r
+               m_reg.PC = htoz(pc);\r
+\r
+               clock += 5;\r
+       }\r
+</instruction>\r
+\r
+<instruction name="JR NC, e" clock="7">\r
+<opcode mask="00110000" signed_operand="e"/>\r
+       if (!m_reg.F.C)\r
+       {\r
+               uint16_t pc = m_reg.PC.host();\r
+               pc = pc + e;\r
+               m_reg.PC = htoz(pc);\r
+\r
+               clock += 5;\r
+       }\r
+</instruction>\r
+\r
+<instruction name="JR Z, e" clock="7">\r
+<opcode mask="00101000" signed_operand="e"/>\r
+       if (m_reg.F.Z)\r
+       {\r
+               uint16_t pc = m_reg.PC.host();\r
+               pc = pc + e;\r
+               m_reg.PC = htoz(pc);\r
+\r
+               clock += 5;\r
+       }\r
+</instruction>\r
+\r
+<instruction name="JR NZ, e" clock="7">\r
+<opcode mask="00100000" signed_operand="e"/>\r
+       if (!m_reg.F.Z)\r
+       {\r
+               uint16_t pc = m_reg.PC.host();\r
+               pc = pc + e;\r
+               m_reg.PC = htoz(pc);\r
+\r
+               clock += 5;\r
+       }\r
+</instruction>\r
+\r
+<instruction name="JP (HL)" clock="4">\r
+<opcode mask="11101001"/>\r
+       m_reg.PC = m_reg.HL;\r
+</instruction>\r
+\r
+<instruction name="JP (IX)" clock="8">\r
+<opcode prefix="0xDD" mask="11101001"/>\r
+       m_reg.PC = m_reg.IX;\r
+</instruction>\r
+\r
+<instruction name="JP (IY)" clock="8">\r
+<opcode prefix="0xFD" mask="11101001"/>\r
+       m_reg.PC = m_reg.IY;\r
+</instruction>\r
+\r
+<instruction name="DJNZ" clock="8" variant="z80" >\r
+<opcode mask="00010000" signed_operand="e"/>\r
+       --m_reg.B;\r
+       if (m_reg.B)\r
+       {\r
+               uint16_t pc = m_reg.PC.host();\r
+               pc = pc + e;\r
+               m_reg.PC = htoz(pc);\r
+\r
+               clock += 5;\r
+       }\r
+</instruction>\r
+\r
+<!--\r
+                       Call and Return Instructions\r
+-->\r
+<instruction name="CALL nn" clock="17">\r
+<opcode mask="11001101" operand="N"/>\r
+       m_reg.SP.dec(2);\r
+       m_mem.write16(m_reg.SP, m_reg.PC);\r
+       m_reg.PC = N;\r
+</instruction>\r
+\r
+<instruction name="CALL cc,nn" clock="10" variant="z80">\r
+<opcode mask="11b100" operand="N"/>\r
+       bool jump(false);\r
+       switch (b)\r
+       {\r
+       case 0: if (!m_reg.F.Z) jump = true; break;\r
+       case 1: if (m_reg.F.Z) jump = true; break;\r
+       case 2: if (!m_reg.F.C) jump = true; break;\r
+       case 3: if (m_reg.F.C) jump = true; break;\r
+       case 4: if (!m_reg.F.P) jump = true; break;\r
+       case 5: if (m_reg.F.P) jump = true; break;\r
+       case 6: if (!m_reg.F.S) jump = true; break;\r
+       case 7: if (m_reg.F.S) jump = true; break;\r
+       }\r
+\r
+       if (jump)\r
+       {\r
+               m_reg.SP.dec(2);\r
+               m_mem.write16(m_reg.SP, m_reg.PC);\r
+               m_reg.PC = N;\r
+               clock += 7;\r
+       }\r
+</instruction>\r
+\r
+<instruction name="RET" clock="10">\r
+<opcode mask="11001001" operand="N"/>\r
+       m_reg.PC = m_mem.read16(m_reg.SP);\r
+       m_reg.SP.inc(2);\r
+</instruction>\r
+\r
+<instruction name="RET cc" clock="5" variant="z80">\r
+<opcode mask="11b000"/>\r
+       bool jump(false);\r
+       switch (b)\r
+       {\r
+       case 0: if (!m_reg.F.Z) jump = true; break;\r
+       case 1: if (m_reg.F.Z) jump = true; break;\r
+       case 2: if (!m_reg.F.C) jump = true; break;\r
+       case 3: if (m_reg.F.C) jump = true; break;\r
+       case 4: if (!m_reg.F.P) jump = true; break;\r
+       case 5: if (m_reg.F.P) jump = true; break;\r
+       case 6: if (!m_reg.F.S) jump = true; break;\r
+       case 7: if (m_reg.F.S) jump = true; break;\r
+       }\r
+\r
+       if (jump)\r
+       {\r
+               m_reg.PC = m_mem.read16(m_reg.SP);\r
+               m_reg.SP.inc(2);\r
+               clock += 6;\r
+       }\r
+</instruction>\r
+\r
+<instruction name="RETI" clock="14">\r
+<opcode prefix="0xED" mask="01001101"/>\r
+       m_reg.PC = m_mem.read16(m_reg.SP);\r
+       m_reg.SP.inc(2);\r
+       m_iff1 = 1;\r
+       m_iff2 = 1;\r
+</instruction>\r
+\r
+<instruction name="RETN" clock="14">\r
+<opcode prefix="0xED" mask="01000101"/>\r
+       m_reg.PC = m_mem.read16(m_reg.SP);\r
+       m_reg.SP.inc(2);\r
+\r
+       m_iff1 = m_iff2;\r
+\r
+       // TODO interruptComplete();\r
+</instruction>\r
+\r
+<instruction name="RST p" clock="11">\r
+<opcode mask="11b111"/>\r
+       uint8_t addr;\r
+       switch (b)\r
+       {\r
+       case 0: addr = 0; break;\r
+       case 1: addr = 0x08; break;\r
+       case 2: addr = 0x10; break;\r
+       case 3: addr = 0x18; break;\r
+       case 4: addr = 0x20; break;\r
+       case 5: addr = 0x28; break;\r
+       case 6: addr = 0x30; break;\r
+       case 7: addr = 0x38; break;\r
+       }\r
+\r
+       m_reg.SP.dec(2);\r
+       m_mem.write16(m_reg.SP, m_reg.PC);\r
+       m_reg.PC.h = 0;\r
+       m_reg.PC.l = addr;\r
+</instruction>\r
+\r
+<!--\r
+                       IN/OUT Instructions\r
+\r
+TODO NOT IMPLEMENTED\r
+-->\r
+\r
+<!--\r
+                       Prefix Dispatch\r
+-->\r
+<instruction name="CB" clock="0">\r
+<opcode mask="11001011"/>\r
+       prefix = (prefix &lt;&lt; 8) | opcode;\r
+       opcode = op8();\r
+       goto *(OpcodeTableCB[opcode]);\r
+</instruction>\r
+\r
+<instruction name="DD" clock="0" variant="z80">\r
+<opcode mask="11011101"/>\r
+       prefix = (prefix &lt;&lt; 8) | opcode;\r
+       opcode = op8();\r
+       goto *(OpcodeTableDD[opcode]);\r
+</instruction>\r
+\r
+<instruction name="ED" clock="0" variant="z80">\r
+<opcode mask="11101101"/>\r
+       prefix = (prefix &lt;&lt; 8) | opcode;\r
+       opcode = op8();\r
+       goto *(OpcodeTableED[opcode]);\r
+</instruction>\r
+\r
+<instruction name="FD" clock="0" variant="z80">\r
+<opcode mask="11111101"/>\r
+       prefix = (prefix &lt;&lt; 8) | opcode;\r
+       opcode = op8();\r
+       goto *(OpcodeTableFD[opcode]);\r
+</instruction>\r
+\r
+<instruction name="DDCB" clock="0" variant="z80">\r
+<opcode prefix="0xDD" mask="11001011"/>\r
+       prefix = (prefix &lt;&lt; 8) | opcode;\r
+       doublePrefixOperand = op8();\r
+       opcode = op8();\r
+       goto *(OpcodeTableDDCB[opcode]);\r
+</instruction>\r
+\r
+<instruction name="FDCB" clock="0" variant="z80">\r
+<opcode prefix="0xFD" mask="11001011"/>\r
+       prefix = (prefix &lt;&lt; 8) | opcode;\r
+       doublePrefixOperand = op8();\r
+       opcode = op8();\r
+       goto *(OpcodeTableFDCB[opcode]);\r
+</instruction>\r
+\r
+<!--\r
+                       GB80 Instructions\r
+-->\r
+<instruction name="LD (nn),SP" clock="16" variant="gb80" >\r
+<opcode mask="00001000" operand="N" />\r
+       m_mem.write16(N, m_reg.SP);\r
+</instruction>\r
+\r
+<instruction name="LD (HLI),A" clock="16" variant="gb80">\r
+<opcode mask="00100010" />\r
+       m_mem.write8(m_reg.HL, m_reg.A);\r
+       m_reg.HL.inc();\r
+</instruction>\r
+\r
+<instruction name="LD A, (HLI)" clock="16" variant="gb80">\r
+<opcode mask="00101010" />\r
+       m_reg.A = m_mem.read8(m_reg.HL);\r
+       m_reg.HL.inc();\r
+</instruction>\r
+\r
+<instruction name="LD (HLD),A" clock="13" variant="gb80">\r
+<opcode mask="00110010"/>\r
+       m_mem.write8(m_reg.HL, m_reg.A);\r
+       m_reg.HL.dec();\r
+</instruction>\r
+\r
+<instruction name="LD A,(HLD)" clock="13" variant="gb80">\r
+<opcode mask="00111010"/>\r
+       m_reg.A = m_mem.read8(m_reg.HL);\r
+       m_reg.HL.dec();\r
+</instruction>\r
+\r
+<instruction name="RETI" clock="14" variant="gb80">\r
+<opcode mask="11011001" />\r
+       m_reg.PC = m_mem.read16(m_reg.SP);\r
+       m_reg.SP.inc(2);\r
+       m_iff1 = 1;\r
+       m_iff2 = 1;\r
+</instruction>\r
+\r
+<instruction name="STOP" clock="4" variant="gb80">\r
+<opcode mask="00010000" />\r
+       halt();\r
+</instruction>\r
+\r
+\r
+<instruction name="ADD SP,d" clock="4" variant="gb80">\r
+<opcode mask="11101000" signed_operand="d"/>\r
+       m_reg.SP = ALU::add(m_reg.SP, htoz(int16_t(d)), m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="LD (N),A" clock="4" variant="gb80">\r
+<opcode mask="11101010" operand="N"/>\r
+       m_mem.write8(N, m_reg.A);\r
 </instruction>\r
 \r
+\r
+<instruction name="LDHL SP,d" clock="4" variant="gb80">\r
+<opcode mask="11111000" signed_operand="d"/>\r
+       m_reg.HL = ALU::add(m_reg.SP, htoz(int16_t(d)), m_reg.F);\r
+</instruction>\r
+\r
+<instruction name="LD A,(N)" clock="4" variant="gb80">\r
+<opcode mask="11111010" operand="N"/>\r
+       m_reg.A = m_mem.read8(N);;\r
+</instruction>\r
+\r
+<instruction name="RET C" clock="5" variant="gb80">\r
+<opcode mask="11011000"/>\r
+       if (m_reg.F.C)\r
+       {\r
+               m_reg.PC = m_mem.read16(m_reg.SP);\r
+               m_reg.SP.inc(2);\r
+               clock += 6;\r
+       }\r
+</instruction>\r
+\r
+<instruction name="RET NC" clock="5" variant="gb80">\r
+<opcode mask="11010000"/>\r
+       if (!m_reg.F.C)\r
+       {\r
+               m_reg.PC = m_mem.read16(m_reg.SP);\r
+               m_reg.SP.inc(2);\r
+               clock += 6;\r
+       }\r
+</instruction>\r
+\r
+<instruction name="JP Z" clock="5" variant="gb80">\r
+<opcode mask="11001010" operand="N"/>\r
+       if (m_reg.F.Z)\r
+       {\r
+               m_reg.PC = N;\r
+               clock += 6;\r
+       }\r
+</instruction>\r
+\r
+<instruction name="RET Z" clock="5" variant="gb80">\r
+<opcode mask="11001000"/>\r
+       if (m_reg.F.Z)\r
+       {\r
+               m_reg.PC = m_mem.read16(m_reg.SP);\r
+               m_reg.SP.inc(2);\r
+               clock += 6;\r
+       }\r
+</instruction>\r
+\r
+<instruction name="RET NZ" clock="8" variant="gb80">\r
+<opcode mask="11000000"/>\r
+       if (!m_reg.F.Z)\r
+       {\r
+               m_reg.PC = m_mem.read16(m_reg.SP);\r
+               m_reg.SP.inc(2);\r
+               clock += 6;\r
+       }\r
+</instruction>\r
+\r
+<instruction name="CALL Z" clock="8" variant="gb80">\r
+<opcode mask="11001100" operand="N"/>\r
+       if (m_reg.F.Z)\r
+       {\r
+               m_reg.SP.dec(2);\r
+               m_mem.write16(m_reg.SP, m_reg.PC);\r
+               m_reg.PC = N;\r
+               clock += 7;\r
+       }\r
+</instruction>\r
+\r
+<instruction name="CALL NZ" clock="8" variant="gb80">\r
+<opcode mask="11000100" operand="N"/>\r
+       if (!m_reg.F.Z)\r
+       {\r
+               m_reg.SP.dec(2);\r
+               m_mem.write16(m_reg.SP, m_reg.PC);\r
+               m_reg.PC = N;\r
+               clock += 7;\r
+       }\r
+</instruction>\r
+\r
+<instruction name="CALL C" clock="8" variant="gb80">\r
+<opcode mask="11011100" operand="N"/>\r
+       if (m_reg.F.C)\r
+       {\r
+               m_reg.SP.dec(2);\r
+               m_mem.write16(m_reg.SP, m_reg.PC);\r
+               m_reg.PC = N;\r
+               clock += 7;\r
+       }\r
+</instruction>\r
+\r
+<instruction name="CALL NC" clock="8" variant="gb80">\r
+<opcode mask="11010100" operand="N"/>\r
+       if (!m_reg.F.C)\r
+       {\r
+               m_reg.SP.dec(2);\r
+               m_mem.write16(m_reg.SP, m_reg.PC);\r
+               m_reg.PC = N;\r
+               clock += 7;\r
+       }\r
+</instruction>\r
+\r
+<instruction name="JP NZ" clock="5" variant="gb80">\r
+<opcode mask="11000010" operand="N"/>\r
+       if (!m_reg.F.Z)\r
+       {\r
+               m_reg.PC = N;\r
+               clock += 6;\r
+       }\r
+</instruction>\r
+\r
+<instruction name="JP NC" clock="5" variant="gb80">\r
+<opcode mask="11010010" operand="N"/>\r
+       if (!m_reg.F.C)\r
+       {\r
+               m_reg.PC = N;\r
+               clock += 6;\r
+       }\r
+</instruction>\r
+\r
+<instruction name="JP C" clock="5" variant="gb80">\r
+<opcode mask="11011010" operand="N"/>\r
+       if (m_reg.F.C)\r
+       {\r
+               m_reg.PC = N;\r
+               clock += 6;\r
+       }\r
+</instruction>\r
+\r
+<instruction name="SWAP r" clock="8" variant="gb80">\r
+<opcode prefix="0xCB" mask="00110r"/>\r
+       uint8_t tmp(r);\r
+       r &lt;&lt;= 4;\r
+       r |= (tmp &gt;&gt; 4);\r
+\r
+       m_reg.F.Z = r == 0;\r
+       m_reg.F.N = 0;\r
+       m_reg.F.H = 0;\r
+       m_reg.F.C = 0;\r
+</instruction>\r
+\r
+<!-- Zero-page gameboy instructions -->\r
+<instruction name="LD (FF00+n),A" clock="4" variant="gb80">\r
+<opcode mask="11100000" operand="n"/>\r
+       m_mem.write8(htoz(0xFF00 + n), m_reg.A);\r
+</instruction>\r
+\r
+<instruction name="LD (FF00+C),A" clock="4" variant="gb80">\r
+<opcode mask="11100010"/>\r
+       m_mem.write8(htoz(0xFF00 + m_reg.C), m_reg.A);\r
+</instruction>\r
+\r
+\r
+<instruction name="LD A,(FF00+d)" clock="4" variant="gb80">\r
+<opcode mask="11110000" operand="n"/>\r
+       m_reg.A = m_mem.read8(htoz(0xFF00 + n));\r
+</instruction>\r
+\r
+<!-- Gameboy NOP's -->\r
+\r
+<instruction name="NOP_D3" clock="4" variant="gb80">\r
+<opcode mask="11010011"/>\r
+</instruction>\r
+\r
+<instruction name="NOP_DB" clock="4" variant="gb80">\r
+<opcode mask="11011011"/>\r
+</instruction>\r
+\r
+<instruction name="NOP_DD" clock="4" variant="gb80">\r
+<opcode mask="11011101"/>\r
+</instruction>\r
+\r
+<instruction name="NOP_E3" clock="4" variant="gb80">\r
+<opcode mask="11100011"/>\r
+</instruction>\r
+\r
+<instruction name="NOP_E4" clock="4" variant="gb80">\r
+<opcode mask="11100100"/>\r
+</instruction>\r
+\r
+<instruction name="NOP_EB" clock="4" variant="gb80">\r
+<opcode mask="11101011"/>\r
+</instruction>\r
+\r
+<instruction name="NOP_EC" clock="4" variant="gb80">\r
+<opcode mask="11101100"/>\r
+</instruction>\r
+\r
+<instruction name="NOP_F2" clock="4" variant="gb80">\r
+<opcode mask="11110010"/>\r
+</instruction>\r
+\r
+<instruction name="NOP_F4" clock="4" variant="gb80">\r
+<opcode mask="11110100"/>\r
+</instruction>\r
+\r
+<instruction name="NOP_FC" clock="4" variant="gb80">\r
+<opcode mask="11111100"/>\r
+</instruction>\r
+\r
+<instruction name="NOP_FD" clock="4" variant="gb80">\r
+<opcode mask="11111101"/>\r
+</instruction>\r
 </opcodes>\r
diff --git a/Log.cc b/Log.cc
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..b77d0ca6b5733bcd2ceeb1b1ed09956878f1c631 100644 (file)
--- a/Log.cc
+++ b/Log.cc
@@ -0,0 +1,72 @@
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of glBoy.
+//
+//     glBoy 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 3 of the License, or
+//     (at your option) any later version.
+//
+//     glBoy 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 glBoy.  If not, see <http://www.gnu.org/licenses/>.
+
+#include "Log.hh"
+
+#include <cstdlib>
+#include <iostream>
+#include <sstream>
+
+using namespace glBoy;
+
+Log&
+Log::Instance()
+{
+       static Log s_instance;
+       return s_instance;
+}
+
+Log::Log() :
+       m_levelMask(7),
+       m_nullStream(&m_nullBuf)
+{
+       char* env(getenv("GLBOY_DEBUG_LEVEL"));
+       if (env)
+       {
+               std::stringstream stream;
+               stream << env;
+
+               // If this conversion fails, level mask remains 0, which is ok.
+               stream >> m_levelMask;
+       }
+}
+
+void
+Log::setDebugLevel(uint32_t levelMask)
+{
+       m_levelMask = levelMask;
+}
+
+bool
+Log::isDebug(DebugLevel level) const
+{
+       return (m_levelMask & level) != 0;
+}
+
+std::ostream&
+Log::operator()(DebugLevel level)
+{
+       if (isDebug(level))
+       {
+               return std::cout;
+       }
+       else
+       {
+               return m_nullStream;
+       }
+}
+
diff --git a/Log.hh b/Log.hh
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1afdb656212dbe0456e2f7e0e07ee4f6e3bc9da7 100644 (file)
--- a/Log.hh
+++ b/Log.hh
@@ -0,0 +1,62 @@
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of glBoy.
+//
+//     glBoy 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 3 of the License, or
+//     (at your option) any later version.
+//
+//     glBoy 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 glBoy.  If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef GLBOY_LOG_HH
+#define GLBOY_LOG_HH
+
+#include "glBoy.hh"
+
+#include <cstdint>
+#include <ostream>
+#include <streambuf>
+
+namespace glBoy
+{
+       class Log
+       {
+       public:
+
+               enum DebugLevel 
+               {
+                       DBG_STAT = 1, DBG_ERROR = 2, DBG_WARNING = 4, DBG_MEMORY = 8, DBG_TRACE = 16
+               };
+               static Log& Instance();
+
+               void setDebugLevel(uint32_t levelMask);
+
+               bool isDebug(DebugLevel level) const;
+               std::ostream& operator()(DebugLevel level);
+
+       private:
+               Log();
+
+               Log(const Log&);
+               Log& operator=(const Log&);
+
+               uint32_t m_levelMask;
+
+               class NullBuf : public std::streambuf
+               {
+               public:
+               };
+
+               NullBuf m_nullBuf;
+               std::ostream m_nullStream;
+       };
+
+} // namespace glBoy
+#endif
diff --git a/Main.cc b/Main.cc
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..b4db93cf187e39b79987fc47e6f474bf219da391 100644 (file)
--- a/Main.cc
+++ b/Main.cc
@@ -0,0 +1,283 @@
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of glBoy.
+//
+//     glBoy 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 3 of the License, or
+//     (at your option) any later version.
+//
+//     glBoy 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 glBoy.  If not, see <http://www.gnu.org/licenses/>.
+
+
+#include "glBoy.hh"
+#include "Core.hh"
+#include "GameboyCart.hh"
+#include "GameboyGraphics.hh"
+#include "GameboyJoypad.hh"
+#include "GameboySound.hh"
+#include "GameboyTimer.hh"
+#include "RAM.hh"
+#include "ROM.hh"
+#include "Log.hh"
+
+#include <SDL/SDL.h>
+#include <SDL/SDL_opengl.h>
+#include <SDL/SDL_video.h>
+
+#include <cassert>
+#include <set>
+#include <sstream>
+
+#include <getopt.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+namespace
+{
+       SDL_Surface*
+       initOpenGL(double xScale, double yScale, bool fullscreen)
+       {
+               glBoy::Log::Instance()(glBoy::Log::DBG_STAT) << "Enabling OpenGL" << std::endl;
+
+               SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+
+               unsigned int options(SDL_OPENGL);
+               if (fullscreen)
+               {
+                       options |= SDL_FULLSCREEN;
+               }
+
+               SDL_Surface* surface(
+                       SDL_SetVideoMode(160*xScale, 144*yScale, 32, options)
+                       );
+
+               glEnable(GL_TEXTURE_2D);
+               glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+               glClear(GL_COLOR_BUFFER_BIT);
+               glDisable(GL_DEPTH_TEST);
+               glMatrixMode(GL_PROJECTION);
+               glLoadIdentity();
+               glOrtho(0.0f, 160, 144, 0.0f, -1.0f, 1.0f);
+               glMatrixMode(GL_MODELVIEW);
+               glLoadIdentity();
+
+               return surface;
+       }
+
+       void usage(const std::string& exe)
+       {
+               std::cout <<
+                       "Usage: " << exe << " "
+                               "[--scale ratio] [--width w] [--height h] \n"
+                               //"\t\t    [--resample-method={bilinear|nearest} \n"
+                               //"\t\t[--scaling-method={hq4x|scale2x|none}] "
+                               "rom.gb" << std::endl <<
+                       std::endl <<
+                               "\t--scale\t(Default=4.0)" << std::endl <<
+                                       "\t\tMultiplier applied to the display output dimensions." << std::endl <<
+                               "\t--width" << std::endl <<
+                                       "\t\tSet the display output dimensions directly" << std::endl <<
+                               "\t--height" << std::endl <<
+                                       "\t\tSet the display output dimensions directly" << std::endl;
+                               /*"\t--scaling-method\n\t\t\t(Default=\"hq4x\")" << std::endl <<
+                                       "\t\tSets the upscaling interpolation method." << std::endl <<
+                               "\t--resample-method\n\t\t\t(Default=\"bilinear\")" << std::endl <<
+                                       "\t\tSets the resampling method." << std::endl;*/
+       }
+}
+
+int main(int argc, char** argv)
+{
+       static struct option longOptions[] = 
+       {
+               {"video", 1, NULL, 'v'},
+               {"scale", 1, NULL, 's'},
+               {"width", 1, NULL, 'w'},
+               {"height", 1, NULL, 'h'},
+               //{"resample-method", 1, NULL, 'r'},
+               //{"scaling-method", 1, NULL, 'm'},
+               {NULL, 0, NULL, 0}
+       };
+
+       std::string filename("tetris.gb");
+       glBoy::GameboyGraphics::PixelScaler scaler(glBoy::GameboyGraphics::Scaler_hq4x);
+       glBoy::GameboyGraphics::ResampleMethod resampler(glBoy::GameboyGraphics::Resample_Bilinear);
+       bool fullscreen(false);
+       double scaleX(4.0);
+       double scaleY(4.0);
+
+       int optionIndex(0);
+       int c;
+       while (
+               (c = getopt_long(argc, argv, "s:w:h:", &longOptions[0], &optionIndex))
+                       != -1)
+       {
+               switch (c)
+               {
+                       case 'h':
+                       {
+                               int height(160);
+                               std::stringstream convert(optarg);
+                               convert >> height;
+                               if (convert && height > 14 && height < 1440)
+                               {
+                                       scaleY = height / 144.0;
+                               }
+                       }; break;
+                       case 'r':
+                       {
+                               if (std::string(optarg) == "bilinear")
+                               {
+                                       resampler = glBoy::GameboyGraphics::Resample_Bilinear;
+                               }
+                               else
+                               {
+                                       resampler = glBoy::GameboyGraphics::Resample_Nearest;
+                               }
+                       }; break;
+                       case 's':
+                       {
+                               double scale(1);
+                               std::stringstream convert(optarg);
+                               convert >> scale;
+                               if (convert && scale > 0.1 && scale < 10)
+                               {
+                                       scaleX = scale;
+                                       scaleY = scale;
+                               }
+                       }; break;
+                       case 'm':
+                       {
+                               if (std::string(optarg) == "hq4x")
+                               {
+                                       scaler = glBoy::GameboyGraphics::Scaler_hq4x;
+                               }
+                               else if (std::string(optarg) == "scale2x")
+                               {
+                                       scaler = glBoy::GameboyGraphics::Scaler_Scale2x;
+                               }
+                               else
+                               {
+                                       scaler = glBoy::GameboyGraphics::Scaler_None;
+                               }
+                       }; break;
+                       case 'w':
+                       {
+                               int width(160);
+                               std::stringstream convert(optarg);
+                               convert >> width;
+                               if (convert && width > 16 && width < 1600)
+                               {
+                                       scaleX = width / 160.0;
+                               }
+                       }; break;
+
+                       case '?':
+                               usage(argv[0]);
+                               exit(1);
+                               break;
+
+                       default:
+                               abort();
+               };
+       }
+
+       if (optind < argc)
+       {
+               filename = argv[optind++];
+       }
+
+       glBoy::Core core;
+       glBoy::MemoryMap& map(core.getMemoryMap());
+
+       SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO);
+
+       SDL_Surface* surface(initOpenGL(scaleX, scaleY, fullscreen));
+
+       glBoy::GameboyGraphics graphics(core, surface, scaler, resampler);
+
+       // Map the cart memory.
+       int fd(open(filename.c_str(), O_RDONLY));
+       assert(fd >= 0);
+
+       struct stat buf;
+       if (fstat(fd, &buf))
+       {
+               assert(!"Failed fstat");
+       }
+       uint8_t* cart = new uint8_t[buf.st_size];
+       read(fd, cart, buf.st_size);
+       close(fd);
+
+       glBoy::GameboyCart tmp(core, cart, buf.st_size);
+
+
+/*
+       int biosFd(open("DMG_ROM.bin", O_RDONLY));
+       uint8_t bios[256];
+       read(biosFd, bios, 256);
+       std::shared_ptr<glBoy::MemoryMap::Memory> biosMem(new glBoy::ROM(bios, 256));
+       map.map(0, 256, biosMem);
+*/
+
+       // Map the cart mem.
+       // Map the standard internal RAM, plus shadow.
+       {
+               std::shared_ptr<glBoy::MemoryMap::Memory> mem(new glBoy::RAM(8192));
+               map.map(0xC000, 8192, mem);
+               map.map(0xE000, 7680, mem);
+       }
+       // Map the "zero-page" high-speed internal ram.
+       // Most interaction between GB hardware and the program occurs here.
+       {
+               std::shared_ptr<glBoy::MemoryMap::Memory> mem(new glBoy::RAM(128));
+               map.map(0xFF80, 128, mem);
+       }
+
+       // Map the Interrupt flags. Currently handled in Core.cc
+       {
+               std::shared_ptr<glBoy::MemoryMap::Memory> mem(new glBoy::RAM(1));
+               mem->write8(0, 0);
+               map.map(0xFF0F, 1, mem);
+       }
+
+       // Map other devices
+       std::shared_ptr<glBoy::GameboyJoypad> joypad(new glBoy::GameboyJoypad(core));
+       map.map(0xFF00, 1, joypad);
+       std::shared_ptr<glBoy::GameboyTimer> timer(
+               new glBoy::GameboyTimer(
+                       core,
+                       std::bind(&glBoy::GameboyGraphics::setFrameSkip, &graphics),
+                       std::bind(&glBoy::GameboyGraphics::clearFrameSkip, &graphics)
+                       )
+               );
+       map.map(0xFF04, 4, timer);
+       std::shared_ptr<glBoy::GameboySound> sound(new glBoy::GameboySound(core));
+       map.map(0xFF10, 48, sound);
+
+       // Cart starts from offset 0x100 with a NOP; JP
+       core.getRegisters().PC = glBoy::htoz(0x100);
+       core.getRegisters().SP = glBoy::htoz(0xFFFE);
+       core.getRegisters().AF = glBoy::htoz(0x01B0);
+       core.getRegisters().BC = glBoy::htoz(0x0013);
+       core.getRegisters().DE = glBoy::htoz(0x00D8);
+       core.getRegisters().HL = glBoy::htoz(0x014D);
+
+       core.run();
+
+       std::cerr << "Done" << std::endl;
+
+       SDL_FreeSurface(surface);
+       SDL_Quit();
+}
+
index f897cc65b53230d1170900c42ede6f5ab9804d4d..37af1ed851a61f73848fe02c2a3825ff4f08e939 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,9 +1,70 @@
+#      Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+#
+#      This file is part of glBoy.
+#
+#      glBoy 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 3 of the License, or
+#      (at your option) any later version.
+#
+#      glBoy 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 glBoy.  If not, see <http://www.gnu.org/licenses/>.
 
-mm80: InstructionSet_Opcodes.cc ALU.cc Core.cc Memory.cc Registers.cc
-       g++ -std=c++0x -I. -o $@ $^
+VERSION=$(shell cat VERSION)
+
+all: glBoy
+
+OBJS = \
+       ALU.o \
+       Core.o \
+       GameboyCart.o \
+       GameboyGraphics.o \
+       GameboyJoypad.o \
+       GameboySound.o \
+       GameboyTimer.o \
+       hq4x.o \
+       Log.o \
+       Main.o \
+       MemoryMap.o \
+       RAM.o \
+       Registers.o \
+       ROM.o \
+
+DEFINES=
+# -DGLBOY_DEBUG
+
+CPPFLAGS=-DHOST_LITTLE_ENDIAN -I. $(DEFINES)
+CXXFLAGS=-g -O3 -march=native -W -Wall -Werror -std=c++0x
+
+LDFLAGS=-lSDL -lGL -lGLU -lglut
+
+glBoy: InstructionSet_Opcodes.cc $(OBJS)
+       $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@ $(OBJS)
 
 InstructionSet_Opcodes.cc: InstructionSet.opcode makeOpcodes.pl
        perl ./makeOpcodes.pl
 
-test: InstructionSet_Opcodes.cc Test.cc
-       g++ -std=c++0x -I.  -o $@ Test.cc
+Core.o: InstructionSet_Opcodes.cc
+
+test: InstructionSet_Opcodes.cc Test.cc ALU.cc
+       $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ Test.cc ALU.cc
+
+package:
+       git commit -a
+       git tag -a -f $(VERSION)
+       mkdir /tmp/glBoy-$(VERSION)
+       -rm /tmp/glBoy-$(VERSION)/*
+       cp *.cc *.hh *.opcode *.pl Makefile COPYING README VERSION /tmp/glBoy-$(VERSION)
+       tar cvf glBoy-$(VERSION).tar -C /tmp glBoy-$(VERSION)
+
+clean:
+       -rm -f *.o
+       -rm -f glBoy
+       -rm -f InstructionSet_Opcodes.cc
+       -rm -f InstructionSet_Tables.cc
+       -rm -f gmon.out
index e54bd2dde3fcf687068c08badcd753393682a83e..62647562162809b3fe70a549b5f36cf759923ddd 100644 (file)
-/** \r
- * Flat memory-model emulation.\r
- *\r
- * Authors: Michael McMaster <email@michaelmcmaster.name>\r
- * Copyright: Michael McMaster <email@michaelmcmaster.name>\r
- */\r
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>\r
+//\r
+//     This file is part of glBoy.\r
+//\r
+//     glBoy is free software: you can redistribute it and/or modify\r
+//     it under the terms of the GNU General Public License as published by\r
+//     the Free Software Foundation, either version 3 of the License, or\r
+//     (at your option) any later version.\r
+//\r
+//     glBoy is distributed in the hope that it will be useful,\r
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+//     GNU General Public License for more details.\r
+//\r
+//     You should have received a copy of the GNU General Public License\r
+//     along with glBoy.  If not, see <http://www.gnu.org/licenses/>.\r
 \r
-#include "bits/Z80.hh"\r
-#include "ByteOrder.hh"\r
-#include "Memory.hh"\r
+#include "glBoy.hh"\r
+#include "MemoryMap.hh"\r
+#include "Log.hh"\r
+#include "util.hh"\r
 \r
-#include <cstring>\r
+#include <algorithm>\r
+#include <cassert>\r
 \r
-using namespace Z80;\r
+using namespace glBoy;\r
 \r
-Memory::Memory(address_t initialSize)\r
+namespace\r
 {\r
-       m_mem.resize(initialSize);\r
+       class NullMemory : public MemoryMap::Memory\r
+       {\r
+       public:\r
+               virtual uint8_t read8(uint16_t) { return 0; }\r
+               virtual void write8(uint16_t, uint8_t) { }\r
+       };\r
+\r
+       struct NullDeleter\r
+       {\r
+               void operator()(MemoryMap::Memory*) {}\r
+       };\r
 }\r
 \r
-u8_t Memory::read8(address_t address)\r
+MemoryMap::MemoryMap()\r
 {\r
-       return m_mem[address];\r
 }\r
 \r
-u8_t Memory::read8(address_t address, offset_t offset)\r
+void\r
+MemoryMap::map(\r
+       uint16_t base,\r
+       uint16_t size,\r
+       const std::shared_ptr<Memory>& mem\r
+       )\r
 {\r
-       return m_mem[address + offset];\r
+       MappedArea mapping(base, size, mem);\r
+\r
+       MapType::iterator it(\r
+               std::lower_bound(m_map.begin(), m_map.end(), mapping)\r
+               );\r
+       m_map.insert(it, mapping);\r
+\r
+       Log::Instance()(Log::DBG_MEMORY) <<\r
+               "Registering " << glBoy::hex(base) <<\r
+               " for " << glBoy::hex(size) << " bytes."<< std::endl;\r
 }\r
 \r
-void Memory::write8(address_t address, u8_t value)\r
+void\r
+MemoryMap::remove(\r
+       uint16_t base\r
+       )\r
 {\r
-       m_mem[address] = value;\r
+       MapType::iterator it(m_map.begin());\r
+       for (; it != m_map.end() && it->base != base ; ++it) ;\r
+\r
+       if (it != m_map.end())\r
+       {\r
+               Log::Instance()(Log::DBG_MEMORY) <<\r
+                       "Removing " << glBoy::hex(it->base) <<\r
+                       " for " << glBoy::hex(it->size) << " bytes."<< std::endl;\r
+\r
+               m_map.erase(it);\r
+       }\r
 }\r
 \r
-void Memory::write8(address_t address, offset_t offset, u8_t value)\r
+\r
+const MemoryMap::MappedArea&\r
+MemoryMap::get(uint16_t address) const\r
 {\r
-       m_mem[address + offset] = value;\r
+\r
+/*\r
+       MappedArea test(address, 0, std::shared_ptr<Memory>());\r
+       MapType::const_iterator it(\r
+               std::lower_bound(m_map.begin(), m_map.end(), test)\r
+               );\r
+\r
+\r
+       if (it != m_map.end() && it->base == address)\r
+       {\r
+               return *it;\r
+       }\r
+       else if (it != m_map.begin())\r
+       {\r
+               --it;\r
+               if (address < it->base + static_cast<uint32_t>(it->size))\r
+               {\r
+                       return *it;\r
+               }\r
+       }\r
+*/\r
+\r
+\r
+       for (\r
+               MapType::const_iterator it(m_map.begin());\r
+               it != m_map.end();\r
+               ++it\r
+               )\r
+       {\r
+               if ((address >= it->base) &&\r
+                       (address < it->base + static_cast<uint32_t>(it->size))\r
+                       )\r
+               {\r
+                       return *it;\r
+               }\r
+       }\r
+\r
+       Log::Instance()(Log::DBG_MEMORY) <<\r
+               "Returning NULL memory for address " << glBoy::hex(address) << std::endl;\r
+\r
+       static NullMemory s_null;\r
+       static MappedArea s_nullArea(\r
+               0, 0, std::shared_ptr<Memory>(&s_null, NullDeleter())\r
+               );\r
+       return s_nullArea;\r
 }\r
 \r
-u16_t Memory::read16(address_t address)\r
+uint8_t\r
+MemoryMap::read8(DWORD address, int8_t offset)\r
 {\r
-       return Z80ToHost(&m_mem[address]);\r
+       uint16_t offsetAddress(address.host() + offset);\r
+       const MappedArea& mapping(get(offsetAddress));\r
+       return mapping.memory->read8(offsetAddress - mapping.base);\r
 }\r
 \r
-u16_t Memory::read16(address_t address, offset_t offset)\r
+DWORD\r
+MemoryMap::read16(DWORD address, int8_t offset)\r
 {\r
-       return Z80ToHost(&m_mem[address + offset]);\r
+       uint16_t offsetAddress(address.host() + offset);\r
+\r
+       const MappedArea& loMap(get(offsetAddress));\r
+       uint8_t lo(loMap.memory->read8(offsetAddress - loMap.base));\r
+\r
+       ++offsetAddress;\r
+       const MappedArea& hiMap(get(offsetAddress));\r
+       uint16_t hi(hiMap.memory->read8(offsetAddress - hiMap.base));\r
+\r
+       return htoz((hi << 8) | lo);\r
 }\r
 \r
-void Memory::write16(address_t address, u16_t value)\r
+void\r
+MemoryMap::write8(DWORD address, uint8_t value)\r
 {\r
-       hostToZ80(value, &m_mem[address]);\r
+       write8(address, 0, value);\r
 }\r
 \r
-void Memory::write16(address_t address, offset_t offset, u16_t value)\r
+void\r
+MemoryMap::write8(DWORD address, int8_t offset, uint8_t value)\r
 {\r
-       hostToZ80(value, &m_mem[address + offset]);\r
+       uint16_t offsetAddress(address.host() + offset);\r
+       const MappedArea& mapping(get(offsetAddress));\r
+       mapping.memory->write8(offsetAddress - mapping.base, value);\r
 }\r
 \r
+void\r
+MemoryMap::write16(DWORD address, DWORD value)\r
+{\r
+       write16(address, 0, value);\r
+}\r
+\r
+void\r
+MemoryMap::write16(DWORD address, int8_t offset, DWORD value)\r
+{\r
+       uint16_t offsetAddress(address.host() + offset);\r
+       const MappedArea& loMap(get(offsetAddress));\r
+       loMap.memory->write8(offsetAddress - loMap.base, value.l);\r
+\r
+       ++offsetAddress;\r
+       const MappedArea& hiMap(get(offsetAddress));\r
+       hiMap.memory->write8(offsetAddress - hiMap.base, value.h);\r
+}\r
+\r
+void\r
+MemoryMap::copy(uint8_t* dest, uint16_t srcAddress, int bytes)\r
+{\r
+       const MappedArea& srcMap(get(srcAddress));\r
+\r
+       assert((srcAddress + bytes) <= (srcMap.base + srcMap.size));\r
+\r
+       srcMap.memory->copy(dest, srcAddress - srcMap.base, bytes);\r
+}\r
 \r
-void Memory::copy(address_t src, address_t dst, address_t count)\r
+void\r
+MemoryMap::Memory::copy(uint8_t* dest, uint16_t address, int bytes)\r
 {\r
-       memcpy(&m_mem[dst], &m_mem[src], count);\r
+       for (int i = 0; i < bytes; ++i)\r
+       {\r
+               dest[i] = read8(address + i);\r
+       }\r
 }\r
 \r
index e54bd2dde3fcf687068c08badcd753393682a83e..7e7eb8c9e4e96e5499bfaf5581d07e18877a4b32 100644 (file)
@@ -1,66 +1,87 @@
-/** \r
- * Flat memory-model emulation.\r
- *\r
- * Authors: Michael McMaster <email@michaelmcmaster.name>\r
- * Copyright: Michael McMaster <email@michaelmcmaster.name>\r
- */\r
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>\r
+//\r
+//     This file is part of glBoy.\r
+//\r
+//     glBoy is free software: you can redistribute it and/or modify\r
+//     it under the terms of the GNU General Public License as published by\r
+//     the Free Software Foundation, either version 3 of the License, or\r
+//     (at your option) any later version.\r
+//\r
+//     glBoy is distributed in the hope that it will be useful,\r
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+//     GNU General Public License for more details.\r
+//\r
+//     You should have received a copy of the GNU General Public License\r
+//     along with glBoy.  If not, see <http://www.gnu.org/licenses/>.\r
 \r
-#include "bits/Z80.hh"\r
-#include "ByteOrder.hh"\r
-#include "Memory.hh"\r
+#ifndef GLBOY_MEMORYMAP_HH\r
+#define GLBOY_MEMORYMAP_HH\r
 \r
-#include <cstring>\r
+#include "glBoy.hh"\r
+#include "DWORD.hh"\r
 \r
-using namespace Z80;\r
-\r
-Memory::Memory(address_t initialSize)\r
+#include <memory>\r
+#include <set>\r
+#include <vector>\r
+namespace glBoy\r
 {\r
-       m_mem.resize(initialSize);\r
-}\r
 \r
-u8_t Memory::read8(address_t address)\r
+class MemoryMap\r
 {\r
-       return m_mem[address];\r
-}\r
+public:\r
+       class Memory\r
+       {\r
+       public:\r
+               virtual ~Memory() {}\r
 \r
-u8_t Memory::read8(address_t address, offset_t offset)\r
-{\r
-       return m_mem[address + offset];\r
-}\r
+               virtual uint8_t read8(uint16_t address) = 0;\r
+               virtual void write8(uint16_t address, uint8_t value) = 0;\r
+               virtual void copy(uint8_t* dest, uint16_t address, int bytes);\r
+       };\r
 \r
-void Memory::write8(address_t address, u8_t value)\r
-{\r
-       m_mem[address] = value;\r
-}\r
+       MemoryMap();\r
 \r
-void Memory::write8(address_t address, offset_t offset, u8_t value)\r
-{\r
-       m_mem[address + offset] = value;\r
-}\r
+       void map(\r
+               uint16_t base,\r
+               uint16_t size,\r
+               const std::shared_ptr<Memory>& mem\r
+               );\r
 \r
-u16_t Memory::read16(address_t address)\r
-{\r
-       return Z80ToHost(&m_mem[address]);\r
-}\r
+       void remove(uint16_t base);\r
 \r
-u16_t Memory::read16(address_t address, offset_t offset)\r
-{\r
-       return Z80ToHost(&m_mem[address + offset]);\r
-}\r
+       uint8_t read8(DWORD address, int8_t offset = 0);\r
+       void write8(DWORD address, uint8_t value);\r
+       void write8(DWORD address, int8_t offset, uint8_t value);\r
 \r
-void Memory::write16(address_t address, u16_t value)\r
-{\r
-       hostToZ80(value, &m_mem[address]);\r
-}\r
+       DWORD read16(DWORD address, int8_t offset = 0);\r
+       void write16(DWORD address, DWORD value);\r
+       void write16(DWORD address, int8_t offset, DWORD value);\r
 \r
-void Memory::write16(address_t address, offset_t offset, u16_t value)\r
-{\r
-       hostToZ80(value, &m_mem[address + offset]);\r
-}\r
+       void copy(uint8_t* dest, uint16_t srcAddress, int bytes);\r
 \r
+private:\r
+       struct MappedArea\r
+       {\r
+               MappedArea(\r
+                       uint16_t _base, uint16_t _size, std::shared_ptr<Memory> _mem\r
+                       ) :\r
+                       base(_base), size(_size), memory(_mem) {}\r
 \r
-void Memory::copy(address_t src, address_t dst, address_t count)\r
-{\r
-       memcpy(&m_mem[dst], &m_mem[src], count);\r
-}\r
+               uint16_t base;\r
+               uint16_t size;\r
+               std::shared_ptr<Memory> memory;\r
+\r
+               bool operator<(const MappedArea& b) const { return base < b.base; }\r
+       };\r
+       const MappedArea& get(uint16_t address) const;\r
+\r
+       typedef std::vector<MappedArea> MapType;\r
+       MapType m_map;\r
+       //std::set<MappedArea> m_map;\r
+};\r
+\r
+} // namespace glBoy\r
+\r
+#endif\r
 \r
diff --git a/RAM.cc b/RAM.cc
index dfd546a7a3e240be99ad44c1dc78319b88becaa6..400eb3383494507470afdf9ae3a5e4ed813e0fe6 100644 (file)
--- a/RAM.cc
+++ b/RAM.cc
@@ -1,55 +1,48 @@
-/** \r
- * Flat memory-model emulation.\r
- *\r
- * Authors: Michael McMaster <email@michaelmcmaster.name>\r
- * Copyright: Michael McMaster <email@michaelmcmaster.name>\r
- */\r
-\r
-#include "mm80.hh"\r
-#include "Memory.hh"\r
-\r
-using namespace mm80;\r
-\r
-Memory::Memory(address_t initialSize)\r
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>\r
+//\r
+//     This file is part of glBoy.\r
+//\r
+//     glBoy is free software: you can redistribute it and/or modify\r
+//     it under the terms of the GNU General Public License as published by\r
+//     the Free Software Foundation, either version 3 of the License, or\r
+//     (at your option) any later version.\r
+//\r
+//     glBoy is distributed in the hope that it will be useful,\r
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+//     GNU General Public License for more details.\r
+//\r
+//     You should have received a copy of the GNU General Public License\r
+//     along with glBoy.  If not, see <http://www.gnu.org/licenses/>.\r
+\r
+#include "glBoy.hh"\r
+#include "RAM.hh"\r
+\r
+#include <string.h>\r
+\r
+using namespace glBoy;\r
+\r
+RAM::RAM(uint16_t size)\r
 {\r
-       m_mem.resize(initialSize);\r
+       m_mem.resize(size);\r
 }\r
 \r
 uint8_t\r
-Memory::read8(DWORD address, int8_t offset)\r
+RAM::read8(uint16_t address)\r
 {\r
-       return m_mem[address.host() + offset];\r
-}\r
-\r
-DWORD\r
-Memory::read16(DWORD address, int8_t offset)\r
-{\r
-       return CreateDWORD(&m_mem[address.host() + offset]);\r
+       return m_mem[address];\r
 }\r
 \r
 void\r
-Memory::write8(DWORD address, uint8_t value)\r
+RAM::write8(uint16_t address, uint8_t value)\r
 {\r
-       m_mem[address.host()] = value;\r
+       m_mem[address] = value;\r
 }\r
 \r
-void\r
-Memory::write8(DWORD address, int8_t offset, uint8_t value)\r
-{\r
-       m_mem[address.host() + offset] = value;\r
-}\r
-\r
-void\r
-Memory::write16(DWORD address, DWORD value)\r
-{\r
-       m_mem[address.host()] = value.l;\r
-       m_mem[address.host() + 1] = value.h;\r
-}\r
 \r
 void\r
-Memory::write16(DWORD address, int8_t offset, DWORD value)\r
+RAM::copy(uint8_t* dest, uint16_t address, int bytes)\r
 {\r
-       m_mem[address.host() + offset] = value.l;\r
-       m_mem[address.host() + offset + 1] = value.h;\r
+       memcpy(dest, &m_mem[0] + address, bytes);\r
 }\r
 \r
diff --git a/RAM.hh b/RAM.hh
index 8f82353f902f8c0c564d10daff194c8f051b81da..b7ae42fc1682e4ed736da7c10f10813929c42d7a 100644 (file)
--- a/RAM.hh
+++ b/RAM.hh
@@ -1,56 +1,44 @@
-/** \r
- * Flat memory-model emulation.\r
- *\r
- * Authors: Michael McMaster <email@michaelmcmaster.name>\r
- * Copyright: Michael McMaster <email@michaelmcmaster.name>\r
- */\r
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>\r
+//\r
+//     This file is part of glBoy.\r
+//\r
+//     glBoy is free software: you can redistribute it and/or modify\r
+//     it under the terms of the GNU General Public License as published by\r
+//     the Free Software Foundation, either version 3 of the License, or\r
+//     (at your option) any later version.\r
+//\r
+//     glBoy is distributed in the hope that it will be useful,\r
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+//     GNU General Public License for more details.\r
+//\r
+//     You should have received a copy of the GNU General Public License\r
+//     along with glBoy.  If not, see <http://www.gnu.org/licenses/>.\r
+\r
+#ifndef GLBOY_RAM_HH\r
+#define GLBOY_RAM_HH\r
+\r
+#include "glBoy.hh"\r
+#include "MemoryMap.hh"\r
 \r
-#ifndef MM80_MEMORYMAP_HH\r
-#define MM80_MEMORYMAP_HH\r
-\r
-#include "mm80.hh"\r
-#include "DWORD.hh"\r
-\r
-#include <memory>\r
 #include <vector>\r
-namespace mm80\r
+namespace glBoy\r
 {\r
 \r
-class MemoryMap\r
+class RAM : public MemoryMap::Memory\r
 {\r
 public:\r
-       class Memory\r
-       {\r
-       public:\r
-               virtual ~Memory;\r
-\r
-               uint8_t read8(DWORD address) = 0;\r
-               void write8(DWORD address, uint8_t value) = 0;\r
-       };\r
-\r
-       Memory();\r
+       RAM(uint16_t size);\r
+       virtual uint8_t read8(uint16_t address);\r
+       virtual void write8(uint16_t address, uint8_t value);\r
+       virtual void copy(uint8_t* dest, uint16_t address, int bytes);\r
 \r
-       void map(\r
-               address_t base,\r
-               address_t size,\r
-               const std::tr1::shared_ptr<Memory>& mem\r
-               );\r
-\r
-       uint8_t read8(DWORD address, int8_t offset = 0);\r
-       void write8(DWORD address, uint8_t value);\r
-       void write8(DWORD address, int8_t offset, uint8_t value);\r
-\r
-       DWORD read16(DWORD address, int8_t offset = 0);\r
-       void write16(DWORD address, DWORD value);\r
-       void write16(DWORD address, int8_t offset, DWORD value);\r
 \r
 private:\r
-\r
-       std::vector<uint8_t> m_map;\r
        std::vector<uint8_t> m_mem;\r
 };\r
 \r
-} // namespace mm80\r
+} // namespace glBoy\r
 \r
 #endif\r
 \r
diff --git a/README b/README
index 034c583c6ff678cd529008f5bdf1e1382287998d..e33c6daeed4e0c462a708af7d02dda2868901fe6 100644 (file)
--- a/README
+++ b/README
@@ -1,5 +1,28 @@
-Third attempt at a Z80 CPU emulator.
-email@michaelmcmaster.name 2008
+glBoy
+Original gameboy emulator.
 
-Language choice: C++
+Usage:
+Usage: ./glBoy [--scale ratio] [--width w] [--height h] rom.gb
+
+       --scale (Default=4.0)
+               Multiplier applied to the display output dimensions.
+       --width
+               Set the display output dimensions directly
+       --height
+               Set the display output dimensions directly
+
+
+Features
+       - MBC1 emulation
+       - Sound
+       - OpenGL hq4x scaling
+
+Known Issues
+       - The emulator crashes on some games due to incorrect z80
+       emulation.
+
+Missing
+       - MBC2/3/4 emulation
+       - Saved games
+       - Serial I/O
 
diff --git a/ROM.cc b/ROM.cc
index 4c3f03df7b8a495833f1977da60f16ec2c6d02b5..23fd63765825afe8646ed9d5a0de0fb807da9027 100644 (file)
--- a/ROM.cc
+++ b/ROM.cc
@@ -1,22 +1,53 @@
-#include "mm80.hh"\r
-#include "RAM.hh"\r
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>\r
+//\r
+//     This file is part of glBoy.\r
+//\r
+//     glBoy is free software: you can redistribute it and/or modify\r
+//     it under the terms of the GNU General Public License as published by\r
+//     the Free Software Foundation, either version 3 of the License, or\r
+//     (at your option) any later version.\r
+//\r
+//     glBoy is distributed in the hope that it will be useful,\r
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+//     GNU General Public License for more details.\r
+//\r
+//     You should have received a copy of the GNU General Public License\r
+//     along with glBoy.  If not, see <http://www.gnu.org/licenses/>.\r
 \r
-using namespace mm80;\r
+#include "glBoy.hh"\r
+#include "ROM.hh"\r
+#include "Log.hh"\r
 \r
-RAM::RAM(uint16_t size)\r
+#include <string.h>\r
+\r
+#include <cassert>\r
+\r
+using namespace glBoy;\r
+\r
+ROM::ROM(const uint8_t* data, uint16_t size) :\r
+       m_mem(data, data + size)\r
 {\r
-       m_mem.resize(size);\r
 }\r
 \r
 uint8_t\r
-RAM::read8(uint16_t address)\r
+ROM::read8(uint16_t address)\r
 {\r
        return m_mem[address];\r
 }\r
 \r
 void\r
-RAM::write8(uint16_t address, uint8_t value)\r
+ROM::write8(uint16_t address, uint8_t)\r
+{\r
+       //assert(false);\r
+       Log::Instance()(Log::DBG_MEMORY) <<\r
+               "Attempt to write to read-only address " << address <<\r
+               std::endl;\r
+}\r
+\r
+void\r
+ROM::copy(uint8_t* dest, uint16_t address, int bytes)\r
 {\r
-       m_mem[address] = value;\r
+       memcpy(dest, &m_mem[0] + address, bytes);\r
 }\r
 \r
diff --git a/ROM.hh b/ROM.hh
index 812bfc78e3e4fcd70537f103f8e01681f979e66b..1e7172d76cfd43050c5c39b7622513ee5fe13c5f 100644 (file)
--- a/ROM.hh
+++ b/ROM.hh
@@ -1,25 +1,43 @@
-#ifndef MM80_RAM_HH\r
-#define MM80_RAM_HH\r
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>\r
+//\r
+//     This file is part of glBoy.\r
+//\r
+//     glBoy is free software: you can redistribute it and/or modify\r
+//     it under the terms of the GNU General Public License as published by\r
+//     the Free Software Foundation, either version 3 of the License, or\r
+//     (at your option) any later version.\r
+//\r
+//     glBoy is distributed in the hope that it will be useful,\r
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+//     GNU General Public License for more details.\r
+//\r
+//     You should have received a copy of the GNU General Public License\r
+//     along with glBoy.  If not, see <http://www.gnu.org/licenses/>.\r
 \r
-#include "mm80.hh"\r
+#ifndef GLBOY_ROM_HH\r
+#define GLBOY_ROM_HH\r
+\r
+#include "glBoy.hh"\r
 #include "MemoryMap.hh"\r
 \r
 #include <vector>\r
-namespace mm80\r
+namespace glBoy\r
 {\r
 \r
 class ROM : public MemoryMap::Memory\r
 {\r
 public:\r
-       RAM(uint16_t size);\r
-       virtual uint8_t read8(uint16_t address) = 0;\r
-       virtual void write8(uint16_t address, uint8_t value) = 0;\r
+       ROM(const uint8_t* data, uint16_t size);\r
+       virtual uint8_t read8(uint16_t address);\r
+       virtual void write8(uint16_t address, uint8_t value);\r
+       virtual void copy(uint8_t* dest, uint16_t address, int bytes);\r
 \r
 private:\r
        std::vector<uint8_t> m_mem;\r
 };\r
 \r
-} // namespace mm80\r
+} // namespace glBoy\r
 \r
 #endif\r
 \r
old mode 100755 (executable)
new mode 100644 (file)
index 775d2af..1f83436
@@ -1,50 +1,71 @@
-/** \r
- * Authors: Michael McMaster <email@michaelmcmaster.name>\r
- * Copyright: Michael McMaster <email@michaelmcmaster.name>\r
- */ \r
-#include "bits/Z80.hh"\r
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>\r
+//\r
+//     This file is part of glBoy.\r
+//\r
+//     glBoy is free software: you can redistribute it and/or modify\r
+//     it under the terms of the GNU General Public License as published by\r
+//     the Free Software Foundation, either version 3 of the License, or\r
+//     (at your option) any later version.\r
+//\r
+//     glBoy is distributed in the hope that it will be useful,\r
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+//     GNU General Public License for more details.\r
+//\r
+//     You should have received a copy of the GNU General Public License\r
+//     along with glBoy.  If not, see <http://www.gnu.org/licenses/>.\r
+\r
+#include "glBoy.hh"\r
 #include "Registers.hh"\r
 \r
 #include "Core.hh"\r
 \r
-using namespace Z80;\r
-\r
-Flags::Flags() :\r
-       C(0),\r
-       N(0),\r
-       P(0),\r
-       U3(0),\r
-       H(0),\r
-       U5(0),\r
-       Z(0),\r
-       S(0)\r
-{\r
-       // do nothing\r
-}\r
+#include <string.h>\r
+\r
+using namespace glBoy;\r
 \r
-op8_t Flags::operator()(op8_t operand, const Core& context)\r
+void\r
+Flags::set(op8_t operand, const Core& context)\r
 {\r
-       S = operand < 0 ? 1 : 0;\r
+       S = static_cast<int8_t>(operand) < 0 ? 1 : 0;\r
        Z = operand == 0 ? 1 : 0;\r
-       U5 = operand & 0b00010000 >> 4;\r
+       U5 = (operand & 0b00010000) >> 4;\r
        H = 0;\r
-       U3 = operand & 0b00000100 >> 2;\r
+       U3 = (operand & 0b00000100) >> 2;\r
        P = context.getIFF2();\r
        N = 0;\r
        // C is not affected\r
-       return operand;\r
 }\r
 \r
-Registers::Registers() :\r
-       AF(0),\r
-       BC(0),\r
-       DE(0),\r
-       HL(0),\r
-       I(0),\r
-       R(0),\r
-       IX(0),\r
-       IY(0),\r
-       SP(0),\r
-       PC(0)\r
+void\r
+Flags::setCounter(DWORD BC)\r
 {\r
+       // S is not affected\r
+       // Z is not affected\r
+       U5 = (BC.host() & 0b00010000) >> 4;\r
+       H = 0;\r
+       U3 = (BC.host() & 0b00000100) >> 2;\r
+       P = BC.host() ? 1 : 0;\r
+       N = 0;\r
+       // C is not affected\r
 }\r
+\r
+void\r
+Flags::setSub(op8_t A, op8_t B, DWORD BC)\r
+{\r
+       S = (A > B) ? 1 : 0;\r
+       Z = (A == B) ? 1 : 0;\r
+       U5 = (A & 0b00010000) >> 4;\r
+       H = (B & 0xF) > (A & 0xF);\r
+       U3 = (A & 0b00000100) >> 2;\r
+       P = BC.host() ? 1 : 0;\r
+       N = 1;\r
+       // C is not affected\r
+}\r
+\r
+void\r
+Registers::reset()\r
+{\r
+       memset(this, 0, sizeof(Registers));\r
+}\r
+\r
old mode 100755 (executable)
new mode 100644 (file)
index 6decfff..4f460cb
@@ -1,21 +1,27 @@
-/** \r
- * Emulated representation of the Z80 CPU registers.\r
- *\r
- * The Z80 register emulation allows efficient access to both the 8-bit ,\r
- * registers an the 16-bit register pairs.  This is achieved by modifying the \r
- * structure layout according to the endianess of the host.\r
- *\r
- * The Z80 cpu supports switching between multiple register sets.  Each Register\r
- * object only stores the details of a single register set. \r
- *\r
- * Authors: Michael McMaster <email@michaelmcmaster.name>\r
- * Copyright: Michael McMaster <email@michaelmcmaster.name>\r
- */ \r
-#pragma once\r
-\r
-#include "bits/Z80.hh"\r
-\r
-namespace Z80\r
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>\r
+//\r
+//     This file is part of glBoy.\r
+//\r
+//     glBoy is free software: you can redistribute it and/or modify\r
+//     it under the terms of the GNU General Public License as published by\r
+//     the Free Software Foundation, either version 3 of the License, or\r
+//     (at your option) any later version.\r
+//\r
+//     glBoy is distributed in the hope that it will be useful,\r
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+//     GNU General Public License for more details.\r
+//\r
+//     You should have received a copy of the GNU General Public License\r
+//     along with glBoy.  If not, see <http://www.gnu.org/licenses/>.\r
+\r
+#ifndef GLBOY_Registers_hh\r
+#define GLBOY_Registers_hh\r
+\r
+#include "glBoy.hh"\r
+#include "DWORD.hh"\r
+\r
+namespace glBoy\r
 {\r
 \r
 class Core;\r
@@ -31,21 +37,35 @@ class Core;
  *   Z    Zero Flag\r
  *   S    Sign Flag. MSB of Flag register\r
  */\r
-struct Flags\r
+struct __attribute__ ((__packed__)) Flags\r
 {\r
-       unsigned C:1; /// Carry.  LSB of Flag register\r
-       unsigned N:1; /// Add/Subtract\r
-       unsigned P:1; /// (V) Parity/Overflow\r
-       unsigned U3:1; /// 3rd bit of last 8bit op that altered flags\r
-       unsigned H:1; /// Half-Carry (BCD)\r
-       unsigned U5:1; /// 5th bit of last 8bit op that altered flags\r
-       unsigned Z:1; /// Zero Flag\r
-       unsigned S:1; /// Sign Flag. MSB of Flag register\r
+       union\r
+       {\r
+               struct __attribute__ ((__packed__))\r
+               {\r
+                       unsigned C:1; /// Carry.  LSB of Flag register\r
+                       unsigned N:1; /// Add/Subtract\r
+                       unsigned P:1; /// (V) Parity/Overflow\r
+                       unsigned U3:1; /// 3rd bit of last 8bit op that altered flags\r
+                       unsigned H:1; /// Half-Carry (BCD)\r
+                       unsigned U5:1; /// 5th bit of last 8bit op that altered flags\r
+                       unsigned Z:1; /// Zero Flag\r
+                       unsigned S:1; /// Sign Flag. MSB of Flag register\r
+               };\r
+               reg8_t byte;\r
+       };\r
 \r
-       Flags();\r
+       // No ctor allowed for POD.\r
+       void reset() { byte = 0; }\r
 \r
        // Update the flags based on an operands value (eg. for simple assignment)\r
-       op8_t operator()(op8_t operand, const Core& context);\r
+       void set(op8_t operand, const Core& context);\r
+\r
+       // Update the flags when changing BC\r
+       void setCounter(DWORD BC);\r
+\r
+       // Set from A - B\r
+       void setSub(op8_t A, op8_t B, DWORD BC);\r
 };\r
 \r
 /** Z80 register access.\r
@@ -56,54 +76,43 @@ struct Flags
  */\r
 struct Registers\r
 {\r
-       Registers();\r
+       // No ctor allowed for POD.\r
+       void reset();\r
 \r
-       // Storage Order allows for 16 bit pairs to be accessed naturally.\r
-       // ie. AF, BC, DE, HL\r
-       // eg. AF = 0x1234, A will == 0x12.\r
-#ifdef HOST_LITTLE_ENDIAN\r
 #define Z80_REG_STRUCT(h,l) \\r
        struct \\r
        { \\r
                l; \\r
                h; \\r
        };\r
-#else\r
-#define Z80_REG_STRUCT(h,l) \\r
-       struct \\r
-       { \\r
-               h; \\r
-               l; \\r
-       };\r
-#endif\r
 \r
        // Note: The order of these unions/structs is important, as\r
        // offsets are taken into the Register struct\r
-       reg8_t[0] begin8;\r
-       reg16_t[0] begin16;\r
+       reg8_t begin8[0];\r
+       DWORD begin16[0];\r
 \r
        union\r
        {\r
                Z80_REG_STRUCT(reg8_t B, reg8_t C);\r
-               reg16_t BC;\r
+               DWORD BC;\r
        };\r
 \r
        union\r
        {\r
                Z80_REG_STRUCT(reg8_t D, reg8_t E);\r
-               reg16_t DE;\r
+               DWORD DE;\r
        };\r
 \r
        union\r
        {\r
                Z80_REG_STRUCT(reg8_t H, reg8_t L);\r
-               reg16_t HL;\r
+               DWORD HL;\r
        };\r
 \r
        union\r
        {\r
-               Z80_REG_STRUCT(reg8_t A, Flags F,);\r
-               reg16_t AF;\r
+               Z80_REG_STRUCT(reg8_t A, Flags F);\r
+               DWORD AF;\r
        };\r
 \r
 \r
@@ -112,9 +121,13 @@ struct Registers
        // Special Purpose\r
        reg8_t I;      // Interrupt Vector\r
        reg8_t R;      // Memory Refresh\r
-       reg16_t IX;     // Index Register\r
-       reg16_t IY;     // Index Register\r
-       reg16_t SP;     // Stack Pointer\r
-       reg16_t PC;     // Program Counter\r
+       DWORD IX;     // Index Register\r
+       DWORD IY;     // Index Register\r
+       DWORD SP;     // Stack Pointer\r
+       DWORD PC;     // Program Counter\r
 };\r
 \r
+} // namespace glBoy\r
+\r
+#endif\r
+\r
diff --git a/Resample.cc b/Resample.cc
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/Test.cc b/Test.cc
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..458b8a2b8dad02910a5761f300a80d562b2c1e3b 100644 (file)
--- a/Test.cc
+++ b/Test.cc
@@ -0,0 +1,275 @@
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of glBoy.
+//
+//     glBoy 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 3 of the License, or
+//     (at your option) any later version.
+//
+//     glBoy 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 glBoy.  If not, see <http://www.gnu.org/licenses/>.
+
+#include "ALU.hh"
+#include "DAA.hh"
+
+#include "stddef.h"
+
+#include <cassert>
+#include <iostream>
+#include <ios>
+
+using namespace glBoy;
+
+void add(uint8_t a, uint8_t b)
+{
+       Flags f;
+       f.reset();
+       uint8_t result(ALU::add(a, b, f));
+
+       uint16_t testFlags;
+       uint8_t testResult;
+       __asm__ (
+               "movb %2, %%al\n\t"
+               "addb %3, %%al\n\t"
+               "pushf\n\t"
+               "mov %%al, %1\n\t"
+               "pop %%ax\n\t"
+               "movw %%ax, %0\n\t"
+               : "=g" (testFlags), "=g" (testResult)
+               : "g" (a), "g" (b)
+               : "eax", "cc"
+               );
+
+       assert(result == testResult);
+       assert(f.S == (testFlags & 0x80) >> 7);
+       assert(f.Z == (testFlags & 0x40) >> 6);
+       assert(f.H == (testFlags & 0x10) >> 4);
+       assert(f.P == (testFlags & 0x800) >> 11); // 0x4 for parity
+       assert(f.C == (testFlags & 0x1));
+}
+
+void sub(uint8_t a, uint8_t b)
+{
+       Flags f;
+       f.reset();
+       uint8_t result(ALU::sub(a, b, f));
+
+       uint16_t testFlags;
+       uint8_t testResult;
+       __asm__ (
+               "movb %2, %%al\n\t"
+               "subb %3, %%al\n\t"
+               "pushf\n\t"
+               "mov %%al, %1\n\t"
+               "pop %%ax\n\t"
+               "movw %%ax, %0\n\t"
+               : "=g" (testFlags), "=g" (testResult)
+               : "g" (a), "g" (b)
+               : "eax", "cc"
+               );
+
+       assert(result == testResult);
+       assert(f.S == (testFlags & 0x80) >> 7);
+       assert(f.Z == (testFlags & 0x40) >> 6);
+       assert(f.H == (testFlags & 0x10) >> 4);
+       assert(f.P == (testFlags & 0x800) >> 11); // 0x4 for parity
+       assert(f.C == (testFlags & 0x1));
+}
+
+void bitwiseAnd(uint8_t a, uint8_t b)
+{
+       Flags f;
+       f.reset();
+       uint8_t result(ALU::bitwiseAnd(a, b, f));
+
+       uint16_t testFlags;
+       uint8_t testResult;
+       __asm__ (
+               "movb %2, %%al\n\t"
+               "andb %3, %%al\n\t"
+               "pushf\n\t"
+               "mov %%al, %1\n\t"
+               "pop %%ax\n\t"
+               "movw %%ax, %0\n\t"
+               : "=g" (testFlags), "=g" (testResult)
+               : "g" (a), "g" (b)
+               : "eax", "cc"
+               );
+
+       assert(result == testResult);
+       assert(f.S == (testFlags & 0x80) >> 7);
+       assert(f.Z == (testFlags & 0x40) >> 6);
+       //assert(f.H == (testFlags & 0x10) >> 4);
+       // H is always set for AND for Z80, but not X86
+       assert(f.H);
+       assert(f.P == (testFlags & 0x04) >> 2); // 0x800 for overflow
+       assert(f.C == (testFlags & 0x1));
+}
+
+void bitwiseOr(uint8_t a, uint8_t b)
+{
+       Flags f;
+       f.reset();
+       uint8_t result(ALU::bitwiseOr(a, b, f));
+
+       uint16_t testFlags;
+       uint8_t testResult;
+       __asm__ __volatile__ (
+               "movb %2, %%al\n\t"
+               "orb %3, %%al\n\t"
+               "pushf\n\t"
+               "mov %%al, %1\n\t"
+               "pop %%ax\n\t"
+               "movw %%ax, %0\n\t"
+               : "=g" (testFlags), "=g" (testResult)
+               : "g" (a), "g" (b)
+               : "eax", "cc"
+               );
+
+       assert(result == testResult);
+       assert(f.S == (testFlags & 0x80) >> 7);
+       assert(f.Z == (testFlags & 0x40) >> 6);
+       assert(f.H == (testFlags & 0x10) >> 4);
+       assert(f.P == (testFlags & 0x04) >> 2); // 0x800 for overflow
+       assert(f.C == (testFlags & 0x1));
+}
+
+void bitwiseXor(uint8_t a, uint8_t b)
+{
+       Flags f;
+       f.reset();
+       uint8_t result(ALU::bitwiseXor(a, b, f));
+
+       uint16_t testFlags;
+       uint8_t testResult;
+       __asm__ __volatile__(
+               "movb %2, %%al\n\t"
+               "xorb %3, %%al\n\t"
+               "pushf\n\t"
+               "mov %%al, %1\n\t"
+               "pop %%ax\n\t"
+               "movw %%ax, %0\n\t"
+               : "=g" (testFlags), "=g" (testResult)
+               : "g" (a), "g" (b)
+               : "eax", "cc"
+               );
+
+       assert(result == testResult);
+       assert(f.S == (testFlags & 0x80) >> 7);
+       assert(f.Z == (testFlags & 0x40) >> 6);
+       assert(f.H == (testFlags & 0x10) >> 4);
+       assert(f.P == (testFlags & 0x04) >> 2); // 0x800 for overflow
+       assert(f.C == (testFlags & 0x1));
+}
+
+uint8_t daa(uint8_t a, uint8_t b)
+{
+       Flags f;
+       f.reset();
+       uint8_t tmp(ALU::add(a, b, f));
+
+       return DAA(tmp, f);
+
+}
+
+uint8_t daaSub(uint8_t a, uint8_t b)
+{
+       Flags f;
+       f.reset();
+       uint8_t tmp(ALU::sub(a, b, f));
+
+       return DAA(tmp, f);
+
+}
+
+int main()
+{
+       // Ensure packed representations are the expected size,
+       // and usable within union types.
+       assert(sizeof(DWORD) == 2);
+       assert(sizeof(Flags) == 1);
+
+       // Ensure 16bit registers are aligned to 16-bit boundaries.
+       assert(offsetof(Registers, BC) == 0);
+       assert(offsetof(Registers, AF) == 6);
+
+       add(0, 0);
+       add(1, 1);
+       add(-1, 1);
+       add(-128, 127);
+       add(127, 1);
+       add(-128, -1);
+       add(5, 5);
+       add(15, 1);
+       add(15, 128);
+
+       Flags f;
+       f.reset();
+       assert(ALU::add(htoz(0x1000), htoz(0x5555), f).host() == 0x6555);
+       assert(!f.C);
+       assert(ALU::add(htoz(0x1000), htoz(0xF555), f).host() == 0x0555);
+       assert(f.C);
+
+
+       sub(0, 0);
+       sub(1, 1);
+       sub(-1, 1);
+       sub(-128, 127);
+       sub(127, 1);
+       sub(-128, -1);
+       sub(5, 5);
+       sub(15, 1);
+       sub(15, 128);
+       sub(0, 0xb);
+
+       bitwiseAnd(0, 0);
+       bitwiseAnd(1, 1);
+       bitwiseAnd(-1, 1);
+       bitwiseAnd(-128, 127);
+       bitwiseAnd(127, 1);
+       bitwiseAnd(-128, -1);
+       bitwiseAnd(5, 5);
+       bitwiseAnd(15, 1);
+       bitwiseAnd(15, 128);
+
+       bitwiseOr(0, 0);
+       bitwiseOr(1, 1);
+       bitwiseOr(-1, 1);
+       bitwiseOr(-128, 127);
+       bitwiseOr(127, 1);
+       bitwiseOr(-128, -1);
+       bitwiseOr(5, 5);
+       bitwiseOr(15, 1);
+       bitwiseOr(15, 128);
+
+       bitwiseXor(0, 0);
+       bitwiseXor(1, 1);
+       bitwiseXor(-1, 1);
+       bitwiseXor(-128, 127);
+       bitwiseXor(127, 1);
+       bitwiseXor(-128, -1);
+       bitwiseXor(5, 5);
+       bitwiseXor(15, 1);
+       bitwiseXor(15, 128);
+
+       assert(daa(0x01, 0x01) == 0x02);
+       assert(daa(0x01, 0x09) == 0x10);
+       assert(daa(0x01, 0x10) == 0x11);
+       assert(daa(0x09, 0x09) == 0x18);
+       assert(daa(0x50, 0x49) == 0x99);
+       assert(daa(0x99, 0x0) == 0x99);
+       assert(daa(0x27, 0x27) == 0x54);
+       assert(daa(0x90, 0x10) == 0x0); // Carry from 0x100
+       assert(daa(0x99, 0x99) == 0x98); // Carry from 0x198
+
+       assert(daaSub(0x10, 0x01) == 0x09);
+       assert(daaSub(0x10, 0x20) == 0x90);
+
+       std::cout << "Tests complete" << std::endl;
+}
index 07331954eae700759f5d0543d0909c240f3921a1..0fb885c95c826ee81a169306516d63fc9f18e4f0 100644 (file)
--- a/glBoy.hh
+++ b/glBoy.hh
@@ -1,20 +1,28 @@
-/** \r
- * Z80 CPU emulator\r
- *\r
- * Authors: Michael McMaster <email@michaelmcmaster.name>\r
- * Copyright: Michael McMaster <email@michaelmcmaster.name>\r
- *\r
- * Description goes here\r
- */ \r
-#pragma once\r
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>\r
+//\r
+//     This file is part of glBoy.\r
+//\r
+//     glBoy is free software: you can redistribute it and/or modify\r
+//     it under the terms of the GNU General Public License as published by\r
+//     the Free Software Foundation, either version 3 of the License, or\r
+//     (at your option) any later version.\r
+//\r
+//     glBoy is distributed in the hope that it will be useful,\r
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+//     GNU General Public License for more details.\r
+//\r
+//     You should have received a copy of the GNU General Public License\r
+//     along with glBoy.  If not, see <http://www.gnu.org/licenses/>.\r
+\r
+#ifndef GLBOY_HH\r
+#define GLBOY_HH\r
 \r
 #include <cstdint>\r
+#include <iostream>\r
 \r
-namespace Z80\r
+namespace glBoy\r
 {\r
-       typedef uint8_t u8_t;\r
-       typedef uint16_t u16_t;\r
-\r
        typedef uint8_t op8_t;\r
        typedef uint8_t op16_t;\r
 \r
@@ -22,14 +30,17 @@ namespace Z80
        typedef uint16_t reg16_t;\r
 \r
        typedef uint16_t address_t;\r
-       typedef int16_t offset_t;\r
 \r
        typedef bool bit;\r
 \r
-       typedef uint64_t cycle_t;\r
+       typedef int64_t cycle_t;\r
 \r
        #if !(defined(HOST_LITTLE_ENDIAN) || defined(HOST_BIG_ENDIAN))\r
-               #warn Assuming little-endian\r
+               #warning Assuming little-endian\r
                #define HOST_LITTLE_ENDIAN\r
        #endif\r
+\r
 }\r
+\r
+#endif\r
+\r
diff --git a/hq4x.cc b/hq4x.cc
index 6c145d2b5626995df699b7bd7ba8a31432e7cd6e..d5521add6740684f4273bf40ab334a7a6aa59160 100644 (file)
--- a/hq4x.cc
+++ b/hq4x.cc
-//hq4x filter demo program\r
+// hq4x OpenGL Shader\r
 //----------------------------------------------------------\r
-//Copyright (C) 2003 MaxSt ( maxst@hiend3d.com )\r
-\r
+// Copyright (C) 2003 MaxSt ( maxst@hiend3d.com )\r
+// Copyright 2011 Michael McMaster <michael@codesrc.com>\r
+//\r
 //This program is free software; you can redistribute it and/or\r
-//modify it under the terms of the GNU Lesser General Public\r
+//modify it under the terms of the GNU General Public\r
 //License as published by the Free Software Foundation; either\r
-//version 2.1 of the License, or (at your option) any later version.\r
+//version 3 of the License, or (at your option) any later version.\r
 //\r
 //This program is distributed in the hope that it will be useful,\r
 //but WITHOUT ANY WARRANTY; without even the implied warranty of\r
 //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
 //Lesser General Public License for more details.\r
 //\r
-//You should have received a copy of the GNU Lesser General Public\r
+//You should have received a copy of the GNU General Public\r
 //License along with this program; if not, write to the Free Software\r
 //Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
+//----------------------------------------------------------\r
+//\r
+// Changes from original code:\r
+//  - Converted to GLSL, inspired by the XNA port of hqNx by\r
+// Phill Djonov <phill@vec3.net> (http://github.com/pdjonov/hqnx/wiki)\r
+// - Applied clause 3 of LGPL 2.1 to alter license to GNU GPL 3.  Updated\r
+// License notice at top of file as required by clause 3.\r
 \r
+#define GL_GLEXT_PROTOTYPES\r
 \r
-// Converted to grayscale 2010 by Michael McMaster (Michael.McMaster@gmail.com)\r
-// Then converted to generate a lookup table, inspired by\r
-// Phill Djonov <phill@vec3.net> XNA implementation (http://github.com/pdjonov/hqnx/wiki)\r
-// -But, each 3x3 weight matrix is packed into a single 32bit RGBA value,\r
-// instead of 2 RGBA's.\r
+#include "hq4x.hh"\r
 \r
-/*\r
-Each weight = number between 0 and 8 inclusive. 4 bits.\r
-Treated as weight/8f\r
+#include <GL/glu.h>\r
+#include <GL/glext.h>\r
 \r
-So, for each input pixel, build a 3x3 matrix of surrounding\r
-pixels.\r
-       1 2 3 12bits\r
-       4 5 6 12bits\r
-       7 8 9 12bits\r
-               ------\r
-               36 bits.\r
-hmm. Skip the 5th weight. The matrix has to Sum to 8, so we can\r
-make up 1 missing value.\r
-       1 2 3 12bits\r
-       4 _ 6 8bits\r
-       7 8 9 12bits\r
-               ------\r
-               32 bits TICK\r
+#include <cassert>\r
+#include <cstdint>\r
+#include <cmath>\r
+#include <cstdlib>\r
+#include <iomanip>\r
+#include <iostream>\r
 \r
-We then produce a 4x4 matrix of output pixels.\r
+using namespace glBoy;\r
 \r
-Need 256 sets of 16  x 3x3 matrices or,\r
+namespace\r
+{\r
 \r
-1) 3x3 matrix fix in 32bits. So just need a 256 sets of 4x4 matrix.\r
-4096 pixels.\r
+const char*\r
+s_VectorSource =\r
+"\\r
+//#version 130\n\\r
+\\r
+uniform vec4 mmTextureSize;\n\\r
+\\r
+varying vec4 c5;\n\\r
+\\r
+void main()\n\\r
+{\n\\r
+       c5 = gl_MultiTexCoord0;\n\\r
+\\r
+       gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n\\r
+}\n\\r
+";\r
 \r
-2) There are 4 choices (2^4 outcomes = 16)\r
-Diff (w[2], w[6])\r
-Diff (w[6], w[8])\r
-Diff (w[8], w[4])\r
-Diff (w[4], w[2])\r
+const char*\r
+s_FragmentSource =\r
+"\\r
+// Enable bitwise operators\n\\r
+#version 130\n\\r
+\\r
+uniform sampler2D mmTexture;\n\\r
+uniform sampler2D mmLookup;\n\\r
+uniform vec4 mmTextureSize;\n\\r
+\\r
+in vec4 c5;\n\\r
+\\r
+out vec4 fragColour;\n\\r
+\\r
+vec4 RGB2YUV(in vec4 val)\n\\r
+{\n\\r
+       return mat4(\\r
+               0.299, 0.587, 0.114, 0,\n\\r
+               -0.14713, -0.28886, 0.436, 0,\n\\r
+               0.615, -0.51499, -0.10001, 0,\n\\r
+               0, 0, 0, 0\n\\r
+               ) * val;\\r
+}\n\\r
+\\r
+bool Diff(in vec4 YUV1, in vec4 YUV2)\n\\r
+{\n\\r
+       return any(\n\\r
+               greaterThan(\n\\r
+                       abs(YUV1 - YUV2),\n\\r
+                       vec4(48.0/255.0, 7.0/255.0, 6.0/255.0, 0)\n\\r
+                       ).xyz\n\\r
+               );\n\\r
+}\n\\r
+\\r
+void main()\n\\r
+{\n\\r
+       vec4 c_offsetX = vec4(1.0/(mmTextureSize.x-1), 0, 0, 0);\n\\r
+       vec4 c_offsetY = vec4(0, 1.0/(mmTextureSize.y-1), 0, 0);\n\\r
+\\r
+       vec4 w[9];\n\\r
+       w[0] = texture2DProj(mmTexture, c5 - c_offsetY - c_offsetX);\n\\r
+       w[1] = texture2DProj(mmTexture, c5 - c_offsetY);\n\\r
+       w[2] = texture2DProj(mmTexture, c5 - c_offsetY + c_offsetX);\n\\r
+\\r
+       w[3] = texture2DProj(mmTexture, c5 - c_offsetX);\n\\r
+       w[4] = texture2DProj(mmTexture, c5);\n\\r
+       w[5] = texture2DProj(mmTexture, c5 + c_offsetX);\n\\r
+\\r
+       w[6] = texture2DProj(mmTexture, c5 + c_offsetY - c_offsetX);\n\\r
+       w[7] = texture2DProj(mmTexture, c5 + c_offsetY);\n\\r
+       w[8] = texture2DProj(mmTexture, c5 + c_offsetY + c_offsetX);\n\\r
+\\r
+       vec4 YUV[9];\n\\r
+       YUV[0] = RGB2YUV(w[0]);\n\\r
+       YUV[1] = RGB2YUV(w[1]);\n\\r
+       YUV[2] = RGB2YUV(w[2]);\n\\r
+       YUV[3] = RGB2YUV(w[3]);\n\\r
+       YUV[4] = RGB2YUV(w[4]);\n\\r
+       YUV[5] = RGB2YUV(w[5]);\n\\r
+       YUV[6] = RGB2YUV(w[6]);\n\\r
+       YUV[7] = RGB2YUV(w[7]);\n\\r
+       YUV[8] = RGB2YUV(w[8]);\n\\r
+\\r
+       int pattern = 0;\n\\r
+       int flag = 1;\n\\r
+       int k;\n\\r
+       for (k = 0; k < 9; k++)\n\\r
+       {\n\\r
+               if (k == 4) continue;\n\\r
+\\r
+               if (Diff(YUV[4], YUV[k]))\n\\r
+               {\n\\r
+                       pattern = pattern + flag;\n\\r
+               }\n\\r
+               flag = flag * 2;\n\\r
+       }\n\\r
+\\r
+       // k is now diff.\n\\r
+       k = 0;\n\\r
+       if (Diff(YUV[1], YUV[5]))\n\\r
+       {\n\\r
+               k = k + 1;\n\\r
+       }\n\\r
+       if (Diff(YUV[5], YUV[7]))\n\\r
+       {\n\\r
+               k = k + 2;\n\\r
+       }\n\\r
+       if (Diff(YUV[7], YUV[3]))\n\\r
+       {\n\\r
+               k = k + 4;\n\\r
+       }\n\\r
+       if (Diff(YUV[3], YUV[1]))\n\\r
+       {\n\\r
+               k = k + 8;\n\\r
+       }\n\\r
+\\r
+       vec2 lookupMax = vec2(1.0/47.0, 1.0/4095);\n\\r
+       ivec2 thisPixel = ivec2(fract(c5 * mmTextureSize) * 4.0);\n\\r
+       ivec2 lookupIndex = ivec2((thisPixel.y*4+thisPixel.x)*3, (pattern * 16) + k);\n\\r
+       vec4 weight1 = texture2D(mmLookup, lookupIndex * lookupMax);\n\\r
+       vec4 weight2 = texture2D(mmLookup, (lookupIndex + ivec2(1, 0)) * lookupMax);\n\\r
+       vec4 weight3 = texture2D(mmLookup, (lookupIndex + vec2(2, 0)) * lookupMax);\n\\r
+\\r
+       // Dodgy fix for inaccurate lookups :-( Seemed to work on Quadro NVS 290 without this\n\\r
+       float allW = dot(\n\\r
+               vec4(1,1,1,0),\n\\r
+               vec4(\n\\r
+                       dot(weight1, vec4(1,1,1,0)),\n\\r
+                       dot(weight2, vec4(1,1,1,0)),\n\\r
+                       dot(weight3, vec4(1,1,1,0)),\n\\r
+                       0\n\\r
+                       )\n\\r
+               );\n\\r
+       fragColour =\n\\r
+               w[0]*weight1.x + w[1]*weight1.y + w[2]*weight1.z +\n\\r
+               w[3]*weight2.x + w[4]*weight2.y + w[5]*weight2.z +\n\\r
+               w[6]*weight3.x + w[7]*weight3.y + w[8]*weight3.z;\n\\r
+       fragColour = fragColour / allW;\n\\r
+\\r
+//if (allW > 1.100) { fragColour =  vec4(0.0,0.0,1.0,0); } \n\\r
+//if (allW < 0.9) { fragColour =  vec4(0.0,1.0,0.0,0); } \n\\r
+\\r
+}\n\\r
+";\r
 \r
-Layout of texture:\r
-Y = pattern # (0 -> 255)\r
-X =\r
-       Top 4 bits = diff bitmask\r
-       Lower 4 bits = output pixel #\r
-*/\r
+void abortGLError()\r
+{\r
+       std::cerr << "Fatal OpenGL error in hq4x shader: " <<\r
+               gluErrorString(glGetError()) << std::endl;\r
+       abort();\r
+}\r
 \r
-#include "hq4x.hh"\r
+void abortShaderError(GLint shader)\r
+{\r
+       std::cerr << "Fatal error using hq4x shader: " <<\r
+               gluErrorString(glGetError()) << std::endl;\r
+\r
+       char buffer[2048];\r
+       int len;\r
+       glGetShaderInfoLog(shader, 2048, &len, &buffer[0]); \r
+       std::cerr << buffer << std::endl;\r
+\r
+       abort();\r
+}\r
+\r
+void abortProgramError(GLint program)\r
+{\r
+       std::cerr << "Fatal error using hq4x program: " <<\r
+               gluErrorString(glGetError()) << std::endl;\r
+\r
+       char buffer[2048];\r
+       int len;\r
+       glGetProgramInfoLog(program, 2048, &len, &buffer[0]); \r
+       std::cerr << buffer << std::endl;\r
+\r
+       abort();\r
+}\r
+\r
+} // namespace\r
 \r
-#include <cstdint>\r
-#include <cmath>\r
-#include <cstdlib>\r
-#include <iomanip>\r
-#include <iostream>\r
 \r
-enum Diff { Diff_2_6 = 1, Diff_6_8 = 2, Diff_8_4 = 4, Diff_4_2 = 8 };\r
+Hq4x::Hq4x(size_t textureWidth, size_t textureHeight) :\r
+       m_textureWidth(textureWidth),\r
+       m_textureHeight(textureHeight)\r
+{\r
+       m_vertexShader = glCreateShader(GL_VERTEX_SHADER);\r
+       m_fragShader = glCreateShader(GL_FRAGMENT_SHADER);\r
+\r
+       if (!m_vertexShader || !m_fragShader) abortGLError();\r
+\r
+       glShaderSource(m_vertexShader, 1, &s_VectorSource, NULL);\r
+       glShaderSource(m_fragShader, 1, &s_FragmentSource, NULL);\r
+\r
+       glCompileShader(m_vertexShader);\r
+       if (glGetError() != GL_NO_ERROR) abortShaderError(m_vertexShader);\r
+       glCompileShader(m_fragShader);\r
+       if (glGetError() != GL_NO_ERROR) abortShaderError(m_fragShader);\r
+\r
+       m_program = glCreateProgram();\r
+       if (!m_program) abortGLError();\r
+\r
+       glAttachShader(m_program, m_vertexShader);\r
+       if (glGetError() != GL_NO_ERROR) abortProgramError(m_program);\r
+       glAttachShader(m_program, m_fragShader);\r
+       if (glGetError() != GL_NO_ERROR) abortProgramError(m_program);\r
+       glLinkProgram(m_program);\r
+       if (glGetError() != GL_NO_ERROR) abortProgramError(m_program);\r
+\r
+       glGenTextures(1, &m_lookupTexture);\r
+       glActiveTexture(GL_TEXTURE0);\r
+       glBindTexture(GL_TEXTURE_2D, m_lookupTexture);\r
+\r
+       // Only nearest neighbour will work\r
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);\r
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);\r
+\r
+       // Create the texture image\r
+       glTexImage2D(\r
+               GL_TEXTURE_2D,\r
+               0,\r
+               GL_RGBA,\r
+               48,\r
+               4096,\r
+               0,\r
+               GL_RGBA,\r
+               GL_UNSIGNED_BYTE,\r
+               getHq4xLookupTable()\r
+               );\r
+       glUseProgram(m_program);\r
+       if (glGetError() != GL_NO_ERROR) abortProgramError(m_program);\r
+\r
+       glActiveTexture(GL_TEXTURE1);\r
+       glBindTexture(GL_TEXTURE_2D, m_lookupTexture);\r
+       GLint tex = glGetUniformLocation(m_program, "mmLookup");\r
+       glUniform1i(tex, 1); // Bind Texture 1\r
+}\r
+\r
+void\r
+Hq4x::prepareShader(GLuint texture)\r
+{\r
+       glUseProgram(m_program);\r
+       if (glGetError() != GL_NO_ERROR) abortProgramError(m_program);\r
+\r
+       glBindFragDataLocationEXT(m_program,0,"fragColor");\r
+\r
+       GLint size(glGetUniformLocation(m_program, "mmTextureSize"));\r
+       glUniform4f(size, m_textureWidth, m_textureHeight, 0, 0);\r
+\r
+       glActiveTexture(GL_TEXTURE0);\r
+       glBindTexture(GL_TEXTURE_2D, texture);\r
+       GLint tex(glGetUniformLocation(m_program, "mmTexture"));\r
+       glUniform1i(tex, 0); // Bind Texture 0\r
+\r
+/*\r
+       glActiveTexture(GL_TEXTURE1);\r
+       glBindTexture(GL_TEXTURE_2D, m_lookupTexture);\r
+       tex = glGetUniformLocation(m_program, "mmLookup");\r
+       glUniform1i(tex, 1); // Bind Texture 1\r
+*/\r
+}\r
+\r
+//--------------------------------------------------\r
+// Lookup Table Generation Below\r
+//--------------------------------------------------\r
 \r
 struct weight\r
 {\r
@@ -82,47 +315,47 @@ struct weight
 \r
 inline void Interp1(uint32_t& c1, uint32_t& c2)\r
 {\r
-       c1 = 6;\r
-       c2 = 2;\r
+       c1 = 191;\r
+       c2 = 64;\r
 }\r
 \r
 inline void Interp2(uint32_t& c1, uint32_t& c2, uint32_t& c3)\r
 {\r
-       c1 = 4;\r
-       c2 = 2;\r
-       c3 = 2;\r
+       c1 = 127;\r
+       c2 = 64;\r
+       c3 = 64;\r
 }\r
 \r
 inline void Interp3(uint32_t& c1, uint32_t& c2)\r
 {\r
-       c1 = 7;\r
-       c2 = 1;\r
+       c1 = 223;\r
+       c2 = 32;\r
 }\r
 \r
 inline void Interp5(uint32_t& c1, uint32_t& c2)\r
 {\r
-       c1 = 4;\r
-       c2 = 4;\r
+       c1 = 127;\r
+       c2 = 128;\r
 }\r
 \r
 inline void Interp6(uint32_t& c1, uint32_t& c2, uint32_t& c3)\r
 {\r
-       c1 = 5;\r
-       c2 = 2;\r
-       c3 = 1;\r
+       c1 = 159;\r
+       c2 = 64;\r
+       c3 = 32;\r
 }\r
 \r
 inline void Interp7(uint32_t& c1, uint32_t& c2, uint32_t& c3)\r
 {\r
-       c1 = 6;\r
-       c2 = 1;\r
-       c3 = 1;\r
+       c1 = 191;\r
+       c2 = 32;\r
+       c3 = 32;\r
 }\r
 \r
 inline void Interp8(uint32_t& c1, uint32_t& c2)\r
 {\r
-       c1 = 5;\r
-       c2 = 3;\r
+       c1 = 159;\r
+       c2 = 96;\r
 }\r
 \r
 \r
@@ -144,7 +377,7 @@ inline void Interp8(uint32_t& c1, uint32_t& c2)
 #define p15 weights[3][2]\r
 #define p16 weights[3][3]\r
 \r
-#define PIXEL00_0     p1.w5 = 8;\r
+#define PIXEL00_0     p1.w5 = 255;\r
 #define PIXEL00_11    Interp1(p1.w5, p1.w4);\r
 #define PIXEL00_12    Interp1(p1.w5, p1.w2);;\r
 #define PIXEL00_20    Interp2(p1.w5, p1.w2, p1.w4);\r
@@ -153,7 +386,7 @@ inline void Interp8(uint32_t& c1, uint32_t& c2)
 #define PIXEL00_81    Interp8(p1.w5, p1.w4);\r
 #define PIXEL00_82    Interp8(p1.w5, p1.w2);\r
 \r
-#define PIXEL01_0     p2.w5 = 8;\r
+#define PIXEL01_0     p2.w5 = 255;\r
 #define PIXEL01_10    Interp1(p2.w5, p2.w1);\r
 #define PIXEL01_12    Interp1(p2.w5, p2.w2);\r
 #define PIXEL01_14    Interp1(p2.w2, p2.w5);\r
@@ -165,7 +398,7 @@ inline void Interp8(uint32_t& c1, uint32_t& c2)
 #define PIXEL01_82    Interp8(p2.w5, p2.w2);\r
 #define PIXEL01_83    Interp8(p2.w2, p2.w4);\r
 \r
-#define PIXEL02_0     p3.w5 = 8;\r
+#define PIXEL02_0     p3.w5 = 255;\r
 #define PIXEL02_10    Interp1(p3.w5, p3.w3);\r
 #define PIXEL02_11    Interp1(p3.w5, p3.w2);\r
 #define PIXEL02_13    Interp1(p3.w2, p3.w5);\r
@@ -177,7 +410,7 @@ inline void Interp8(uint32_t& c1, uint32_t& c2)
 #define PIXEL02_81    Interp8(p3.w5, p3.w2);\r
 #define PIXEL02_83    Interp8(p3.w2, p3.w6);\r
 \r
-#define PIXEL03_0     p4.w5 = 8;\r
+#define PIXEL03_0     p4.w5 = 255;\r
 #define PIXEL03_11    Interp1(p4.w5, p4.w2);\r
 #define PIXEL03_12    Interp1(p4.w5, p4.w6);\r
 #define PIXEL03_20    Interp2(p4.w5, p4.w2, p4.w6);\r
@@ -186,7 +419,7 @@ inline void Interp8(uint32_t& c1, uint32_t& c2)
 #define PIXEL03_81    Interp8(p4.w5, p4.w2);\r
 #define PIXEL03_82    Interp8(p4.w5, p4.w6);\r
 \r
-#define PIXEL10_0     p5.w5 = 8;\r
+#define PIXEL10_0     p5.w5 = 255;\r
 #define PIXEL10_10    Interp1(p5.w5, p5.w1);\r
 #define PIXEL10_11    Interp1(p5.w5, p5.w4);\r
 #define PIXEL10_13    Interp1(p5.w4, p5.w5);\r
@@ -198,19 +431,19 @@ inline void Interp8(uint32_t& c1, uint32_t& c2)
 #define PIXEL10_81    Interp8(p5.w5, p5.w4);\r
 #define PIXEL10_83    Interp8(p5.w4, p5.w2);\r
 \r
-#define PIXEL11_0     p6.w5 = 8;\r
+#define PIXEL11_0     p6.w5 = 255;\r
 #define PIXEL11_30    Interp3(p6.w5, p6.w1);\r
 #define PIXEL11_31    Interp3(p6.w5, p6.w4);\r
 #define PIXEL11_32    Interp3(p6.w5, p6.w2);\r
 #define PIXEL11_70    Interp7(p6.w5, p6.w4, p6.w2);\r
 \r
-#define PIXEL12_0     p7.w5 = 8;\r
+#define PIXEL12_0     p7.w5 = 255;\r
 #define PIXEL12_30    Interp3(p7.w5, p7.w3);\r
 #define PIXEL12_31    Interp3(p7.w5, p7.w2);\r
 #define PIXEL12_32    Interp3(p7.w5, p7.w6);\r
 #define PIXEL12_70    Interp7(p7.w5, p7.w6, p7.w2);\r
 \r
-#define PIXEL13_0     p8.w5 = 8;\r
+#define PIXEL13_0     p8.w5 = 255;\r
 #define PIXEL13_10    Interp1(p8.w5, p8.w3);\r
 #define PIXEL13_12    Interp1(p8.w5, p8.w6);\r
 #define PIXEL13_14    Interp1(p8.w6, p8.w5);\r
@@ -222,7 +455,7 @@ inline void Interp8(uint32_t& c1, uint32_t& c2)
 #define PIXEL13_82    Interp8(p8.w5, p8.w6);\r
 #define PIXEL13_83    Interp8(p8.w6, p8.w2);\r
 \r
-#define PIXEL20_0     p9.w5 = 8;\r
+#define PIXEL20_0     p9.w5 = 255;\r
 #define PIXEL20_10    Interp1(p9.w5, p9.w7);\r
 #define PIXEL20_12    Interp1(p9.w5, p9.w4);\r
 #define PIXEL20_14    Interp1(p9.w4, p9.w5);\r
@@ -234,19 +467,19 @@ inline void Interp8(uint32_t& c1, uint32_t& c2)
 #define PIXEL20_82    Interp8(p9.w5, p9.w4);\r
 #define PIXEL20_83    Interp8(p9.w4, p9.w8);\r
 \r
-#define PIXEL21_0     p10.w5 = 8;\r
+#define PIXEL21_0     p10.w5 = 255;\r
 #define PIXEL21_30    Interp3(p10.w5, p10.w7);\r
 #define PIXEL21_31    Interp3(p10.w5, p10.w8);\r
 #define PIXEL21_32    Interp3(p10.w5, p10.w4);\r
 #define PIXEL21_70    Interp7(p10.w5, p10.w4, p10.w8);\r
 \r
-#define PIXEL22_0     p11.w5 = 8;\r
+#define PIXEL22_0     p11.w5 = 255;\r
 #define PIXEL22_30    Interp3(p11.w5, p11.w9);\r
 #define PIXEL22_31    Interp3(p11.w5, p11.w6);\r
 #define PIXEL22_32    Interp3(p11.w5, p11.w8);\r
 #define PIXEL22_70    Interp7(p11.w5, p11.w6, p11.w8);\r
 \r
-#define PIXEL23_0     p12.w5 = 8;\r
+#define PIXEL23_0     p12.w5 = 255;\r
 #define PIXEL23_10    Interp1(p12.w5, p12.w9);\r
 #define PIXEL23_11    Interp1(p12.w5, p12.w6);\r
 #define PIXEL23_13    Interp1(p12.w6, p12.w5);\r
@@ -258,7 +491,7 @@ inline void Interp8(uint32_t& c1, uint32_t& c2)
 #define PIXEL23_81    Interp8(p12.w5, p12.w6);\r
 #define PIXEL23_83    Interp8(p12.w6, p12.w8);\r
 \r
-#define PIXEL30_0     p13.w5 = 8;\r
+#define PIXEL30_0     p13.w5 = 255;\r
 #define PIXEL30_11    Interp1(p13.w5, p13.w8);\r
 #define PIXEL30_12    Interp1(p13.w5, p13.w4);\r
 #define PIXEL30_20    Interp2(p13.w5, p13.w8, p13.w4);\r
@@ -267,7 +500,7 @@ inline void Interp8(uint32_t& c1, uint32_t& c2)
 #define PIXEL30_81    Interp8(p13.w5, p13.w8);\r
 #define PIXEL30_82    Interp8(p13.w5, p13.w4);\r
 \r
-#define PIXEL31_0     p14.w5 = 8;\r
+#define PIXEL31_0     p14.w5 = 255;\r
 #define PIXEL31_10    Interp1(p14.w5, p14.w7);\r
 #define PIXEL31_11    Interp1(p14.w5, p14.w8);\r
 #define PIXEL31_13    Interp1(p14.w8, p14.w5);\r
@@ -279,7 +512,7 @@ inline void Interp8(uint32_t& c1, uint32_t& c2)
 #define PIXEL31_81    Interp8(p14.w5, p14.w8);\r
 #define PIXEL31_83    Interp8(p14.w8, p14.w4);\r
 \r
-#define PIXEL32_0     p15.w5 = 8;\r
+#define PIXEL32_0     p15.w5 = 255;\r
 #define PIXEL32_10    Interp1(p15.w5, p15.w9);\r
 #define PIXEL32_12    Interp1(p15.w5, p15.w8);\r
 #define PIXEL32_14    Interp1(p15.w8, p15.w5);\r
@@ -291,7 +524,7 @@ inline void Interp8(uint32_t& c1, uint32_t& c2)
 #define PIXEL32_82    Interp8(p15.w5, p15.w8);\r
 #define PIXEL32_83    Interp8(p15.w8, p15.w6);\r
 \r
-#define PIXEL33_0     p16.w5 = 8;\r
+#define PIXEL33_0     p16.w5 = 255;\r
 #define PIXEL33_11    Interp1(p16.w5, p16.w6);\r
 #define PIXEL33_12    Interp1(p16.w5, p16.w8);\r
 #define PIXEL33_20    Interp2(p16.w5, p16.w8, p16.w6);\r
@@ -302,7 +535,8 @@ inline void Interp8(uint32_t& c1, uint32_t& c2)
 \r
 \r
 \r
-uint32_t* getHq4xLookupTable()\r
+uint32_t*\r
+Hq4x::getHq4xLookupTable() const\r
 {\r
        static uint32_t* s_table(0);\r
 \r
@@ -312,12 +546,14 @@ uint32_t* getHq4xLookupTable()
        }\r
        else\r
        {\r
+               s_table = new uint32_t[4096*48];\r
+\r
                for (int pattern = 0; pattern < 256; ++pattern)\r
                {\r
-                       weight weights[4][4];\r
-\r
                        for (int diff = 0; diff < 16; ++diff)\r
                        {\r
+                               weight weights[4][4];\r
+\r
                                switch (pattern)\r
                                {\r
                                        case 0:\r
@@ -5299,22 +5535,41 @@ uint32_t* getHq4xLookupTable()
                                        {\r
                                                weight w(weights[pixely][pixelx]);\r
 \r
-                                               uint32_t rgba =\r
-                                                       (w.w1 << 28) |\r
-                                                       (w.w2 << 24) |\r
-                                                       (w.w3 << 20) |\r
-                                                       (w.w4 << 16) |\r
-                                                       (w.w6 << 12) |\r
-                                                       (w.w7 << 8) |\r
-                                                       (w.w8 << 4) |\r
-                                                       w.w9;\r
+                                               assert(w.w1+w.w2+w.w3+w.w4+w.w5+w.w6+w.w7+w.w8+w.w9 == 255);\r
+                                               size_t index(\r
+                                                       ((pattern << 4) | diff) * 48 + (pixely*4+pixelx)*3\r
+                                                       );\r
 \r
+\r
+// TODO byteorder fixes here!\r
+                                               s_table[index] =\r
+                                                       (w.w1 << 0) |\r
+                                                       (w.w2 << 8) |\r
+                                                       (w.w3 << 16);\r
+\r
+\r
+                                               s_table[index + 1] =\r
+                                                       (w.w4 << 0) |\r
+                                                       (w.w5 << 8) |\r
+                                                       (w.w6 << 16);\r
+\r
+                                               s_table[index + 2] =\r
+                                                       (w.w7 << 0) |\r
+                                                       (w.w8 << 8) |\r
+                                                       (w.w9 << 16);\r
+\r
+//hmm, order appears to be ABGR\r
+/*\r
+                                               s_table[index] = 0x0090FF10;\r
+                                               s_table[index+1] = 0x00;\r
+                                               s_table[index+2] = 0x00;\r
+*/\r
                                        }\r
                                }\r
                        }\r
                }\r
        }\r
 \r
-       return s_lookup;\r
+       return s_table;\r
 }\r
 \r
diff --git a/hq4x.hh b/hq4x.hh
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1242141526faf7a72c501eef8b3957bef788a27e 100644 (file)
--- a/hq4x.hh
+++ b/hq4x.hh
@@ -0,0 +1,50 @@
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of glBoy.
+//
+//     glBoy 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 3 of the License, or
+//     (at your option) any later version.
+//
+//     glBoy 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 glBoy.  If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef GLBOY_HQ4X_HH
+#define GLBOY_HQ4X_HH
+
+#include <cstdint>
+
+#include <GL/gl.h>
+
+namespace glBoy
+{
+       class Hq4x
+       {
+       public:
+               Hq4x(size_t textureWidth, size_t textureHeight);
+
+               void prepareShader(GLuint texture);
+
+       private:
+               enum Diff { Diff_2_6 = 1, Diff_6_8 = 2, Diff_8_4 = 4, Diff_4_2 = 8 };
+
+               uint32_t* getHq4xLookupTable() const;
+
+               int m_textureWidth;
+               int m_textureHeight;
+
+               GLint m_vertexShader;
+               GLint m_fragShader;
+               GLint m_program;
+               GLuint m_lookupTexture;
+       };
+}
+
+#endif
+
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..2c4c5be7da4e8180c3905599fbe489872d756fde 100644 (file)
@@ -0,0 +1,393 @@
+#!/usr/bin/perl -w
+#      Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+#
+#      This file is part of glBoy.
+#
+#      glBoy 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 3 of the License, or
+#      (at your option) any later version.
+#
+#      glBoy 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 glBoy.  If not, see <http://www.gnu.org/licenses/>.
+
+use warnings;
+use strict;
+use Carp;
+
+use XML::LibXML::Reader;
+
+# < Ugly Globals >
+my %RegMasks = ();
+my %BitMasks = ();
+my %Instructions = ();
+
+my $VARIANT = "gb80";
+
+# </ Ugly Globals >
+
+my $reader = new XML::LibXML::Reader(location => "InstructionSet.opcode")
+       || die "Cannot read opcode file\n";
+
+open (TABLES, '>InstructionSet_Tables.cc') || die "Could not write to file\n";
+open (CODE, '>InstructionSet_Opcodes.cc') || die "Could not write to file\n";
+
+$reader->read;
+$reader->name eq "opcodes" || die "Invalid xml\n";
+
+my $node = $reader->read;
+while ($node)
+{
+       if ($reader->nodeType == XML_READER_TYPE_ELEMENT)
+       {
+               # Convert subtree to a DOM object.
+               my $subtree = $reader->copyCurrentNode(1);
+
+               if ($reader->name eq "registerMask")
+               {
+                       processRegisterMask($subtree);
+               }
+               elsif ($reader->name eq "bitMask")
+               {
+                       processBitMask($subtree);
+               }
+               elsif ($reader->name eq "instruction")
+               {
+                       processInstruction($subtree)
+               }
+               else
+               {
+                       # Ignore unknown XML
+               }
+       }
+
+       $node = $reader->next();
+}
+
+outputInstructionTable();
+
+close TABLES;
+close CODE;
+
+sub outputInstructionTable
+{
+       foreach my $prefix (keys %Instructions)
+       {
+               my $outPrefix = "OpcodeTable$prefix";
+               $outPrefix =~ s/0x//;
+               $outPrefix =~ s/default//;
+               print TABLES "static void* $outPrefix\[256\] =\n{\n";
+
+               my %opcodes = %{$Instructions{$prefix}};
+
+               my $count = 0;
+               while ($count < 256)
+               {
+                       if ($Instructions{$prefix}{$count})
+                       {
+                               print TABLES "&&$Instructions{$prefix}{$count}";
+                       }
+                       else
+                       {
+                               print TABLES "&&BAD_OPCODE";
+                       }
+
+                       $count = $count + 1;
+
+                       if ($count != 256)
+                       {
+                               print TABLES ",\t";
+                       }
+                       if ($count % 4 == 0)
+                       {
+                               print TABLES "\n";
+                       }
+               }
+               print TABLES "};\n\n";
+       }
+}
+
+
+sub processRegisterMask
+{
+       my $node = shift;
+
+       my $name = $node->findvalue('./@name') || die;
+       my $maskBits = $node->findvalue('./@maskBits') || die;
+       ($maskBits > 0 && $maskBits < 8) || die;
+       my $regBytes = $node->findvalue('./@regBytes') || die;
+       ($regBytes > 0 && $regBytes <= 2) || die;
+
+       my $variant = $node->findvalue('./@variant');
+
+       if ($variant && $variant ne $VARIANT)
+       {
+               return;
+       }
+
+       my $max = 1 << $maskBits;
+       my $count = 0;
+
+       print TABLES "static size_t regMask_$name\[$max\] =\n{\n";
+
+       my @masks = ();
+
+       while ($count < $max)
+       {
+               my $mask = sprintf("%0$maskBits"."b", $count);
+               my $reg = $node->findvalue("./reg[\@mask='$mask']/\@name");
+
+               if ($count != 0) { print TABLES ",\n"; }
+               if ($reg && length $reg != 0)
+               {
+                       print TABLES "\toffsetof(glBoy::Registers, $reg) / $regBytes";
+                       push @masks, $mask;
+               }
+               else
+               {
+                       print TABLES "\tsize_t(-1)";
+               }
+
+               $count = $count + 1;
+       }
+       print TABLES "\n};\n\n";
+
+       $RegMasks{$name}{'masks'} = \@masks;
+       $RegMasks{$name}{'maskBits'} = $maskBits;
+       $RegMasks{$name}{'regBytes'} = $regBytes;
+
+}
+
+sub processBitMask
+{
+       my $node = shift;
+
+       my $name = $node->findvalue('./@name') || die;
+       my $maskBits = $node->findvalue('./@maskBits') || die;
+       ($maskBits > 0 && $maskBits <= 8) || die;
+
+       my $max = 1 << $maskBits;
+       my $count = 0;
+
+       my @masks = ();
+
+       while ($count < $max)
+       {
+               my $mask = sprintf("%0$maskBits"."b", $count);
+               push @masks, $mask;
+               $count = $count + 1;
+       }
+
+       $BitMasks{$name}{'masks'} = \@masks;
+       $BitMasks{$name}{'maskBits'} = $maskBits;
+}
+
+sub expandMask
+{
+       my $mask = shift;
+
+       my @result = ();
+
+       if ($mask =~ /([a-zA-Z])/)
+       {
+               my $match = $1;
+
+               if ($BitMasks{$match})
+               {
+                       foreach my $bits (@{$BitMasks{$match}{'masks'}})
+                       {
+                               my $nextMask = $mask;
+                               $nextMask =~ s/$match/$bits/;
+                               push @result, expandMask($nextMask);
+                       }
+               }
+               elsif ($RegMasks{$match})
+               {
+                       foreach my $bits (@{$RegMasks{$match}{'masks'}})
+                       {
+                               my $nextMask = $mask;
+                               $nextMask =~ s/$match/$bits/;
+                               push @result, expandMask($nextMask);
+                       }
+
+               }
+               $mask =~ s/$match/_/;
+       }
+       else
+       {
+               my $pos = 0;
+               my $value = 0;
+
+               if (length $mask != 8) { croak "Bad Mask $mask\n"; }
+               while ($pos < 8)
+               {
+                       my $char = substr($mask, $pos, 1);
+                       if ($char == 1)
+                       {
+                               $value = $value + (1 << (7 - $pos));
+                       }
+                       $pos = $pos + 1;
+               }
+               push @result, $value;
+       }
+       return @result;
+}
+
+sub createRegReference
+{
+       my $mask = shift;
+
+       my %seenChars = ();
+
+       my @dbgResult = ();
+
+       # This is probably not the fastest way, but who cares.
+       my $pos = 0;
+       my $bitPos = 0;
+       while ($pos < length $mask)
+       {
+               my $char = substr($mask, $pos, 1);
+               if ($char =~ /[a-zA-Z]/ && $RegMasks{$char})
+               {
+                       my $outputName = $char;
+                       if ($seenChars{$outputName})
+                       {
+                               $seenChars{$outputName} = $seenChars{$outputName} + 1;
+                               $outputName .= $seenChars{$outputName};
+                       }
+                       else
+                       {
+                               $seenChars{$outputName} = 1;
+                       }
+
+                       push @dbgResult, $outputName;
+
+                       my $maskBits = $RegMasks{$char}{'maskBits'};
+                       my $shiftNum = 8 - ($bitPos + $RegMasks{$char}{'maskBits'});
+                       if ($RegMasks{$char}{'regBytes'} == 1)
+                       {
+                               print CODE "\tuint8_t& $outputName(m_reg.begin8[regMask_$char\[(opcode >> $shiftNum) & (uint8_t(-1) >> (8 - $maskBits)) \]\]);\n";
+                       }
+                       else
+                       {
+                               print CODE "\tDWORD& $outputName(m_reg.begin16[regMask_$char\[(opcode >> $shiftNum) & (uint8_t(-1) >> (8 - $maskBits)) \]\]);\n";
+                       }
+
+                       $bitPos = $bitPos + $maskBits;
+               }
+               elsif ($char =~ /[a-zA-Z]/ && $BitMasks{$char})
+               {
+                       my $maskBits = $BitMasks{$char}{'maskBits'};
+                       my $shiftNum = 8 - ($bitPos + $BitMasks{$char}{'maskBits'});
+
+                       print CODE "uint8_t $char((opcode >> $shiftNum) & (uint8_t(-1) >> (8 - $maskBits)));\n";
+                       $bitPos = $bitPos + $maskBits;
+               }
+               else
+               {
+                       $bitPos = $bitPos + 1;
+               }
+
+               $pos = $pos + 1;
+       }
+       return @dbgResult;
+}
+
+sub processInstruction
+{
+       my $node = shift;
+
+       my $name = $node->findvalue('./@name') || die;
+       my $clock = $node->findvalue('./@clock');
+       my $mask = $node->findvalue('./opcode/@mask');
+       my $prefix = $node->findvalue('./opcode/@prefix');
+       my $operand = $node->findvalue('./opcode/@operand');
+       my $signedOperand = $node->findvalue('./opcode/@signed_operand');
+       my $variant = $node->findvalue('./@variant');
+
+       if ($variant && $variant ne $VARIANT)
+       {
+               return;
+       }
+       if ($VARIANT eq "gb80" && $prefix && $prefix ne "0xCB")
+       {
+               return;
+       }
+
+       print CODE "// Instruction: $name\n";
+       print CODE "// Clock: $clock\n";
+       my $label = $name;
+       $label =~ tr/ ,'+()/______/;
+       print CODE "$label:\n";
+       print CODE "{\n";
+
+
+       # Note: May have both signed operand (index offset) and operand
+       if ($signedOperand && length $signedOperand == 1)
+       {
+               print CODE "\tint8_t $signedOperand(op8());\n";
+       }
+
+       if ($operand && length $operand == 1)
+       {
+               if (length($prefix) > 4)
+               {
+                       print CODE "\t uint8_t& $operand(doublePrefixOperand);\n";
+               }
+               elsif (uc($operand) eq $operand)
+               {
+                       print CODE "\tDWORD $operand(op16());\n";
+               }
+               else
+               {
+                       print CODE "\tuint8_t $operand(op8());\n";
+               }
+       }
+
+       my @dbgRegs = createRegReference($mask);
+
+       print CODE "#ifdef GLBOY_DEBUG\n";
+       print CODE "std::ostream& trace(log(Log::DBG_TRACE));\n".
+               "trace << ".
+               "opcodePC << \": \";\n".
+               "if (prefix) trace << hex(prefix) << \" \";\n".
+               "trace << hex(opcode) << \" $name\" << ".
+               ($operand ? "\" ($operand=\" << $operand << \")\" << " : "") .
+               ($dbgRegs[0] ? "\" ($dbgRegs[0]=\" << $dbgRegs[0] << \")\" << " : "") .
+               "std::endl;\n";
+       print CODE "#endif\n";
+
+       my $statements = $node->findvalue('./text()');
+       chomp($statements);
+       print CODE $statements."\n";
+
+       print CODE "\tclock += $clock;\n";
+
+       print "Processing $name\n";
+       my @opcodes = expandMask($mask);
+
+       if (!$prefix || length $prefix == 0)
+       {
+               $prefix = "default";
+       }
+
+       foreach my $opcode (@opcodes)
+       {
+               if ($Instructions{$prefix}{$opcode})
+               {
+                       die "Duplicate opcode. Prefix = $prefix. Mask = $mask, $name.\n".
+                               "Conflicting label = $Instructions{$prefix}{$opcode}\n";
+               }
+               $Instructions{$prefix}{$opcode} = $label;
+       }
+
+       print CODE "\n}\n";
+       print CODE "goto END_INSTRUCTIONS;\n";
+       print CODE "\n\n";
+}
+
index 7f4cb3c6d32f99f5a1851b1dd86281fb6002ac22..8dcc8dcb0f8083a8ae66f2f46b8cb6bdd1cc1f54 100644 (file)
@@ -1,23 +1,11 @@
-// http://www.opengl.org/sdk/docs/tutorials/ClockworkCoders/loading.php
+uniform sampler2D mmTexture;
+uniform vec4 mmTextureSize;
 
-#version 130
-
-uniform sampler2D OGL2Texture;
-uniform vec4 OGL2Size;
-
-/*
-void main (void)  
-{
-       // Every pixel becomes green
-       gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);  
-}
-*/
-
-in vec4 vTexCoord; // supplied from vertex processor
-in vec4 B;
-in vec4 D;
-in vec4 F;
-in vec4 H;
+varying vec4 vTexCoord; // supplied from vertex processor
+varying vec4 B;
+varying vec4 D;
+varying vec4 F;
+varying vec4 H;
 
 void main(void)
 
@@ -35,11 +23,14 @@ void main(void)
        vec4 E2;
        vec4 E3;
 
-       eColour = texture2DProj(OGL2Texture, vTexCoord);
-       bColour = texture2DProj(OGL2Texture, B);
-       dColour = texture2DProj(OGL2Texture, D);
-       fColour = texture2DProj(OGL2Texture, F);
-       hColour = texture2DProj(OGL2Texture, H);
+       vec4 outRow1;
+       vec4 outRow2;
+
+       eColour = texture2DProj(mmTexture, vTexCoord);
+       bColour = texture2DProj(mmTexture, B);
+       dColour = texture2DProj(mmTexture, D);
+       fColour = texture2DProj(mmTexture, F);
+       hColour = texture2DProj(mmTexture, H);
 
        if (bColour != hColour && dColour != fColour) {
                E0 = dColour == bColour ? dColour : eColour;
@@ -54,30 +45,10 @@ void main(void)
        }
 
        // Is this pixel E0,E1,E2 or E3, or somewhere in between ?
-       fragmentLocation = fract(vTexCoord * OGL2Size);
-       //fragmentLocation = fract(vTexCoord * texturesize(OGL2Texture));
+       // Bi-linear interpolate (may not be exact x2 scale)
+       fragmentLocation = fract(vTexCoord * mmTextureSize);
 
-       // TODO interpolate
-       if (fragmentLocation.x < 0.5)
-       {
-               if (fragmentLocation.y < 0.5)
-               {
-                       gl_FragColor = E0;
-               }
-               else
-               {
-                       gl_FragColor = E2;
-               }
-       }
-       else
-       {
-               if (fragmentLocation.y < 0.5)
-               {
-                       gl_FragColor = E1;
-               }
-               else
-               {
-                       gl_FragColor = E3;
-               }
-       }
+       outRow1 = mix(E0, E1, fragmentLocation.x);
+       outRow2 = mix(E2, E3, fragmentLocation.x);
+       gl_FragColor = mix(outRow1, outRow2, fragmentLocation.y);
 }
index 2297a09f7addcb072aa97f540e073f703c77c400..a2f67d5cc20a15a1ebc43837ec9765f9b85b588e 100644 (file)
@@ -1,38 +1,10 @@
-// http://www.opengl.org/sdk/docs/tutorials/ClockworkCoders/loading.php
+uniform vec4 mmTextureSize;
 
-
-// Built-in vertex attributes (per vertex)
-// gl_Vertex  Position of current vertex (vec4)
-// gl_Color   Primary colour of vertex (vec4)
-//gl_MultiTexCoord0  Texture Coordinate of texture unit 0
-
-// Built-in variables (passed from vertex to fragment)
-// Will be interpolated between vertex and fragment.
-
-// Scales output, so image only takes up top left of screen (1/4 of image);
-/*
-void main(void)
-{
-   vec4 a = gl_Vertex;
-   a.x = a.x * 2.0;//0.5;
-   a.y = a.y * 0.5;
-
-
-   gl_Position = gl_ModelViewProjectionMatrix * a;
-  // gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; normal code
-
-}  
-*/
-
-#version 130
-
-uniform vec4 OGL2Param;
-
-out vec4 vTexCoord; // Passed to fragment
-out vec4 B; // Passed to fragment
-out vec4 D; // Passed to fragment
-out vec4 F; // Passed to fragment
-out vec4 H; // Passed to fragment
+varying vec4 vTexCoord; // Passed to fragment
+varying vec4 B; // Passed to fragment
+varying vec4 D; // Passed to fragment
+varying vec4 F; // Passed to fragment
+varying vec4 H; // Passed to fragment
 
 
 void main(void)
@@ -40,8 +12,8 @@ void main(void)
        vec4 offsetx;
        vec4 offsety;
 
-       offsetx = vec4(OGL2Param.x, 0, 0, 0);
-       offsety = vec4(0, OGL2Param.y, 0, 0);
+       offsetx = vec4(1.0/mmTextureSize.x, 0, 0, 0);
+       offsety = vec4(0, 1.0/mmTextureSize.y, 0, 0);
 
        B = gl_MultiTexCoord0 - offsety;
        D = gl_MultiTexCoord0 - offsetx;
diff --git a/util.hh b/util.hh
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..40dc5ec00c817eaacff8464fb20539685fd9eef6 100644 (file)
--- a/util.hh
+++ b/util.hh
@@ -0,0 +1,63 @@
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
+//
+//     This file is part of glBoy.
+//
+//     glBoy 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 3 of the License, or
+//     (at your option) any later version.
+//
+//     glBoy 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 glBoy.  If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef GLBOY_UTIL_HH
+#define GLBOY_UTIL_HH
+
+#include <string>
+namespace glBoy
+{
+       static char hexChars[] =
+               {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
+
+       inline std::string hex(uint8_t val)
+       {
+               std::string result("0xFF");
+               result[2] = hexChars[val >> 4];
+               result[3] = hexChars[val & 0xf];
+               return result;
+       }
+
+       inline std::string hex(int8_t val)
+       {
+               std::string result;
+
+               if (val >= 0)
+               {
+                       result = "0x";
+               }
+               else
+               {
+                       result = "-0x";
+               }
+               result += hexChars[val >> 4];
+               result += hexChars[val & 0xf];
+               return result;
+       }
+
+       inline std::string hex(uint16_t val)
+       {
+               std::string result("0xFFFF");
+               result[2] = hexChars[val >> 12];
+               result[3] = hexChars[(val >> 8) & 0xf];
+               result[4] = hexChars[(val >> 4) & 0xf];
+               result[5] = hexChars[val & 0xf];
+               return result;
+       }
+}
+#endif
+