]> localhost Git - glBoy.git/commitdiff
Initial android port master origin/HEAD origin/master
authorMichael McMaster <michael@codesrc.com>
Fri, 14 Oct 2011 20:46:50 +0000 (06:46 +1000)
committerMichael McMaster <michael@codesrc.com>
Fri, 14 Oct 2011 20:46:50 +0000 (06:46 +1000)
56 files changed:
ALU.cc [deleted file]
ALU.hh
Config.cc [deleted file]
Core.cc
Core.hh
DAA.hh
DWORD.hh
FPS.cc [new file with mode: 0644]
FPS.hh [new file with mode: 0644]
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
MBC1.cc
MBC1.hh
MBC2.cc
MBC2.hh
MBC3.cc
MBC3.hh
Makefile.am
MemoryMap.cc
MemoryMap.hh
NEWS
RAM.cc
RAM.hh
RAM_4bit.cc
Registers.cc
Registers.hh
Test.cc [deleted file]
android/AndroidManifest.xml [new file with mode: 0644]
android/jni/Android.mk [new file with mode: 0644]
android/jni/AndroidInterface.cc [new file with mode: 0644]
android/jni/Application.mk [new file with mode: 0644]
android/jni/autoconfig.h [new file with mode: 0644]
android/src/com/codesrc/glboy/GlBoyActivity.java [new file with mode: 0644]
glBoy.config
hq4x.cc
hq4x.hh
libzipper
makeOpcodes.pl
sdl/GLGraphics.cc [new file with mode: 0644]
sdl/GLGraphics.hh [new file with mode: 0644]
sdl/Main.cc [moved from Main.cc with 80% similarity]
sdl/SDLKeys.cc [new file with mode: 0644]
sdl/SDLKeys.hh [new file with mode: 0644]
sdl/SDLSound.cc [new file with mode: 0644]
sdl/SDLSound.hh [new file with mode: 0644]

diff --git a/ALU.cc b/ALU.cc
deleted file mode 100644 (file)
index b8291f5..0000000
--- a/ALU.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-//     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 d71a1e644e3ec5ff90b10e404a9c8fd93df497ba..fa0132161bd977ddbff75d98bbb7d59277e086a2 100644 (file)
--- a/ALU.hh
+++ b/ALU.hh
@@ -26,33 +26,16 @@ namespace glBoy
 {\r
 namespace ALU\r
 {\r
 {\r
 namespace ALU\r
 {\r
-void load();\r
-extern const uint8_t ParityFlagLookup[256];\r
-\r
 inline uint8_t\r
 add(uint8_t a, uint8_t b, Flags& flags, bool carry = false)\r
 {\r
        uint8_t c = (carry && flags.C) ? 1 : 0;\r
        uint8_t result(a + b + c);\r
 \r
 inline uint8_t\r
 add(uint8_t a, uint8_t b, Flags& flags, bool carry = false)\r
 {\r
        uint8_t c = (carry && 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 ^ b ^ result) & 0x10) |             // H\r
-                                                                                               // U3 TODO\r
-\r
-               // If a and b have the same sign, and the result\r
-               // has a different sign, then we overflowed the\r
-               // register.\r
-               // Can never overflow if a and b have different signs. (even with\r
-               // carry, since 127 + -1 + carry == 127).\r
-               // Step 1) Determine whether a and b have the same sign. (a NXOR b)\r
-               // Step 2) Determine whether the result has // a different sign.\r
-               ((((~(a ^ b)) & (result ^ a)) & 0x80) >> 5) |// P\r
-\r
-                                                                                               // N\r
-               (((uint16_t(a) + uint16_t(b) + c) & 0x100) >> 8); // C\r
+       flags.C = ((uint16_t(a) + uint16_t(b) + c) & 0x100) ? 1 : 0;\r
+       flags.H = ((a ^ b ^ result) & 0x10) ? 1 : 0;\r
+       flags.N = 0;\r
+       flags.Z = (result == 0) ? 1 : 0;\r
 \r
        return result;\r
 }\r
 \r
        return result;\r
 }\r
@@ -75,10 +58,7 @@ add(DWORD a, DWORD b, Flags& flags, bool carry = false)
        if (carry)\r
        {\r
                // Flags only affected for adc.\r
        if (carry)\r
        {\r
                // Flags only affected for adc.\r
-               flags.S = (result & 0x8000) ? 1 : 0;\r
                flags.Z = (result == 0);\r
                flags.Z = (result == 0);\r
-               flags.P = (((~(aHost ^ bHost)) & (result ^ aHost)) & 0x8000) ? 1 : 0;\r
-\r
        }\r
 \r
        flags.N = 0;\r
        }\r
 \r
        flags.N = 0;\r
@@ -88,6 +68,21 @@ add(DWORD a, DWORD b, Flags& flags, bool carry = false)
        return glBoy::htoz(result);\r
 }\r
 \r
        return glBoy::htoz(result);\r
 }\r
 \r
+inline DWORD\r
+add(DWORD a, int8_t b, Flags& flags)\r
+{\r
+       const uint16_t aHost(a.host());\r
+\r
+       uint16_t result(aHost + b);\r
+\r
+       flags.C = (aHost & 0xFF) + uint8_t(b) > 0xFF ? 1 : 0;\r
+       flags.N = 0;\r
+       flags.H = ((aHost ^ b ^ result) & 0x10) ? 1 : 0;\r
+       flags.Z = 0;\r
+\r
+       return glBoy::htoz(result);\r
+}\r
+\r
 inline DWORD\r
 adc(DWORD a, DWORD b, Flags& flags)\r
 {\r
 inline DWORD\r
 adc(DWORD a, DWORD b, Flags& flags)\r
 {\r
@@ -100,22 +95,10 @@ sub(uint8_t a, uint8_t b, Flags& flags, bool borrow = false)
        uint8_t c = (borrow && flags.C) ? 1 : 0;\r
        uint8_t result(a - b - c);\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) - c) & 0x100) >> 8); // C\r
+       flags.C = ((uint16_t(a) - uint16_t(b) - c) & 0x100) ? 1 : 0;\r
+       flags.H = (((a & 0xf) - (b & 0xf) - c) & 0x10) ? 1 : 0;\r
+       flags.N = 1;\r
+       flags.Z = (result == 0) ? 1 : 0;\r
 \r
        return result;\r
 }\r
 \r
        return result;\r
 }\r
@@ -135,22 +118,10 @@ sub(DWORD a, DWORD b, Flags& flags, bool borrow = false)
        uint16_t c = (borrow && flags.C) ? 1 : 0;\r
        uint16_t result(aHost - bHost - c);\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) - c) & 0x10000) >> 16); // C\r
+       flags.C = (((uint32_t(aHost) - uint32_t(bHost) - c) & 0x10000)) ? 1 : 0;\r
+       flags.H = (((aHost & 0xf00) - (bHost & 0xf00)) & 0x1000) ? 1 : 0;\r
+       flags.N = 1;\r
+       flags.Z = (result == 0) ? 1 : 0;\r
 \r
        return htoz(result);\r
 }\r
 \r
        return htoz(result);\r
 }\r
@@ -165,15 +136,12 @@ inline uint8_t
 bitwiseAnd(uint8_t a, uint8_t b, Flags& flags)\r
 {\r
        uint8_t result(a & b);\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
+\r
+       flags.C = 0;\r
+       flags.H = 1;\r
+       flags.N = 0;\r
+       flags.Z = (result == 0) ? 1 : 0;\r
+\r
        return result;\r
 }\r
 \r
        return result;\r
 }\r
 \r
@@ -181,15 +149,11 @@ inline uint8_t
 bitwiseOr(uint8_t a, uint8_t b, Flags& flags)\r
 {\r
        uint8_t result(a | b);\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
+\r
+       flags.C = 0;\r
+       flags.H = 0;\r
+       flags.N = 0;\r
+       flags.Z = (result == 0) ? 1 : 0;\r
        return result;\r
 }\r
 \r
        return result;\r
 }\r
 \r
@@ -197,15 +161,11 @@ inline uint8_t
 bitwiseXor(uint8_t a, uint8_t b, Flags& flags)\r
 {\r
        uint8_t result(a ^ b);\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
+\r
+       flags.C = 0;\r
+       flags.H = 0;\r
+       flags.N = 0;\r
+       flags.Z = (result == 0) ? 1 : 0;\r
        return result;\r
 }\r
 \r
        return result;\r
 }\r
 \r
@@ -226,14 +186,14 @@ rotateLeft(uint8_t a, Flags& flags, bool throughCarry, bool setAllFlags = false)
        flags.H = 0;\r
        flags.N = 0;\r
        flags.C = msb;\r
        flags.H = 0;\r
        flags.N = 0;\r
        flags.C = msb;\r
-\r
        if (setAllFlags)\r
        {\r
        if (setAllFlags)\r
        {\r
-               flags.byte |= (a & 0x80);\r
                flags.Z = (a == 0) ? 1 : 0;\r
                flags.Z = (a == 0) ? 1 : 0;\r
-               flags.byte |= ParityFlagLookup[a];\r
        }\r
        }\r
-// TODO U3 U5\r
+       else\r
+       {\r
+               flags.Z = 0;\r
+       }\r
 \r
        return a;\r
 }\r
 \r
        return a;\r
 }\r
@@ -244,15 +204,10 @@ shiftLeft(uint8_t a, Flags& flags)
        uint8_t msb(a >> 7);\r
        a <<= 1;\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
+       flags.C = msb;\r
+       flags.H = 0;\r
+       flags.N = 0;\r
+       flags.Z = (a == 0) ? 1 : 0;\r
 \r
        return a;\r
 }\r
 \r
        return a;\r
 }\r
@@ -277,11 +232,12 @@ rotateRight(uint8_t a, Flags& flags, bool throughCarry, bool setAllFlags = false
 \r
        if (setAllFlags)\r
        {\r
 \r
        if (setAllFlags)\r
        {\r
-               flags.byte |= (a & 0x80);\r
                flags.Z = (a == 0) ? 1 : 0;\r
                flags.Z = (a == 0) ? 1 : 0;\r
-               flags.byte |= ParityFlagLookup[a];\r
        }\r
        }\r
-// TODO U3 U5\r
+       else\r
+       {\r
+               flags.Z = 0;\r
+       }\r
 \r
        return a;\r
 }\r
 \r
        return a;\r
 }\r
@@ -296,21 +252,16 @@ shiftRight(uint8_t a, Flags& flags, bool arithmatic)
        // Restore sign bit\r
        if (arithmatic) a |= msb;\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
+       flags.C = lsb;\r
+       flags.H = 0;\r
+       flags.N = 0;\r
+       flags.Z = (a == 0) ? 1 : 0;\r
 \r
        return a;\r
 }\r
 \r
 inline void\r
 \r
        return a;\r
 }\r
 \r
 inline void\r
-RLD(uint8_t& a, uint8_t& m, Flags& f)\r
+RLD(uint8_t& a, uint8_t& m, Flags& flags)\r
 {\r
        // Whoever came up with RLD was on crack.\r
 \r
 {\r
        // Whoever came up with RLD was on crack.\r
 \r
@@ -318,19 +269,14 @@ RLD(uint8_t& a, uint8_t& m, Flags& f)
        a = (a & 0xF0) | (m >> 4);\r
        m = newM;\r
 \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
+       flags.C = 0;\r
+       flags.H = 0;\r
+       flags.N = 0;\r
+       flags.Z = (a == 0) ? 1 : 0;\r
 }\r
 \r
 inline void\r
 }\r
 \r
 inline void\r
-RRD(uint8_t& a, uint8_t& m, Flags& f)\r
+RRD(uint8_t& a, uint8_t& m, Flags& flags)\r
 {\r
        // Whoever came up with RRD was on crack.\r
 \r
 {\r
        // Whoever came up with RRD was on crack.\r
 \r
@@ -338,15 +284,10 @@ RRD(uint8_t& a, uint8_t& m, Flags& f)
        a = (a & 0xF0) | (m & 0xf);\r
        m = newM;\r
 \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
+       flags.C = 0;\r
+       flags.H = 0;\r
+       flags.N = 0;\r
+       flags.Z = (a == 0) ? 1 : 0;\r
 }\r
 \r
 } // namespace ALU\r
 }\r
 \r
 } // namespace ALU\r
diff --git a/Config.cc b/Config.cc
deleted file mode 100644 (file)
index 3ced34d..0000000
--- a/Config.cc
+++ /dev/null
@@ -1,291 +0,0 @@
-//     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++];
-       }
-
-       if (access(filename.c_str(), R_OK) != 0)
-       {
-               char* error(strerror(errno));
-               std::cerr << "Could not read file " << filename <<
-                       "(" << error << ")" << std::endl;
-               exit(1);
-       }
-
-       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();
-}
-
diff --git a/Core.cc b/Core.cc
index 8daf450ccd969233b569c7c2c0c2ff0466ff5bd9..493a3b510f3ce87fdfaa4829fbe8bcfb75ae8725 100644 (file)
--- a/Core.cc
+++ b/Core.cc
 #include "Log.hh"\r
 #include "util.hh"\r
 \r
 #include "Log.hh"\r
 #include "util.hh"\r
 \r
+#include "RAM.hh"\r
+\r
 #include <cassert>\r
 #include <cstddef>\r
 #include <limits>\r
 #include <sstream>\r
 \r
 #include <cassert>\r
 #include <cstddef>\r
 #include <limits>\r
 #include <sstream>\r
 \r
-#include <string.h> // ffs\r
+#include <strings.h> // ffs\r
 \r
 using namespace glBoy;\r
 \r
 \r
 using namespace glBoy;\r
 \r
+namespace\r
+{\r
+       class MappedRegister : public MemoryMap::Memory\r
+       {\r
+       public:\r
+               MappedRegister(uint8_t* reg) : m_reg(reg) {}\r
+               virtual uint8_t read8(uint16_t) { return *m_reg; }\r
+               virtual void write8(uint16_t, uint8_t value) { *m_reg = value; }\r
+\r
+       private:\r
+               uint8_t* m_reg; // Points to member variable of Core class.\r
+       };\r
+}\r
+\r
 Core::Core() :\r
 Core::Core() :\r
-       m_iff1(0),\r
-       m_iff2(0),\r
-       m_pendingInterrupts(0),\r
+       m_running(true),\r
+       m_ime(0),\r
+       m_ie(0),\r
+       m_if(0),\r
        m_halted(false),\r
        m_clock(0),\r
        m_nextPeriodicCallback(std::numeric_limits<cycle_t>::max())\r
 {\r
        m_reg.reset();\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
+       std::shared_ptr<MemoryMap::Memory> ifMemory(\r
+               new MappedRegister(&m_if));\r
+       m_mem.map(0xFF0F, 1, ifMemory);\r
+\r
+       std::shared_ptr<MemoryMap::Memory> ieMemory(\r
+               new MappedRegister(&m_ie));\r
+       m_mem.map(0xFFFF, 1, ieMemory);\r
 }\r
 \r
 void\r
 }\r
 \r
 void\r
-Core::raiseInterrupt(int interrupt)\r
+Core::raiseInterrupt(uint8_t interrupt)\r
 {\r
        // TODO this interrupt handling is GB80 specific\r
 {\r
        // TODO this interrupt handling is GB80 specific\r
-\r
-       switch (interrupt)\r
-       {\r
-       case 0x40: m_pendingInterrupts |= 0x1; break;\r
-       case 0x48: m_pendingInterrupts |= 0x2; break;\r
-       case 0x50: m_pendingInterrupts |= 0x4; break;\r
-       case 0x58: m_pendingInterrupts |= 0x8; break;\r
-       case 0x60: m_pendingInterrupts |= 0x10; break;\r
-       }\r
+       m_if = (m_if | (uint8_t(1) << ((interrupt - 0x40) / 8))) & m_ie;\r
 }\r
 \r
 void\r
 }\r
 \r
 void\r
@@ -69,50 +85,54 @@ Core::run()
        // handler\r
        // TODO z80 only. Unused for gb80 uint8_t doublePrefixOperand(0);\r
 \r
        // handler\r
        // TODO z80 only. Unused for gb80 uint8_t doublePrefixOperand(0);\r
 \r
-       while (true)\r
+#ifdef GLBOY_DEBUG\r
+       bool traceCPU(Log::Instance().isDebugLevelOn(Log::DBG_TRACE_CPU));\r
+#endif\r
+       while (m_running)\r
        {\r
                cycle_t clock = 0;\r
 \r
                uint16_t prefix(0); // Used for debug logging only.\r
 \r
                // Check for interrupts.\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_pendingInterrupts)\r
+               if (m_if)\r
                {\r
                        // Only call ONE interrupt, even if multiple triggered.\r
                {\r
                        // Only call ONE interrupt, even if multiple triggered.\r
-                       uint8_t priorityBit(1 << (ffs(m_pendingInterrupts) - 1));\r
-\r
-                       // We clear the IF of the interrupt we end up calling.\r
-                       // BUT if there are 2 simulataneous interrupts, it is left\r
-                       // with bits set, but never called.\r
-                       uint8_t iFlag(m_mem.read8(htoz(0xFF0F)));\r
-                       iFlag |= m_pendingInterrupts;\r
-                       m_pendingInterrupts = 0;\r
-\r
-                       uint8_t ie(m_mem.read8(htoz(0xFFFF)));\r
-\r
-                       if (m_iff1 && (ie & priorityBit))\r
+                       if (m_ime)\r
                        {\r
                        {\r
-                               // Clear the IF of ONLY the interrupt we end up calling.\r
-                               iFlag ^= priorityBit;\r
+                               // We clear the IF of the interrupt we end up calling.\r
+                               // BUT if there are 2 simulataneous interrupts, it is left\r
+                               // with bits set\r
 \r
 \r
-                               m_iff1 = 0;\r
-                               m_iff2 = 0;\r
+                               uint8_t priorityBit(ffs(m_if) - 1);\r
+                               m_if ^= (uint8_t(1) << priorityBit);\r
+                               m_ime = 0;\r
 \r
                                m_reg.SP.dec(2);\r
                                m_mem.write16(m_reg.SP, m_reg.PC);\r
 \r
 \r
                                m_reg.SP.dec(2);\r
                                m_mem.write16(m_reg.SP, m_reg.PC);\r
 \r
-                               static const int addr[] = {0x40, 0x48, 0x50, 0x58, 0x60};\r
-                               m_reg.PC = htoz(addr[ffs(priorityBit) - 1]);\r
-\r
-                               m_halted = false;\r
+                               m_reg.PC = htoz((priorityBit * 8) + 0x40);\r
                        }\r
                        }\r
-                       m_mem.write8(htoz(0xFF0F), iFlag);\r
+                       m_halted = false; // Interrupts always recover from HALT\r
                }\r
                }\r
-               else if (!m_halted)\r
+\r
+               if (!m_halted)\r
                {\r
                {\r
-                       uint16_t opcodePC = m_reg.PC.host(); // For debugging\r
                        uint8_t opcode = op8();\r
 \r
                        uint8_t opcode = op8();\r
 \r
+#ifdef GLBOY_DEBUG\r
+                       DWORD opcodePC = m_reg.PC; // For debugging\r
+\r
+                       std::stringstream ss;\r
+                       std::ostream& dbg(traceCPU ? ss : Log::Instance().nullStream());\r
+\r
+                       if (m_mem.getBankSize(m_reg.PC))\r
+                       {\r
+                               dbg << hex(m_mem.getBankIndex(opcodePC)) << ".";\r
+                       }\r
+                       dbg << opcodePC << " ";\r
+#endif\r
+\r
                        goto *(OpcodeTable[opcode]);\r
 \r
 #include "InstructionSet_Opcodes.cc"\r
                        goto *(OpcodeTable[opcode]);\r
 \r
 #include "InstructionSet_Opcodes.cc"\r
@@ -120,11 +140,14 @@ Core::run()
 BAD_OPCODE:\r
                        Err() <<\r
                                "Unknown Opcode: " << glBoy::hex(opcode) <<\r
 BAD_OPCODE:\r
                        Err() <<\r
                                "Unknown Opcode: " << glBoy::hex(opcode) <<\r
-                               " (PC=" << opcodePC << ")" <<\r
+                               " (PC=" << m_reg.PC << ")" <<\r
                                Log::endl;\r
                        assert(false);\r
 \r
 END_INSTRUCTIONS: ;\r
                                Log::endl;\r
                        assert(false);\r
 \r
 END_INSTRUCTIONS: ;\r
+#ifdef GLBOY_DEBUG\r
+                       TraceCPU() << ss.str() << " A=" << hex(m_reg.A) << Log::endl;\r
+#endif\r
                }\r
                else\r
                {\r
                }\r
                else\r
                {\r
@@ -134,7 +157,7 @@ END_INSTRUCTIONS: ;
                m_clock += clock;\r
 \r
 \r
                m_clock += clock;\r
 \r
 \r
-               if (m_halted && (m_pendingInterrupts == 0))\r
+               if (m_halted && (m_if == 0))\r
                {\r
                        m_clock = std::max(m_clock, m_nextPeriodicCallback);\r
                }\r
                {\r
                        m_clock = std::max(m_clock, m_nextPeriodicCallback);\r
                }\r
@@ -162,6 +185,7 @@ END_INSTRUCTIONS: ;
                        }\r
                }\r
        }\r
                        }\r
                }\r
        }\r
+\r
 }\r
 \r
 Core::CallbackId\r
 }\r
 \r
 Core::CallbackId\r
diff --git a/Core.hh b/Core.hh
index a2b10b02cdc64df4b143ac0ac73011b3c7b4a159..e3f3a13a611233aba28a7c3bd7b52fda2eb61ad3 100644 (file)
--- a/Core.hh
+++ b/Core.hh
@@ -40,12 +40,13 @@ public:
        Registers& getRegisters() { return m_reg; }\r
 \r
        void run();\r
        Registers& getRegisters() { return m_reg; }\r
 \r
        void run();\r
+       void resume() { m_running = true; }\r
+       void stop() { m_running = false; }\r
+       bool isRunning() const { return m_running; }\r
 \r
        cycle_t getClockCount() const { return m_clock; }\r
 \r
 \r
        cycle_t getClockCount() const { return m_clock; }\r
 \r
-       bit getIFF2() const { return m_iff2; }\r
-\r
-       void raiseInterrupt(int interrupt);\r
+       void raiseInterrupt(uint8_t interrupt);\r
        void halt() { m_halted = true; }\r
 \r
        typedef int CallbackId;\r
        void halt() { m_halted = true; }\r
 \r
        typedef int CallbackId;\r
@@ -59,17 +60,18 @@ private:
        uint8_t op8();\r
        DWORD op16();\r
 \r
        uint8_t op8();\r
        DWORD op16();\r
 \r
-       // The Z80 can switch between register sets.\r
+       bool m_running;\r
+\r
        Registers m_reg;\r
        Registers m_reg;\r
-       Registers m_regDash;\r
 \r
 \r
-       // Interrupt enablement\r
-       bit m_iff1;\r
-       bit m_iff2;\r
-       uint8_t m_pendingInterrupts;\r
+       // Interrupt master enable\r
+       bit m_ime;\r
+\r
+       // Interrupt enable - enables/disables specific interrupts\r
+       uint8_t m_ie;\r
 \r
 \r
-       enum InterruptMode { IM_0, IM_1, IM_2 };\r
-       InterruptMode m_interruptMode;\r
+       // Interrupt flag - pending interrupts\r
+       uint8_t m_if;\r
 \r
        bool m_halted;\r
 \r
 \r
        bool m_halted;\r
 \r
diff --git a/DAA.hh b/DAA.hh
index 6e1c3bf6a25caafd58a89a52e29d5837d257aa0c..d0d5cc92366178d979022109d7a608dadc16f793 100644 (file)
--- a/DAA.hh
+++ b/DAA.hh
@@ -42,17 +42,10 @@ inline uint8_t DAA(uint8_t a, Flags& flags)
                if (flags.C || (a > 0x99)) result += 0x60;
        }
 
                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
+       flags.C = (flags.C || (a > 0x99)) ? 1 : 0;
+       flags.H = 0;
+       // flags.N = no change
+       flags.Z = (result == 0) ? 1 : 0;
 
        return result;
 }
 
        return result;
 }
index abf665a7fb074870cddad06a04c872f997b098d4..8376fcca35d7d625813ba86b05b5114a56b27a17 100644 (file)
--- a/DWORD.hh
+++ b/DWORD.hh
@@ -78,6 +78,11 @@ inline std::ostream& operator<<(std::ostream& out, const glBoy::DWORD& val)
        return out;\r
 }\r
 \r
        return out;\r
 }\r
 \r
+inline std::string hex(const DWORD& val)\r
+{\r
+       return hex(val.host());\r
+}\r
+\r
 // Create a dword from z80-ordered mem.\r
 inline DWORD CreateDWORD(uint8_t* in)\r
 {\r
 // Create a dword from z80-ordered mem.\r
 inline DWORD CreateDWORD(uint8_t* in)\r
 {\r
diff --git a/FPS.cc b/FPS.cc
new file mode 100644 (file)
index 0000000..2d9a62e
--- /dev/null
+++ b/FPS.cc
@@ -0,0 +1,96 @@
+//     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 "FPS.hh"
+#include "Log.hh"
+
+#include <cmath>
+
+#include <sys/time.h>
+#include <time.h>
+
+using namespace glBoy;
+
+FPS::FPS(const std::string& displayName, const double& target) :
+       m_displayName(displayName),
+       m_targetFPS(target),
+       m_frameCount(0),
+       m_fps(0)
+{
+       gettimeofday(&m_start, 0);
+}
+
+void
+FPS::frameComplete()
+{
+       m_frameCount++;
+       calcFPS();
+}
+
+double
+FPS::getFPS()
+{
+       calcFPS();
+       return m_fps;
+}
+
+void
+FPS::calcFPS()
+{
+       timeval now;
+       gettimeofday(&now, NULL);
+       if (now.tv_sec == m_start.tv_sec)
+       {
+               return;
+       }
+
+       //bool display(now.tv_sec - m_start.tv_sec > 3);
+
+       const double usec(1000000.0);
+       m_fps = static_cast<double>(m_frameCount) /
+               (
+                       (now.tv_sec + now.tv_usec / usec) -
+                       (m_start.tv_sec + m_start.tv_usec / usec)
+               );
+       m_start = now;
+       m_frameCount = 0;
+
+       //if (display) displayStats();
+       displayStats();
+}
+
+void
+FPS::displayStats()
+{
+       if (m_targetFPS > 0)
+       {
+               Stat() <<
+                       m_displayName << " FPS = " << m_fps <<
+                       " (error=" << std::abs((100 - (m_fps * 100 / m_targetFPS))) <<
+                       "%) " <<
+                       Log::endl;
+       }
+       else
+       {
+               Stat() <<
+                       m_displayName << " FPS = " << m_fps <<
+                       Log::endl;
+       }
+}
+
diff --git a/FPS.hh b/FPS.hh
new file mode 100644 (file)
index 0000000..8ad7af1
--- /dev/null
+++ b/FPS.hh
@@ -0,0 +1,51 @@
+//     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_FPS_HH
+#define GLBOY_FPS_HH
+
+#include "glBoy.hh"
+#include <string>
+
+namespace glBoy
+{
+
+class FPS
+{
+public:
+       FPS(const std::string& displayName, const double& target);
+
+       double getFPS();
+
+       void frameComplete();
+private:
+       void calcFPS();
+       void displayStats();
+
+       std::string m_displayName;
+       double m_targetFPS;
+
+       timeval m_start;
+       int m_frameCount;
+       double m_fps;
+
+};
+
+} // namespace glBoy
+
+#endif
+
index a48313fba1bcdcdb7aed3e3384c3897477883dda..d7d9e37251cba3b78a1f5e1bab9a1732726e4e10 100644 (file)
 
 #include <cassert>
 
 
 #include <cassert>
 
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <unistd.h>
+
 using namespace glBoy;
 using namespace zipper;
 using namespace std;
 using namespace glBoy;
 using namespace zipper;
 using namespace std;
@@ -69,7 +75,7 @@ namespace
                        {
                                if (offset + bytes <= GameboyCart::MaxRomBytes)
                                {
                        {
                                if (offset + bytes <= GameboyCart::MaxRomBytes)
                                {
-                                       data.resize(std::max(offset + bytes, data.size()));
+                                       data.resize(std::max(offset + bytes, zsize_t(data.size())));
                                        std::copy(inData, inData + bytes, &data[offset]);
                                }
                                else
                                        std::copy(inData, inData + bytes, &data[offset]);
                                }
                                else
@@ -148,7 +154,7 @@ GameboyCart::init(const std::vector<uint8_t>& rom)
        {
                m_mbc = "ROM";
 
        {
                m_mbc = "ROM";
 
-               size_t romSize(std::min(0x8000ul, rom.size()));
+               size_t romSize(std::min(size_t(0x8000ul), rom.size()));
                shared_ptr<MemoryMap::Memory> mem(
                        new ROM(&rom[0], romSize)
                        );
                shared_ptr<MemoryMap::Memory> mem(
                        new ROM(&rom[0], romSize)
                        );
@@ -162,7 +168,7 @@ GameboyCart::init(const std::vector<uint8_t>& rom)
        {
                m_mbc = "MBC1";
 
        {
                m_mbc = "MBC1";
 
-               size_t romSize(std::min(0x200000ul, rom.size()));
+               size_t romSize(std::min(size_t(0x200000ul), rom.size()));
                shared_ptr<MemoryMap::Memory> mem(
                        new MBC1(this, &rom[0], &rom[0] + romSize)
                        );
                shared_ptr<MemoryMap::Memory> mem(
                        new MBC1(this, &rom[0], &rom[0] + romSize)
                        );
@@ -176,7 +182,7 @@ GameboyCart::init(const std::vector<uint8_t>& rom)
        {
                m_mbc = "MBC2";
 
        {
                m_mbc = "MBC2";
 
-               size_t romSize(std::min(0x40000ul, rom.size()));
+               size_t romSize(std::min(size_t(0x40000ul), rom.size()));
                shared_ptr<MemoryMap::Memory> mem(
                        new MBC2(this, &rom[0], &rom[0] + romSize)
                        );
                shared_ptr<MemoryMap::Memory> mem(
                        new MBC2(this, &rom[0], &rom[0] + romSize)
                        );
@@ -197,7 +203,7 @@ GameboyCart::init(const std::vector<uint8_t>& rom)
        {
                m_mbc = "MBC3";
 
        {
                m_mbc = "MBC3";
 
-               size_t romSize(std::min(0x200000ul, rom.size()));
+               size_t romSize(std::min(size_t(0x200000ul), rom.size()));
                shared_ptr<MemoryMap::Memory> mem(
                        new MBC3(this, &rom[0], &rom[0] + romSize)
                        );
                shared_ptr<MemoryMap::Memory> mem(
                        new MBC3(this, &rom[0], &rom[0] + romSize)
                        );
@@ -325,6 +331,8 @@ GameboyCart::init(const std::vector<uint8_t>& rom)
                tmp << (cartRam / 1024) << "KB";
                tmp >> m_extRamSize;
        }
                tmp << (cartRam / 1024) << "KB";
                tmp >> m_extRamSize;
        }
+
+       loadExtRam();
 }
 
 void
 }
 
 void
@@ -357,3 +365,79 @@ GameboyCart::isValid() const
 {
        return m_isValid;
 }
 {
        return m_isValid;
 }
+
+void
+GameboyCart::loadExtRam()
+{
+       if (!m_battery) return;
+
+       struct passwd* entry(getpwuid(getuid()));
+       if (!entry || !entry->pw_dir)
+       {
+               return;
+       }
+
+       std::stringstream file;
+       file << entry->pw_dir << "/.glBoy/" << m_title;
+       int fd(::open(file.str().c_str(), O_RDONLY));
+       if (fd < 0) return;
+
+       std::vector<std::shared_ptr<MemoryMap::Memory> >::iterator
+               it(m_ramBanks.begin());
+       for (; it != m_ramBanks.end(); ++it)
+       {
+               uint8_t buffer[8192];
+               ::read(fd, buffer, sizeof(buffer)); // TODO EINTR ?
+               for (uint16_t i = 0; i < sizeof(buffer); ++i)
+               {
+                       (*it)->write8(i, buffer[i]);
+               }
+       }
+
+       ::close(fd);
+}
+
+
+void
+GameboyCart::saveExtRam()
+{
+       if (!m_battery) return;
+
+       struct passwd* entry(getpwuid(getuid()));
+       if (!entry || !entry->pw_dir)
+       {
+               return;
+       }
+
+       std::stringstream file;
+       file << entry->pw_dir << "/.glBoy/";
+
+       ::mkdir(file.str().c_str(), 0775);
+
+       file << m_title;
+
+       std::stringstream tmpFile;
+       tmpFile << file.str() << ".tmp";
+       int fd(::open(file.str().c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0660));
+       if (fd < 0) return;
+
+       bool error(false);
+
+       std::vector<std::shared_ptr<MemoryMap::Memory> >::iterator
+               it(m_ramBanks.begin());
+       for (; it != m_ramBanks.end() && !error; ++it)
+       {
+               uint8_t buffer[8192];
+               std::fill(buffer, buffer + sizeof(buffer), 0);
+               (*it)->copy(buffer, 0, 0x2000);
+               int writeResult(::write(fd, buffer, sizeof(buffer))); // TODO EINTR ?
+               error = writeResult != sizeof(buffer);
+       }
+       ::close(fd);
+       if (!error)
+       {
+               // atomicly switch the files.
+               ::rename(tmpFile.str().c_str(), file.str().c_str());
+       }
+
+}
index c5c1589630c17395eeb10f21d95130c445749cd5..cce1745c2884071104ea8bb17dae56e26e6375bc 100644 (file)
@@ -20,7 +20,9 @@
 
 #include "glBoy.hh"
 #include "Core.hh"
 
 #include "glBoy.hh"
 #include "Core.hh"
+#include <exception>
 #include <ostream>
 #include <ostream>
+#include <stdexcept>
 #include <vector>
 
 namespace glBoy
 #include <vector>
 
 namespace glBoy
@@ -29,6 +31,17 @@ namespace glBoy
 class GameboyCart
 {
 public:
 class GameboyCart
 {
 public:
+       class Invalid : public std::runtime_error
+       {
+       public:
+               Invalid(const std::string& what) : std::runtime_error(what) {}
+       };
+
+       class Unsupported : public std::runtime_error
+       {
+               Unsupported(const std::string& what) : std::runtime_error(what) {}
+       };
+
        enum
        {
                MinRomBytes = 16384,
        enum
        {
                MinRomBytes = 16384,
@@ -43,6 +56,10 @@ public:
        void dump(std::ostream& out);
 
        void setRamBank(size_t bank);
        void dump(std::ostream& out);
 
        void setRamBank(size_t bank);
+
+       void loadExtRam();
+       void saveExtRam();
+
 private:
        void init(const std::vector<uint8_t>& rom);
        Core& m_core;
 private:
        void init(const std::vector<uint8_t>& rom);
        Core& m_core;
index 651666f3131c14c58aee7e7c1635b3692735687e..44c17866dcc0623f88bed25b6fc424ca7f3584cc 100644 (file)
 //     You should have received a copy of the GNU General Public License
 //     along with glBoy.  If not, see <http://www.gnu.org/licenses/>.
 
 //     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 "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 <cassert>
 #include <limits>
 
-#include <sys/time.h>
-
+#include <string.h>
 
 using namespace glBoy;
 
 
 using namespace glBoy;
 
@@ -239,15 +231,9 @@ public:
 
 
 GameboyGraphics::GameboyGraphics(
 
 
 GameboyGraphics::GameboyGraphics(
-       Core& core,
-       SDL_Surface* surface,
-       PixelScaler scaler,
-       ResampleMethod resampler
+       Core& core
        ) :
        m_core(core),
        ) :
        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(
        m_vram(new uint8_t[0x2000]),
        m_oam(new uint8_t[0x100]), // Should be 0x9F, but many access to 100
        m_reg(
@@ -258,13 +244,11 @@ GameboyGraphics::GameboyGraphics(
                        )
                ),
        m_backgroundRedraw(true),
                        )
                ),
        m_backgroundRedraw(true),
-       m_surface(surface),
        m_frameSkip(false),
        m_frameSkip(false),
-       m_lastSkipped(false),
-       m_stat_frames(0),
-       m_stat_skipped(0),
-       m_stat_redraw(0)
+       m_fps("Renderer", 59.73)
 {
 {
+       m_screenShadowMutex = PTHREAD_MUTEX_INITIALIZER;
+
        MemoryMap& mapping(core.getMemoryMap());
        mapping.map(
                0x8000,
        MemoryMap& mapping(core.getMemoryMap());
        mapping.map(
                0x8000,
@@ -298,12 +282,11 @@ GameboyGraphics::GameboyGraphics(
        m_mode3 = core.registerPeriodic(
                456, 80, 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()
 {
 }
 
 GameboyGraphics::~GameboyGraphics()
 {
+       pthread_mutex_destroy(&m_screenShadowMutex);
        delete[] m_vram;
        delete[] m_oam;
 }
        delete[] m_vram;
        delete[] m_oam;
 }
@@ -359,18 +342,11 @@ GameboyGraphics::startScanLine()
        {
                // Draw the entire screen when entering vblank. This will be done just
                // under 60 times per second.
        {
                // Draw the entire screen when entering vblank. This will be done just
                // under 60 times per second.
-               if (!m_frameSkip || m_lastSkipped)
+               if (!m_frameSkip)
                {
                        renderScreen();
                {
                        renderScreen();
-                       m_lastSkipped = false;
-               }
-               else
-               {
-                       ++m_stat_skipped;
-                       m_lastSkipped = true;
                }
                m_frameSkip = false;
                }
                m_frameSkip = false;
-               displayStats();
 
                m_core.raiseInterrupt(0x40); // Start of vblank
                if (m_reg->STAT & 0x10)
 
                m_core.raiseInterrupt(0x40); // Start of vblank
                if (m_reg->STAT & 0x10)
@@ -539,7 +515,6 @@ GameboyGraphics::renderScreen()
                if (m_backgroundRedraw)
                {
                        m_backgroundRedraw = false;
                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);
 
                        // Select one of two maps at 0x9800/0x9C00
                        uint16_t tileMapAddr(m_reg->LCDC & 0x08 ? 0x1C00 : 0x1800);
@@ -634,9 +609,12 @@ GameboyGraphics::renderScreen()
                                );
                }
        }
                                );
                }
        }
-       updateOutputDevice();
 
 
+       pthread_mutex_lock(&m_screenShadowMutex);
+       ::memcpy(m_screenShadow, m_screen, sizeof(m_screenShadow));
+       pthread_mutex_unlock(&m_screenShadowMutex);
 
 
+       m_fps.frameComplete();
 }
 
 GameboyGraphics::Sprite
 }
 
 GameboyGraphics::Sprite
@@ -653,108 +631,31 @@ GameboyGraphics::getSprite(int spriteNum) const
 }
 
 void
 }
 
 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)
-                       );
-
-               Stat() <<
-                       "FPS = " << fps <<
-                       " (error=" << (100 - (fps * 100 / 59.73)) << "%) " <<
-                       "backround redraw=" << m_stat_redraw <<
-                       Log::endl;
-               m_stat_frames = 0;
-               m_stat_skipped = 0;
-               m_stat_redraw = 0;
-       }
-}
-
-void
-GameboyGraphics::blitGreyscale(uint8_t* dest)
+GameboyGraphics::blitGreyscale(uint8_t* dest, const Pixel* screen)
 {
        for (int y = 0; y < 144; ++y)
        {
                uint8_t* row(dest + y*160);
                for (int x = 0; x < 160; ++x)
                {
 {
        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);
+                       int index(screen[y*160+x].colourIndex);
+                       int colour((screen[y*160+x].palette >> (index * 2)) & 0x3);
                        row[x] = 255 - ( colour * 85);
                }
        }
 }
 
                        row[x] = 255 - ( colour * 85);
                }
        }
 }
 
+// Thread-safe method.
+// Call this from the HMI thread, and then display it as
+// 160x144 8bpp greyscale.
 void
 void
-GameboyGraphics:: updateOutputDevice()
+GameboyGraphics::getGreyscaleFrame(uint8_t* frameBuffer)
 {
 {
-       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
-               );
+       Pixel screen[144][160];
+       pthread_mutex_lock(&m_screenShadowMutex);
+       ::memcpy(screen, m_screenShadow, sizeof(m_screenShadow));
+       pthread_mutex_unlock(&m_screenShadowMutex);
 
 
-       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);
-       }
+       blitGreyscale(frameBuffer, &screen[0][0]);
 }
 
 }
 
index 1795a6b200c5dec420fb7a4f76feefbdddb357d3..295c69c3d3fafbb2706698f9d5a89e7226e3669b 100644 (file)
 
 #include "glBoy.hh"
 #include "Core.hh"
 
 #include "glBoy.hh"
 #include "Core.hh"
-
+#include "FPS.hh"
 #include "MemoryMap.hh"
 
 #include "MemoryMap.hh"
 
-#include "hq4x.hh"
-
-#include <SDL/SDL.h>
-#include <SDL/SDL_opengl.h>
-
 #include <vector>
 
 #include <vector>
 
+#include <pthread.h>
+
 namespace glBoy
 {
 
 class GameboyGraphics
 {
 public:
 namespace glBoy
 {
 
 class GameboyGraphics
 {
 public:
-       enum PixelScaler { Scaler_None, Scaler_Scale2x, Scaler_hq4x };
-       enum ResampleMethod { Resample_Nearest, Resample_Bilinear };
-
        GameboyGraphics(
        GameboyGraphics(
-               Core& core,
-               SDL_Surface* surface,
-               PixelScaler scaler,
-               ResampleMethod resampler
+               Core& core
                );
        ~GameboyGraphics();
 
                );
        ~GameboyGraphics();
 
+       void getGreyscaleFrame(uint8_t* frameBuffer);
+
        void startScanLine();
        void renderScanLine();
 
        void startScanLine();
        void renderScanLine();
 
@@ -79,7 +72,6 @@ private:
        enum RenderMode{ RENDER_BLIT, RENDER_SPRITE };
 
        void renderScreen();
        enum RenderMode{ RENDER_BLIT, RENDER_SPRITE };
 
        void renderScreen();
-       void displayStats();
 
        void renderTile(
                uint8_t tile,
 
        void renderTile(
                uint8_t tile,
@@ -110,16 +102,10 @@ private:
        };
        Sprite getSprite(int spriteNum) const;
 
        };
        Sprite getSprite(int spriteNum) const;
 
-       void blitGreyscale(uint8_t* dest);
-       void updateOutputDevice();
+       void blitGreyscale(uint8_t* dest, const Pixel* screen);
 
        Core& m_core;
 
 
        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.
        // 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.
@@ -143,11 +129,9 @@ private:
        Pixel m_background[256][256];
        Pixel m_screen[144][160];
 
        Pixel m_background[256][256];
        Pixel m_screen[144][160];
 
-       // Output device
-       SDL_Surface* m_surface;
-
-       // Shader program
-       GLint m_shaderProgram;
+       // Copy of raster display for sending to the hardware-dependant code.
+       pthread_mutex_t m_screenShadowMutex;
+       Pixel m_screenShadow[144][160];
 
        Core::CallbackId m_mode0;
        Core::CallbackId m_mode2;
 
        Core::CallbackId m_mode0;
        Core::CallbackId m_mode2;
@@ -156,14 +140,9 @@ private:
        int m_lineScrollX[154];
        int m_lineScrollY[154];
 
        int m_lineScrollX[154];
        int m_lineScrollY[154];
 
-
        bool m_frameSkip;
        bool m_frameSkip;
-       bool m_lastSkipped;
 
 
-       timeval m_stat_fpsTimer;
-       int m_stat_frames;
-       int m_stat_skipped;
-       int m_stat_redraw;
+       FPS m_fps;
 };
 
 } // namespace glBoy
 };
 
 } // namespace glBoy
index dd5244a68045a542d178e223e82525bfee08f74f..f1f4b3eb58b3b583bbc71d545bbcc3aea0230280 100644 (file)
@@ -27,77 +27,24 @@ using namespace glBoy;
 
 // TODO implement interrupt! 
 
 
 // TODO implement interrupt! 
 
-namespace
+GameboyJoypad::GameboyJoypad(Core& core) :
+       m_core(core),
+       m_state(Joypad_Off),
+       m_buttonState(0xffff)
 {
 {
-       void setConfiguredKey(
-               const std::map<std::string, int>& keyMap,
-               const libconfig::Config& config,
-               const std::string& configParam,
-               int& mappedKey
-               )
-       {
-               if (config.exists(configParam))
-               {
-                       std::string value;
-                       config.lookupValue(configParam, value);
-                       std::map<std::string, int>::const_iterator it(
-                               keyMap.find(value)
-                               );
-                       if (!value.empty() && it != keyMap.end())
-                       {
-                               mappedKey = it->second;
-                       }
-               }
-       }
 }
 
 }
 
-GameboyJoypad::GameboyJoypad(Core& core, const libconfig::Config& config) :
-       m_state(Joypad_Off)
+uint8_t
+GameboyJoypad::read8(uint16_t)
 {
 {
-       std::map<std::string, int> sdlKeyMap;
-
-       for (int i = SDLK_FIRST; i < SDLK_LAST; ++i)
+       // Thread-safe non-blocking read.
+       SharedState buttonState(m_buttonState);
+       while (
+               !__sync_bool_compare_and_swap(&m_buttonState, buttonState, buttonState))
        {
        {
-               std::string keyName = SDL_GetKeyName(static_cast<SDLKey>(i));
-               if (keyName.find("SDLK_") == 0)
-               {
-                       keyName = keyName.substr(5);
-               }
-               sdlKeyMap[keyName] = i;
+               buttonState = m_buttonState;
        }
 
        }
 
-       m_inputs[DOWN].sdlKey = SDLK_DOWN;
-       //m_inputs[DOWN].sdlJoystick = 0 or none or something;
-       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;
-
-       setConfiguredKey(sdlKeyMap, config, "glBoy.keys.up", m_inputs[UP].sdlKey);
-       setConfiguredKey(sdlKeyMap, config, "glBoy.keys.down", m_inputs[DOWN].sdlKey);
-       setConfiguredKey(sdlKeyMap, config, "glBoy.keys.left", m_inputs[LEFT].sdlKey);
-       setConfiguredKey(sdlKeyMap, config, "glBoy.keys.right", m_inputs[RIGHT].sdlKey);
-       setConfiguredKey(sdlKeyMap, config, "glBoy.keys.start", m_inputs[START].sdlKey);
-       setConfiguredKey(sdlKeyMap, config, "glBoy.keys.select", m_inputs[SELECT].sdlKey);
-       setConfiguredKey(sdlKeyMap, config, "glBoy.keys.a", m_inputs[A].sdlKey);
-       setConfiguredKey(sdlKeyMap, config, "glBoy.keys.b", m_inputs[B].sdlKey);
-
-       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)
        uint8_t result(0xff);
 
        switch (m_state)
@@ -107,11 +54,11 @@ GameboyJoypad::read8(uint16_t)
                break;
 
        case Joypad_Directional:
                break;
 
        case Joypad_Directional:
-               result = m_directionalState;
+               result = buttonState >> 8;
                break;
 
        case Joypad_Button:
                break;
 
        case Joypad_Button:
-               result = m_buttonState;
+               result = buttonState & 0xff;
                break;
        };
        return result;
                break;
        };
        return result;
@@ -142,51 +89,34 @@ GameboyJoypad::write8(uint16_t, uint8_t value)
 }
 
 void
 }
 
 void
-GameboyJoypad::pollInput(Core::CallbackId)
+GameboyJoypad::setInput(
+       bool up,
+       bool down,
+       bool left,
+       bool right,
+       bool start,
+       bool select,
+       bool a,
+       bool b)
 {
 {
-       SDL_Event event;
-
-       while (SDL_PollEvent(&event))
-       {
-               switch(event.type)
-               {
-                       case SDL_KEYUP:
-                       case SDL_KEYDOWN:
-                       {
-                               if (event.key.keysym.sym == SDLK_ESCAPE)
-                               {
-                                       exit(0);
-                               }
-
-                               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;
+       SharedState buttonState(0xffff);
+       if (down) buttonState ^= (0x8 << 8);
+       if (up) buttonState ^= (0x4 << 8);
+       if (left) buttonState ^= (0x2 << 8);
+       if (right) buttonState ^= (0x1 << 8);
+
+       if (start) buttonState ^= 0x8;
+       if (select) buttonState ^= 0x4;
+       if (b) buttonState ^= 0x2;
+       if (a) buttonState ^= 0x1;
+
+       // Thread-safe. Assume the read8 and write8 methods will be called
+       // in a different thread to that handling user input. We need to
+       SharedState old(m_buttonState);
+       bool cmpResult(
+               __sync_bool_compare_and_swap(&m_buttonState, old, buttonState));
+
+       // There should only be one thread writing to m_buttonState
+       assert(cmpResult);
 }
 
 }
 
index 8662875b8a3c17248e806b4523cfda32768f6612..ab9fbb78aa9892c10c1983180629c82c2635b461 100644 (file)
 
 #include "MemoryMap.hh"
 
 
 #include "MemoryMap.hh"
 
-#include <libconfig.h++>
-#include <SDL/SDL.h>
-
 namespace glBoy
 {
 
 class GameboyJoypad : public MemoryMap::Memory
 {
 public:
 namespace glBoy
 {
 
 class GameboyJoypad : public MemoryMap::Memory
 {
 public:
-       GameboyJoypad(Core& core, const libconfig::Config& config);
-
-       void pollInput(Core::CallbackId id);
+       GameboyJoypad(Core& core);
+
+       // Thread safe
+       void setInput(
+               bool up,
+               bool down,
+               bool left,
+               bool right,
+               bool start,
+               bool select,
+               bool a,
+               bool b);
 
        virtual uint8_t read8(uint16_t address);
        virtual void write8(uint16_t address, uint8_t value);
 
 private:
 
        virtual uint8_t read8(uint16_t address);
        virtual void write8(uint16_t address, uint8_t value);
 
 private:
+       Core& m_core;
+
        enum State { Joypad_Off, Joypad_Directional, Joypad_Button };
        State m_state;
 
        enum State { Joypad_Off, Joypad_Directional, Joypad_Button };
        State m_state;
 
-       struct Input
-       {
-               Input() : sdlKey(0), depressed(false) {}
-               int sdlKey;
-               int sdlJoystick;
-               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;
+       // Data is (possible) used in both user input and emulation threads.
+       typedef unsigned int SharedState;
+       SharedState m_buttonState;
 };
 
 } // namespace glBoy
 };
 
 } // namespace glBoy
index 09c0734db8997ac5c3b282c539c7eb4591d4b319..694b42bf3ae1a78acdd1ff424a29f3a7d4e1ebb3 100644 (file)
 
 using namespace glBoy;
 
 
 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)
 {
 GameboySound::GameboySound(Core& core)
 {
-       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
        using namespace std::placeholders;
        core.registerPeriodic(
                32768, // 128 Hz
@@ -235,41 +209,48 @@ void GameboySound::periodicUpdate(Core::CallbackId)
 }
 
 void
 }
 
 void
-GameboySound::mixSounds(Uint8 *stream, int len)
+GameboySound::mixSounds(int16_t *pcmStream, size_t samples)
 {
 {
-       for (int i = 0; i < len / 2; ++i)
+       enum { MaxChunkSize = 512 };
+       int32_t pcmBuffer[MaxChunkSize];
+       std::fill(pcmBuffer, pcmBuffer + MaxChunkSize, 0);
+       while (samples > 0)
        {
        {
-               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;
-       }
+               size_t chunk = std::min(samples, size_t(MaxChunkSize));
+
+               mixSamples(m_channel1, pcmBuffer, chunk);
+               mixSamples(m_channel2, pcmBuffer, chunk);
+               mixSamplesChannel3(pcmBuffer, chunk);
+               mixSamplesChannel4(pcmBuffer, chunk);
 
 
+               for (size_t i = 0; i < chunk; ++i)
+               {
+                       pcmStream[i] = (pcmBuffer[i] / 4); // Can't overflow.
+               }
+               samples -= chunk;
+               pcmStream += chunk;
+       }
 }
 
 }
 
-int16_t
-GameboySound::sample(SquareWave& channel)
+void
+GameboySound::mixSamples(
+       SquareWave& channel, int32_t* pcmBuffer, size_t samples)
 {
 {
-       int16_t sample(0);
+       //int32_t sampleVolume(channel.currentVolume * channel.currentVolume);
+       //sampleVolume = sampleVolume * sampleVolume;
+       int32_t sampleVolume(channel.currentVolume * 2048);
 
 
-       if (channel.currentVolume && channel.remainingLength > 0)
+       size_t i;
+       for (i = 0; (i < samples) && (channel.remainingLength > 0); ++i)
        {
                int pos = channel.squareStep / (channel.samplesPerDutyBit);
 
        {
                int pos = channel.squareStep / (channel.samplesPerDutyBit);
 
-               sample = channel.currentVolume * channel.currentVolume;
-               sample = sample * sample;
+               int32_t sample(sampleVolume);
                if (!((channel.dutyMask >> (7 - pos)) & 1))
                {
                        sample = -sample;
                }
                if (!((channel.dutyMask >> (7 - pos)) & 1))
                {
                        sample = -sample;
                }
+               pcmBuffer[i] += sample;
 
                ++channel.squareStep;
                if (channel.squareStep > (channel.samplesPerDutyBit * 8))
 
                ++channel.squareStep;
                if (channel.squareStep > (channel.samplesPerDutyBit * 8))
@@ -277,7 +258,7 @@ GameboySound::sample(SquareWave& channel)
                        channel.squareStep = 0;
                }
        }
                        channel.squareStep = 0;
                }
        }
-       return sample;
+       std::fill(pcmBuffer + i, pcmBuffer + samples, 0);
 }
 
 void
 }
 
 void
@@ -350,27 +331,31 @@ GameboySound::prepChannel(SquareWave& channel)
        channel.samplesPerDutyBit = (SAMPLE_RATE/freq) / 8;
 }
 
        channel.samplesPerDutyBit = (SAMPLE_RATE/freq) / 8;
 }
 
-int16_t
-GameboySound::sampleChannel4()
+void
+GameboySound::mixSamplesChannel4(int32_t* pcmBuffer, size_t samples)
 {
 {
-       int16_t sample(0);
+       //int32_t sampleVolume(m_channel4.currentVolume * m_channel4.currentVolume);
+       //sampleVolume = sampleVolume * sampleVolume;
+       int32_t sampleVolume(m_channel4.currentVolume * 2048);
 
 
-       if (m_channel4.currentVolume && m_channel4.remainingLength > 0)
+       size_t i;
+       for (i = 0; (i < samples) && (m_channel4.remainingLength > 0); ++i)
        {
        {
-               sample = m_channel4.currentVolume * m_channel4.currentVolume;
-               sample = sample * sample;
+               int32_t sample(sampleVolume);
                if (!m_channel4.high)
                {
                        sample = -sample;
                }
                if (!m_channel4.high)
                {
                        sample = -sample;
                }
+               pcmBuffer[i] += sample;
 
                ++m_channel4.currentCounter;
                if (m_channel4.currentCounter > m_channel4.samplesPerStep)
                {
                        m_channel4.high = rand() & 0x1;
 
                ++m_channel4.currentCounter;
                if (m_channel4.currentCounter > m_channel4.samplesPerStep)
                {
                        m_channel4.high = rand() & 0x1;
+                       m_channel4.currentCounter = 0;
                }
        }
                }
        }
-       return sample;
+       std::fill(pcmBuffer + i, pcmBuffer + samples, 0);
 }
 
 
 }
 
 
@@ -437,17 +422,18 @@ GameboySound::prepChannel3()
        m_channel3.samplesPerNibble = (SAMPLE_RATE/freq) / 32;
 }
 
        m_channel3.samplesPerNibble = (SAMPLE_RATE/freq) / 32;
 }
 
-int16_t
-GameboySound::sampleChannel3()
+void
+GameboySound::mixSamplesChannel3(int32_t* pcmBuffer, size_t samples)
 {
 {
-       int16_t sample(0);
-
-       if      (m_channel3.remainingLength > 0 &&
-               (m_channel3.enabled & 0x80)
-               )
+       int volume((m_channel3.volume >> 5) & 0x3);
+
+       size_t i;
+       for (i = 0;
+               (i < samples) &&
+                       (m_channel3.remainingLength) > 0 &&
+                       (m_channel3.enabled & 0x80);
+               ++i)
        {
        {
-               int volume((m_channel3.volume >> 5) & 0x3);
-
                uint8_t nibble = m_channel3.waveTable[m_channel3.wavePos / 2];
 
                if (m_channel3.wavePos & 0x1)
                uint8_t nibble = m_channel3.waveTable[m_channel3.wavePos / 2];
 
                if (m_channel3.wavePos & 0x1)
@@ -469,11 +455,11 @@ GameboySound::sampleChannel3()
                }
                else if (volume == 3)
                {
                }
                else if (volume == 3)
                {
-                       nibble >>= 3;
+                       nibble >>= 2;
                }
 
                // Ok, nibble is now between 0 and 0xf. Map this to full range.
                }
 
                // Ok, nibble is now between 0 and 0xf. Map this to full range.
-               sample = (static_cast<int16_t>(nibble) - 8) * 4096;
+               pcmBuffer[i] += (static_cast<int32_t>(nibble) - 8) * 4096;
 
                ++m_channel3.waveSamples;
                if (m_channel3.waveSamples > m_channel3.samplesPerNibble * (m_channel3.wavePos + 1))
 
                ++m_channel3.waveSamples;
                if (m_channel3.waveSamples > m_channel3.samplesPerNibble * (m_channel3.wavePos + 1))
@@ -487,5 +473,6 @@ GameboySound::sampleChannel3()
                }
 
        }
                }
 
        }
-       return sample;
+       std::fill(pcmBuffer + i, pcmBuffer + samples, 0);
 }
 }
+
index 8a01d39fdd737c4656a32e23041c56c0f7b2320d..039898bdee408469437b61ce2aa225e900b85c74 100644 (file)
 
 #include "MemoryMap.hh"
 
 
 #include "MemoryMap.hh"
 
-#include <SDL/SDL.h>
-
 namespace glBoy
 {
 
 class GameboySound : public MemoryMap::Memory
 {
 public:
 namespace glBoy
 {
 
 class GameboySound : public MemoryMap::Memory
 {
 public:
+       enum Constants { SAMPLE_RATE = 48000 };
+
        GameboySound(Core& core);
 
        void periodicUpdate(Core::CallbackId id);
        GameboySound(Core& core);
 
        void periodicUpdate(Core::CallbackId id);
@@ -38,10 +38,9 @@ public:
        virtual uint8_t read8(uint16_t address);
        virtual void write8(uint16_t address, uint8_t value);
 
        virtual uint8_t read8(uint16_t address);
        virtual void write8(uint16_t address, uint8_t value);
 
-       void mixSounds(Uint8* stream, int len);
+       // TODO: Thread safe
+       void mixSounds(int16_t* pcmStream, size_t samples);
 private:
 private:
-       int16_t getSample(uint8_t volume);
-
        struct SquareWave
        {
                SquareWave() : sweep(0), duty(0), volume(0), freqLow(0), freqHigh(0)
        struct SquareWave
        {
                SquareWave() : sweep(0), duty(0), volume(0), freqLow(0), freqHigh(0)
@@ -97,7 +96,7 @@ private:
 
                int remainingLength;
        };
 
                int remainingLength;
        };
-       int16_t sample(SquareWave& channel);
+       void mixSamples(SquareWave& channel, int32_t* pcmBuffer, size_t samples);
        void prepChannel(SquareWave& channel);
 
        SquareWave m_channel1;
        void prepChannel(SquareWave& channel);
 
        SquareWave m_channel1;
@@ -134,7 +133,7 @@ private:
        };
 
        Noise m_channel4;
        };
 
        Noise m_channel4;
-       int16_t sampleChannel4();
+       void mixSamplesChannel4(int32_t* pcmBuffer, size_t samples);
        void prepChannel4();
 
        struct Wave
        void prepChannel4();
 
        struct Wave
@@ -178,7 +177,7 @@ private:
        };
 
        Wave m_channel3;
        };
 
        Wave m_channel3;
-       int16_t sampleChannel3();
+       void mixSamplesChannel3(int32_t* pcmBuffer, size_t samples);
        void prepChannel3();
 
        uint8_t m_channelControl;
        void prepChannel3();
 
        uint8_t m_channelControl;
index 889eb2a25529217a860235b91ce0d133ea6185dc..5bd7b0ba79e1b408e95cbf603c8f0582dfe842bc 100644 (file)
 #include <algorithm>
 #include <cassert>
 
 #include <algorithm>
 #include <cassert>
 
+#include <string.h>
 #include <time.h>
 
 using namespace glBoy;
 
 #include <time.h>
 
 using namespace glBoy;
 
+enum
+{
+       GB_HZ = 4194304, // DMG freq ~ 4MHz
+       IF_HZ = 16384, // Intermediate Frequency. Must be a power-of-2
+       IF_PERIOD_NS = 61035, // (1.0/IF_HZ * 1e9)
+       MIN_SLEEP_NS = 500000
+};
+
 GameboyTimer::GameboyTimer(
        Core& core,
        const std::function<void(void)>& setFrameSkip,
 GameboyTimer::GameboyTimer(
        Core& core,
        const std::function<void(void)>& setFrameSkip,
@@ -33,13 +42,14 @@ GameboyTimer::GameboyTimer(
        ) :
        m_core(core),
        m_setSkipFrame(setFrameSkip),
        ) :
        m_core(core),
        m_setSkipFrame(setFrameSkip),
-       m_clearSkipFrame(clearFrameSkip)
+       m_clearSkipFrame(clearFrameSkip),
+       m_tick(0)
 {
        memset(m_reg, 0, 4);
 
        using namespace std::placeholders;
        core.registerPeriodic(
 {
        memset(m_reg, 0, 4);
 
        using namespace std::placeholders;
        core.registerPeriodic(
-               256,
+               GB_HZ / IF_HZ,
                0,
                std::bind(&glBoy::GameboyTimer::tick, this, _1)
                );
                0,
                std::bind(&glBoy::GameboyTimer::tick, this, _1)
                );
@@ -47,31 +57,45 @@ GameboyTimer::GameboyTimer(
        timespec res;
        clock_getres(CLOCK_REALTIME, &res);
 
        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;
+       long clockSleepNs = std::max(res.tv_nsec, long(MIN_SLEEP_NS));
+       m_clockSleepDelay = clockSleepNs / IF_PERIOD_NS;
        m_clockSleepCounter = m_clockSleepDelay;
        m_clockSleepCounter = m_clockSleepDelay;
+       m_frameSkipAllowanceNs = std::max(long(1e9/60), 10 * clockSleepNs);
 
 
-/*
-       if (m_clockSleepDelay)
+       if (clockSleepNs > MIN_SLEEP_NS)
        {
        {
-               Log::Instance()(Log::DBG_WARNING) <<
-                       "Timer delay set to " << m_clockSleepDelay <<
-                       " nanoseconds. Enable HR timers for improved emulation" << std::endl;
+               Warn() <<
+                       "Timer delay set to " << clockSleepNs <<
+                       " nanoseconds. Enable HR timers for improved emulation" <<
+                       Log::endl;
        }
        }
-*/
 
        clock_gettime(CLOCK_REALTIME, &m_lastSlept);
 }
 
 
        clock_gettime(CLOCK_REALTIME, &m_lastSlept);
 }
 
-// Call this every 256 clock cycles. (16384Hz)
+template<class T>
+static inline void
+increment(unsigned int freq, unsigned int tick, T& counter)
+{
+       // freq and IF_HZ MUST be a power-of-two.
+       if (freq < IF_HZ)
+       {
+               if (tick % (IF_HZ / freq) == 0) ++counter;
+       }
+       else
+       {
+               counter += freq / IF_HZ;
+       }
+}
 
 void
 GameboyTimer::tick(Core::CallbackId)
 {
 
 void
 GameboyTimer::tick(Core::CallbackId)
 {
-       ++m_reg[DIV];
+       // Note: This will overrun! code should not depend on differences between
+       // any successive values.
+       ++m_tick;
+
+       increment(16384, m_tick, m_reg[DIV]);
 
        if (m_reg[TAC] & 0x4) // Timer enabled
        {
 
        if (m_reg[TAC] & 0x4) // Timer enabled
        {
@@ -79,16 +103,16 @@ GameboyTimer::tick(Core::CallbackId)
                switch (m_reg[TAC] & 0x3)
                {
                case 0: // 4096Hz
                switch (m_reg[TAC] & 0x3)
                {
                case 0: // 4096Hz
-                       if (m_reg[DIV] % 4 == 0) ++counter;
+                       increment(4096, m_tick, counter);
                        break;
                        break;
-               case 1: // 268400Hz
-                       counter += 16;
+               case 1: // 262144
+                       increment(262144, m_tick, counter);
                        break;
                case 2: // 65536Hz
                        break;
                case 2: // 65536Hz
-                       counter += 4;
+                       increment(65536, m_tick, counter);
                        break;
                case 3: // 16384Hz
                        break;
                case 3: // 16384Hz
-                       ++counter;
+                       increment(16384, m_tick, counter);
                        break;
                }
 
                        break;
                }
 
@@ -103,39 +127,44 @@ GameboyTimer::tick(Core::CallbackId)
                }
        }
 
                }
        }
 
-       m_lastSlept.tv_nsec += 61035;
+       m_lastSlept.tv_nsec += (1.0/IF_HZ) * 1e9;
        if (m_lastSlept.tv_nsec > 1e9)
        {
                m_lastSlept.tv_nsec -= 1e9;
                m_lastSlept.tv_sec++;
        }
 
        if (m_lastSlept.tv_nsec > 1e9)
        {
                m_lastSlept.tv_nsec -= 1e9;
                m_lastSlept.tv_sec++;
        }
 
+       --m_clockSleepCounter;
        if (m_clockSleepCounter == 0)
        {
                timespec now;
                clock_gettime(CLOCK_REALTIME, &now);
        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)
-                               )
-                       )
+
+               int64_t nowNs(now.tv_sec * uint64_t(1e9) + now.tv_nsec);
+               int64_t lastNs(m_lastSlept.tv_sec * uint64_t(1e9) + m_lastSlept.tv_nsec);
+
+               if (lastNs > nowNs)
                {
                        m_clearSkipFrame();
                        clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &m_lastSlept, NULL);
                }
                {
                        m_clearSkipFrame();
                        clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &m_lastSlept, NULL);
                }
-               else
+               else if (nowNs - lastNs > (1e9 / 4))
+               {
+                       // We're significantly behind. At this point, the user will
+                       // experience a glitch no matter what we do.
+                       // "Pretend" we've caught up, and hopefully it will be good from
+                       // this point forward.
+                       m_clearSkipFrame();
+                       m_lastSlept = now;
+               }
+               else if (nowNs - lastNs > m_frameSkipAllowanceNs)
                {
                {
+                       // If we start dragging the chain by a user-visible amount,
+                       // then start frame skipping.
                        m_setSkipFrame();
                }
                m_clockSleepCounter = m_clockSleepDelay;
        }
                        m_setSkipFrame();
                }
                m_clockSleepCounter = m_clockSleepDelay;
        }
-       else
-       {
-               m_clockSleepCounter--;
-       }
-
 }
 
 uint8_t
 }
 
 uint8_t
index 2d5df43ebfe72c276d3e2cc259352ea40a8d21b1..4bcf0798b7f82834f0b723840236b6aaeb33a118 100644 (file)
@@ -25,8 +25,6 @@
 
 #include <time.h>
 
 
 #include <time.h>
 
-#include <SDL/SDL.h>
-
 namespace glBoy
 {
 
 namespace glBoy
 {
 
@@ -57,6 +55,9 @@ private:
 
        int m_clockSleepDelay;
        int m_clockSleepCounter;
 
        int m_clockSleepDelay;
        int m_clockSleepCounter;
+       long m_frameSkipAllowanceNs;
+
+       unsigned int m_tick;
 };
 
 } // namespace glBoy
 };
 
 } // namespace glBoy
index ca022d8e6d82b01f9e894e3eb44b8c2b8bb8ec28..bb4078548fb3397a67ab8b8056b44b387037befc 100644 (file)
@@ -47,7 +47,6 @@
        <reg name="BC" mask="00" />\r
        <reg name="DE" mask="01" />\r
        <reg name="HL" mask="10" />\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" maskBits="2" regBytes="2" variant="z80">\r
 </registerMask>\r
 \r
 <registerMask name="p" maskBits="2" regBytes="2" variant="z80">\r
        m_mem.write16(m_reg.SP, q);\r
 </instruction>\r
 \r
        m_mem.write16(m_reg.SP, q);\r
 </instruction>\r
 \r
+<instruction name="push AF" clock="11" variant="gb80">\r
+<opcode mask="11110101"/>\r
+       m_reg.SP.dec(2);\r
+       m_reg.F.byte &amp;= 0xF0;\r
+       m_mem.write16(m_reg.SP, m_reg.AF);\r
+</instruction>\r
+\r
+<instruction name="push AF" clock="11" variant="z80">\r
+<opcode mask="11110101"/>\r
+       m_reg.SP.dec(2);\r
+       m_mem.write16(m_reg.SP, m_reg.AF);\r
+</instruction>\r
+\r
 <instruction name="push IX" clock="15">\r
 <opcode prefix="0xDD" mask="11100101" />\r
        m_reg.SP.dec(2);\r
 <instruction name="push IX" clock="15">\r
 <opcode prefix="0xDD" mask="11100101" />\r
        m_reg.SP.dec(2);\r
        m_reg.SP.inc(2);\r
 </instruction>\r
 \r
        m_reg.SP.inc(2);\r
 </instruction>\r
 \r
+<instruction name="pop AF" clock="10" variant="gb80">\r
+<opcode mask="11110001" />\r
+       m_reg.AF = m_mem.read16(m_reg.SP);\r
+       m_reg.F.byte &amp;= 0xF0;\r
+       m_reg.SP.inc(2);\r
+</instruction>\r
+\r
+<instruction name="pop AF" clock="10" variant="z80">\r
+<opcode mask="11110001" />\r
+       m_reg.AF = 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
 <instruction name="pop IX" clock="14">\r
 <opcode prefix="0xDD" mask="11100001" />\r
        m_reg.IX = m_mem.read16(m_reg.SP);\r
 \r
 <instruction name="INC r" clock="4">\r
 <opcode mask="00r100" />\r
 \r
 <instruction name="INC r" clock="4">\r
 <opcode mask="00r100" />\r
+       bit oldC(m_reg.F.C);\r
        r = ALU::add(r, 1, m_reg.F);\r
        r = ALU::add(r, 1, m_reg.F);\r
+       m_reg.F.C = oldC;\r
 </instruction>\r
 \r
 <instruction name="INC (HL)" clock="11">\r
 <opcode mask="00110100" />\r
 </instruction>\r
 \r
 <instruction name="INC (HL)" clock="11">\r
 <opcode mask="00110100" />\r
+       bit oldC(m_reg.F.C);\r
        m_mem.write8(m_reg.HL, ALU::add(m_mem.read8(m_reg.HL), 1, m_reg.F));\r
        m_mem.write8(m_reg.HL, ALU::add(m_mem.read8(m_reg.HL), 1, m_reg.F));\r
+       m_reg.F.C = oldC;\r
 </instruction>\r
 \r
 <instruction name="INC (IX+d)" clock="23">\r
 </instruction>\r
 \r
 <instruction name="INC (IX+d)" clock="23">\r
 \r
 <instruction name="DEC r" clock="4">\r
 <opcode mask="00r101" />\r
 \r
 <instruction name="DEC r" clock="4">\r
 <opcode mask="00r101" />\r
+       bit oldC(m_reg.F.C);\r
        r = ALU::sub(r, 1, m_reg.F);\r
        r = ALU::sub(r, 1, m_reg.F);\r
+       m_reg.F.C = oldC;\r
 </instruction>\r
 \r
 <instruction name="DEC (HL)" clock="11">\r
 <opcode mask="00110101" />\r
 </instruction>\r
 \r
 <instruction name="DEC (HL)" clock="11">\r
 <opcode mask="00110101" />\r
+       bit oldC(m_reg.F.C);\r
        m_mem.write8(m_reg.HL, ALU::sub(m_mem.read8(m_reg.HL), 1, m_reg.F));\r
        m_mem.write8(m_reg.HL, ALU::sub(m_mem.read8(m_reg.HL), 1, m_reg.F));\r
+       m_reg.F.C = oldC;\r
 </instruction>\r
 \r
 <instruction name="DEC (IX+d)" clock="23">\r
 </instruction>\r
 \r
 <instruction name="DEC (IX+d)" clock="23">\r
 <instruction name="CCF" clock="4">\r
 <opcode mask="00111111"/>\r
        m_reg.F.C = (m_reg.F.C) ? 0 : 1;\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.H = 0;\r
        m_reg.F.N = 0;\r
 </instruction>\r
 \r
        m_reg.F.N = 0;\r
 </instruction>\r
 \r
 \r
 <instruction name="DI" clock="4">\r
 <opcode mask="11110011"/>\r
 \r
 <instruction name="DI" clock="4">\r
 <opcode mask="11110011"/>\r
-       m_iff1 = 0;\r
-       m_iff2 = 0;\r
+       m_ime = 0;\r
 </instruction>\r
 \r
 <instruction name="EI" clock="4">\r
 <opcode mask="11111011"/>\r
 </instruction>\r
 \r
 <instruction name="EI" clock="4">\r
 <opcode mask="11111011"/>\r
-       m_iff1 = 1;\r
-       m_iff2 = 1;\r
+       m_ime = 1;\r
 </instruction>\r
 \r
 <instruction name="IM 0" clock="8">\r
 </instruction>\r
 \r
 <instruction name="IM 0" clock="8">\r
 <opcode prefix="0xED" mask="01001101"/>\r
        m_reg.PC = m_mem.read16(m_reg.SP);\r
        m_reg.SP.inc(2);\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
+       m_ime = 1;\r
 </instruction>\r
 \r
 <instruction name="RETN" clock="14">\r
 </instruction>\r
 \r
 <instruction name="RETN" clock="14">\r
        // TODO interruptComplete();\r
 </instruction>\r
 \r
        // TODO interruptComplete();\r
 </instruction>\r
 \r
-<instruction name="RST p" clock="11">\r
+<instruction name="RST b" clock="11">\r
 <opcode mask="11b111"/>\r
        uint8_t addr;\r
        switch (b)\r
 <opcode mask="11b111"/>\r
        uint8_t addr;\r
        switch (b)\r
@@ -1450,8 +1481,7 @@ TODO NOT IMPLEMENTED
 <opcode mask="11011001" />\r
        m_reg.PC = m_mem.read16(m_reg.SP);\r
        m_reg.SP.inc(2);\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
+       m_ime = 1;\r
 </instruction>\r
 \r
 <instruction name="STOP" clock="4" variant="gb80">\r
 </instruction>\r
 \r
 <instruction name="STOP" clock="4" variant="gb80">\r
@@ -1462,7 +1492,7 @@ TODO NOT IMPLEMENTED
 \r
 <instruction name="ADD SP,d" clock="4" variant="gb80">\r
 <opcode mask="11101000" signed_operand="d"/>\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
+       m_reg.SP = ALU::add(m_reg.SP, d, m_reg.F);\r
 </instruction>\r
 \r
 <instruction name="LD (N),A" clock="4" variant="gb80">\r
 </instruction>\r
 \r
 <instruction name="LD (N),A" clock="4" variant="gb80">\r
@@ -1471,9 +1501,9 @@ TODO NOT IMPLEMENTED
 </instruction>\r
 \r
 \r
 </instruction>\r
 \r
 \r
-<instruction name="LDHL SP,d" clock="4" variant="gb80">\r
+<instruction name="LDHL SP,d" clock="12" variant="gb80">\r
 <opcode mask="11111000" signed_operand="d"/>\r
 <opcode mask="11111000" signed_operand="d"/>\r
-       m_reg.HL = ALU::add(m_reg.SP, htoz(int16_t(d)), m_reg.F);\r
+       m_reg.HL = ALU::add(m_reg.SP, d, m_reg.F);\r
 </instruction>\r
 \r
 <instruction name="LD A,(N)" clock="4" variant="gb80">\r
 </instruction>\r
 \r
 <instruction name="LD A,(N)" clock="4" variant="gb80">\r
@@ -1626,19 +1656,24 @@ TODO NOT IMPLEMENTED
        m_reg.F.C = 0;\r
 </instruction>\r
 \r
        m_reg.F.C = 0;\r
 </instruction>\r
 \r
+\r
 <!-- Zero-page gameboy instructions -->\r
 <!-- Zero-page gameboy instructions -->\r
-<instruction name="LD (FF00+n),A" clock="4" variant="gb80">\r
+<instruction name="LDH (FF00+n),A" clock="12" variant="gb80">\r
 <opcode mask="11100000" operand="n"/>\r
        m_mem.write8(htoz(0xFF00 + n), m_reg.A);\r
 </instruction>\r
 \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
+<instruction name="LD (FF00+C),A" clock="8" variant="gb80">\r
 <opcode mask="11100010"/>\r
        m_mem.write8(htoz(0xFF00 + m_reg.C), m_reg.A);\r
 </instruction>\r
 \r
 <opcode mask="11100010"/>\r
        m_mem.write8(htoz(0xFF00 + m_reg.C), m_reg.A);\r
 </instruction>\r
 \r
+<instruction name="LD A,(FF00+C)" clock="8" variant="gb80">\r
+<opcode mask="11110010"/>\r
+       m_reg.A = m_mem.read8(htoz(0xFF00 + m_reg.C));\r
+</instruction>\r
 \r
 \r
-<instruction name="LD A,(FF00+d)" clock="4" variant="gb80">\r
+<instruction name="LDH A,(FF00+d)" clock="12" variant="gb80">\r
 <opcode mask="11110000" operand="n"/>\r
        m_reg.A = m_mem.read8(htoz(0xFF00 + n));\r
 </instruction>\r
 <opcode mask="11110000" operand="n"/>\r
        m_reg.A = m_mem.read8(htoz(0xFF00 + n));\r
 </instruction>\r
@@ -1673,9 +1708,6 @@ TODO NOT IMPLEMENTED
 <opcode mask="11101100"/>\r
 </instruction>\r
 \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
 \r
 <instruction name="NOP_F4" clock="4" variant="gb80">\r
 <opcode mask="11110100"/>\r
diff --git a/Log.cc b/Log.cc
index 62dc21d47159f700b2b5be79c80e5cd812a83c4f..55c69168da37089e200f3bb9f4aaffae89f1fad9 100644 (file)
--- a/Log.cc
+++ b/Log.cc
@@ -60,6 +60,10 @@ Log& glBoy::TraceMem()
        return GetLog(Log::DBG_TRACE_MEM);
 }
 
        return GetLog(Log::DBG_TRACE_MEM);
 }
 
+Log& glBoy::TraceCPU()
+{
+       return GetLog(Log::DBG_TRACE_CPU);
+}
 
 Log&
 Log::Instance()
 
 Log&
 Log::Instance()
@@ -70,7 +74,8 @@ Log::Instance()
 
 Log::Log() :
        m_levelMask(15),
 
 Log::Log() :
        m_levelMask(15),
-       m_currentLevel(DBG_NONE)
+       m_currentLevel(DBG_NONE),
+       m_nullStream(&m_nullBuf)
 {
        char* env(getenv("GLBOY_DEBUG_LEVEL"));
        if (env)
 {
        char* env(getenv("GLBOY_DEBUG_LEVEL"));
        if (env)
diff --git a/Log.hh b/Log.hh
index 8aaf4192e1f638448e4831cc00557b4c27cd02b1..68fa1ccff2b174ab99e91c959c6b387bc1a07cca 100644 (file)
--- a/Log.hh
+++ b/Log.hh
@@ -34,6 +34,7 @@ namespace glBoy
        Log& Info();
        Log& Stat();
        Log& TraceMem();
        Log& Info();
        Log& Stat();
        Log& TraceMem();
+       Log& TraceCPU();
 
        class Log
        {
 
        class Log
        {
@@ -52,6 +53,7 @@ namespace glBoy
                        DBG_INFO = 4,
                        DBG_STAT = 8,
                        DBG_TRACE_MEM = 16,
                        DBG_INFO = 4,
                        DBG_STAT = 8,
                        DBG_TRACE_MEM = 16,
+                       DBG_TRACE_CPU = 32,
 
                        DBG_NONE = 0xFFFFFFFF
                };
 
                        DBG_NONE = 0xFFFFFFFF
                };
@@ -95,6 +97,7 @@ namespace glBoy
                void setDebugLevel(uint32_t levelMask);
                bool isDebugLevelOn(DebugLevel level) const;
 
                void setDebugLevel(uint32_t levelMask);
                bool isDebugLevelOn(DebugLevel level) const;
 
+               std::ostream& nullStream() { return m_nullStream; }
        private:
                friend Log& GetLog(int);
 
        private:
                friend Log& GetLog(int);
 
@@ -108,6 +111,14 @@ namespace glBoy
                DebugLevel m_currentLevel;
 
                std::stringstream m_collector;
                DebugLevel m_currentLevel;
 
                std::stringstream m_collector;
+
+               class NullBuf : public std::streambuf
+               {
+                       public:
+               };
+
+               NullBuf m_nullBuf;
+               std::ostream m_nullStream;
        };
 
 } // namespace glBoy
        };
 
 } // namespace glBoy
diff --git a/MBC1.cc b/MBC1.cc
index 31defe32e72b9f27a4938a0c9ca4634d45f8c47e..ded039c846b065bdc55b0c8a51c813064a232bdd 100644 (file)
--- a/MBC1.cc
+++ b/MBC1.cc
 
 #include "glBoy.hh"
 #include "MBC1.hh"
 
 #include "glBoy.hh"
 #include "MBC1.hh"
+#include "GameboyCart.hh"
 #include "Log.hh"
 
 #include "Log.hh"
 
-#include <cassert>
+#include <sstream>
 
 using namespace glBoy;
 using namespace std;
 
 using namespace glBoy;
 using namespace std;
@@ -29,15 +30,51 @@ MBC1::MBC1(
        ) :
        m_parent(parent),
        m_rom(begin, end),
        ) :
        m_parent(parent),
        m_rom(begin, end),
+       m_bankSize((end - begin) / 0x4000),
        m_mode(ROM_SELECT),
        m_romLow(1),
        m_romHigh(0),
        m_mode(ROM_SELECT),
        m_romLow(1),
        m_romHigh(0),
-       m_romBank(0)
+       m_romBank(1),
+       m_ramEnabled(false)
 {
        if (end - begin < 0x8000)
        {
 {
        if (end - begin < 0x8000)
        {
-               Err() << "MBC1 ROM too small (" << end - begin << " bytes)" << Log::endl;
-               abort();
+               std::stringstream error;
+               error << "MBC1 ROM too small (" << end - begin << " bytes)";
+               throw GameboyCart::Invalid(error.str());
+       }
+       else if ((end - begin) % 0x4000)
+       {
+               std::stringstream error;
+               error << "MBC1 ROM invalid size (" << end - begin << " bytes)";
+               throw GameboyCart::Invalid(error.str());
+
+       }
+}
+
+uint16_t
+MBC1::getBankIndex(uint16_t address) const
+{
+       if (address < 0x4000)
+       {
+               return 0;
+       }
+       else
+       {
+               return m_romBank;
+       }
+}
+
+uint16_t
+MBC1::getBankSize(uint16_t address) const
+{
+       if (address < 0x4000)
+       {
+               return 0;
+       }
+       else
+       {
+               return m_bankSize;
        }
 }
 
        }
 }
 
@@ -55,7 +92,7 @@ MBC1::read8(uint16_t address)
        else
        {
                TraceMem() << "Attempt to read unmapped MBC1 rom data." << Log::endl;
        else
        {
                TraceMem() << "Attempt to read unmapped MBC1 rom data." << Log::endl;
-               abort();
+               return 0;
        }
 }
 
        }
 }
 
@@ -64,7 +101,18 @@ MBC1::write8(uint16_t address, uint8_t value)
 {
        if (address < 0x1FFF)
        {
 {
        if (address < 0x1FFF)
        {
-               // TODO enable/disable ram
+               if ((value & 0xf) == 0xa) // enable
+               {
+                       m_ramEnabled = true;
+               }
+               else
+               {
+                       if (m_ramEnabled)
+                       {
+                               m_parent->saveExtRam();
+                       }
+                       m_ramEnabled = false;
+               }
        }
        else if (address < 0x4000)
        {
        }
        else if (address < 0x4000)
        {
@@ -76,7 +124,6 @@ MBC1::write8(uint16_t address, uint8_t value)
                        m_romBank |= (m_romHigh << 5);
                }
 
                        m_romBank |= (m_romHigh << 5);
                }
 
-std::cerr << "MBC1 ROM LOW " << (int)m_romBank << std::endl;
        }
        else if (address < 0x6000)
        {
        }
        else if (address < 0x6000)
        {
@@ -88,7 +135,6 @@ std::cerr << "MBC1 ROM LOW " << (int)m_romBank << std::endl;
                else
                {
                        m_romBank = m_romLow | (m_romHigh << 5);
                else
                {
                        m_romBank = m_romLow | (m_romHigh << 5);
-std::cerr << "MBC1 ROM HIGH " << (int)m_romBank << std::endl;
                }
        }
        else
                }
        }
        else
@@ -100,14 +146,12 @@ std::cerr << "MBC1 ROM HIGH " << (int)m_romBank << std::endl;
 
                        m_parent->setRamBank(m_romHigh);
                        m_romBank = m_romLow;
 
                        m_parent->setRamBank(m_romHigh);
                        m_romBank = m_romLow;
-std::cerr << "MBC1 RAM SELECT " << (int)m_romBank << std::endl;
                }
                else
                {
                        m_mode = ROM_SELECT;
                        m_parent->setRamBank(0);
                        m_romBank = m_romLow | (m_romHigh << 5);
                }
                else
                {
                        m_mode = ROM_SELECT;
                        m_parent->setRamBank(0);
                        m_romBank = m_romLow | (m_romHigh << 5);
-std::cerr << "MBC1 ROM SELECT " << (int)m_romBank << std::endl;
                }
        }
 }
                }
        }
 }
diff --git a/MBC1.hh b/MBC1.hh
index a2b61af704488998bed33091d3e5875e37009b92..67394ba68793e10f28026391c034b464a10449aa 100644 (file)
--- a/MBC1.hh
+++ b/MBC1.hh
@@ -33,6 +33,9 @@ namespace glBoy
                        const uint8_t* begin,
                        const uint8_t* end);
 
                        const uint8_t* begin,
                        const uint8_t* end);
 
+               virtual uint16_t getBankIndex(uint16_t address) const;
+               virtual uint16_t getBankSize(uint16_t address) const;
+
                virtual uint8_t read8(uint16_t address);
                virtual void write8(uint16_t address, uint8_t value);
 
                virtual uint8_t read8(uint16_t address);
                virtual void write8(uint16_t address, uint8_t value);
 
@@ -40,6 +43,7 @@ namespace glBoy
                GameboyCart* m_parent;
 
                std::vector<uint16_t> m_rom;
                GameboyCart* m_parent;
 
                std::vector<uint16_t> m_rom;
+               size_t m_bankSize;
 
                enum Mode { ROM_SELECT, RAM_SELECT };
                Mode m_mode;
 
                enum Mode { ROM_SELECT, RAM_SELECT };
                Mode m_mode;
@@ -48,6 +52,8 @@ namespace glBoy
                uint8_t m_romHigh;
 
                uint32_t m_romBank;
                uint8_t m_romHigh;
 
                uint32_t m_romBank;
+
+               bool m_ramEnabled;
        };
 }
 
        };
 }
 
diff --git a/MBC2.cc b/MBC2.cc
index 000a830282c47cba1755aa58e153eea22fa18fa0..156ed84a8581c5ffe3906ce936d4f06b9ff94e6e 100644 (file)
--- a/MBC2.cc
+++ b/MBC2.cc
@@ -20,6 +20,7 @@
 #include "Log.hh"
 
 #include <cassert>
 #include "Log.hh"
 
 #include <cassert>
+#include <sstream>
 
 using namespace glBoy;
 using namespace std;
 
 using namespace glBoy;
 using namespace std;
@@ -29,12 +30,48 @@ MBC2::MBC2(
        ) :
        m_parent(parent),
        m_rom(begin, end),
        ) :
        m_parent(parent),
        m_rom(begin, end),
-       m_romBank(0)
+       m_bankSize((end - begin) / 0x4000),
+       m_romBank(1),
+       m_ramEnabled(false)
 {
        if (end - begin < 0x8000)
        {
 {
        if (end - begin < 0x8000)
        {
-               Err() << "MBC2 ROM too small (" << end - begin << " bytes)" << Log::endl;
-               abort();
+               std::stringstream error;
+               error << "MBC2 ROM too small (" << end - begin << " bytes)";
+               throw GameboyCart::Invalid(error.str());
+       }
+       else if ((end - begin) % 0x4000)
+       {
+               std::stringstream error;
+               error << "MBC2 ROM invalid size (" << end - begin << " bytes)";
+               throw GameboyCart::Invalid(error.str());
+
+       }
+}
+
+uint16_t
+MBC2::getBankIndex(uint16_t address) const
+{
+       if (address < 0x4000)
+       {
+               return 0;
+       }
+       else
+       {
+               return m_romBank;
+       }
+}
+
+uint16_t
+MBC2::getBankSize(uint16_t address) const
+{
+       if (address < 0x4000)
+       {
+               return 0;
+       }
+       else
+       {
+               return m_bankSize;
        }
 }
 
        }
 }
 
@@ -52,7 +89,7 @@ MBC2::read8(uint16_t address)
        else
        {
                TraceMem() << "Attempt to read unmapped MBC2 rom data." << Log::endl;
        else
        {
                TraceMem() << "Attempt to read unmapped MBC2 rom data." << Log::endl;
-               abort();
+               return 0;
        }
 }
 
        }
 }
 
@@ -61,7 +98,21 @@ MBC2::write8(uint16_t address, uint8_t value)
 {
        if (address < 0x1FFF)
        {
 {
        if (address < 0x1FFF)
        {
-               // TODO enable/disable ram
+               if ((address & 0x10) == 0)
+               {
+                       if ((value & 0xf) == 0xa) // enable
+                       {
+                               m_ramEnabled = true;
+                       }
+                       else
+                       {
+                               if (m_ramEnabled)
+                               {
+                                       m_parent->saveExtRam();
+                               }
+                               m_ramEnabled = false;
+                       }
+               }
        }
        else if (address < 0x4000)
        {
        }
        else if (address < 0x4000)
        {
diff --git a/MBC2.hh b/MBC2.hh
index 1445f8356cd01f4e3f22b3fe0f357959a14c386e..2fc178e1b917bb5b72890fb8b47bf202f2cc8b2e 100644 (file)
--- a/MBC2.hh
+++ b/MBC2.hh
@@ -33,6 +33,8 @@ namespace glBoy
                        const uint8_t* begin,
                        const uint8_t* end);
 
                        const uint8_t* begin,
                        const uint8_t* end);
 
+               virtual uint16_t getBankIndex(uint16_t address) const;
+               virtual uint16_t getBankSize(uint16_t address) const;
                virtual uint8_t read8(uint16_t address);
                virtual void write8(uint16_t address, uint8_t value);
 
                virtual uint8_t read8(uint16_t address);
                virtual void write8(uint16_t address, uint8_t value);
 
@@ -40,8 +42,11 @@ namespace glBoy
                GameboyCart* m_parent;
 
                std::vector<uint16_t> m_rom;
                GameboyCart* m_parent;
 
                std::vector<uint16_t> m_rom;
+               size_t m_bankSize;
 
                uint32_t m_romBank;
 
                uint32_t m_romBank;
+
+               bool m_ramEnabled;
        };
 }
 
        };
 }
 
diff --git a/MBC3.cc b/MBC3.cc
index 750a38b091aae57953f15ee6998f30dd368bc84b..91d14a9bf5e521886cedc55b85d89ca8658b3137 100644 (file)
--- a/MBC3.cc
+++ b/MBC3.cc
@@ -21,6 +21,8 @@
 
 #include <cassert>
 
 
 #include <cassert>
 
+#include <sstream>
+
 using namespace glBoy;
 using namespace std;
 
 using namespace glBoy;
 using namespace std;
 
@@ -29,15 +31,52 @@ MBC3::MBC3(
        ) :
        m_parent(parent),
        m_rom(begin, end),
        ) :
        m_parent(parent),
        m_rom(begin, end),
-       m_romBank(0)
+       m_bankSize((end - begin) / 0x4000),
+       m_romBank(1),
+       m_ramEnabled(false)
 {
        if (end - begin < 0x8000)
        {
 {
        if (end - begin < 0x8000)
        {
-               Err() << "MBC3 ROM too small (" << end - begin << " bytes)" << Log::endl;
-               abort();
+               std::stringstream error;
+               error << "MBC3 ROM too small (" << end - begin << " bytes)";
+               throw GameboyCart::Invalid(error.str());
+       }
+       else if ((end - begin) % 0x4000)
+       {
+               std::stringstream error;
+               error << "MBC3 ROM invalid size (" << end - begin << " bytes)";
+               throw GameboyCart::Invalid(error.str());
+
        }
 }
 
        }
 }
 
+uint16_t
+MBC3::getBankIndex(uint16_t address) const
+{
+       if (address < 0x4000)
+       {
+               return 0;
+       }
+       else
+       {
+               return m_romBank;
+       }
+}
+
+uint16_t
+MBC3::getBankSize(uint16_t address) const
+{
+       if (address < 0x4000)
+       {
+               return 0;
+       }
+       else
+       {
+               return m_bankSize;
+       }
+}
+
+
 uint8_t
 MBC3::read8(uint16_t address)
 {
 uint8_t
 MBC3::read8(uint16_t address)
 {
@@ -52,7 +91,7 @@ MBC3::read8(uint16_t address)
        else
        {
                TraceMem() << "Attempt to read unmapped MBC3 rom data." << Log::endl;
        else
        {
                TraceMem() << "Attempt to read unmapped MBC3 rom data." << Log::endl;
-               abort();
+               return 0;
        }
 }
 
        }
 }
 
@@ -63,7 +102,18 @@ MBC3::write8(uint16_t address, uint8_t value)
 
        if (address < 0x1FFF)
        {
 
        if (address < 0x1FFF)
        {
-               // TODO enable/disable ram
+               if ((value & 0xf) == 0xa) // enable
+               {
+                       m_ramEnabled = true;
+               }
+               else
+               {
+                       if (m_ramEnabled)
+                       {
+                               m_parent->saveExtRam();
+                       }
+                       m_ramEnabled = false;
+               }
        }
        else if (address < 0x4000)
        {
        }
        else if (address < 0x4000)
        {
diff --git a/MBC3.hh b/MBC3.hh
index 0b7081a7a1a428bbc9759036f5e4cd091c0a8ed2..c8247207a38cda680bda9498ba8820f70a965974 100644 (file)
--- a/MBC3.hh
+++ b/MBC3.hh
@@ -33,6 +33,8 @@ namespace glBoy
                        const uint8_t* begin,
                        const uint8_t* end);
 
                        const uint8_t* begin,
                        const uint8_t* end);
 
+               virtual uint16_t getBankIndex(uint16_t address) const;
+               virtual uint16_t getBankSize(uint16_t address) const;
                virtual uint8_t read8(uint16_t address);
                virtual void write8(uint16_t address, uint8_t value);
 
                virtual uint8_t read8(uint16_t address);
                virtual void write8(uint16_t address, uint8_t value);
 
@@ -40,8 +42,11 @@ namespace glBoy
                GameboyCart* m_parent;
 
                std::vector<uint16_t> m_rom;
                GameboyCart* m_parent;
 
                std::vector<uint16_t> m_rom;
+               size_t m_bankSize;
 
                uint32_t m_romBank;
 
                uint32_t m_romBank;
+
+               bool m_ramEnabled;
        };
 }
 
        };
 }
 
index 0dad624c91764d079c3ab8634046d4d870c1b837..469250e164baa4b4a2b9e467641a409bd5fb7784 100644 (file)
 
 SUBDIRS = libzipper
 
 
 SUBDIRS = libzipper
 
-check_PROGRAMS = \
-       test_alu
-
-TESTS = $(check_PROGRAMS)
-
-test_alu_SOURCES = Test.cc ALU.cc
-
 dist_noinst_SCRIPTS = autogen.sh
 
 EXTRA_DIST = \
 dist_noinst_SCRIPTS = autogen.sh
 
 EXTRA_DIST = \
@@ -40,11 +33,12 @@ bin_PROGRAMS = glBoy
 
 glBoy_SOURCES = \
        ALU.hh \
 
 glBoy_SOURCES = \
        ALU.hh \
-       ALU.cc \
        Core.hh \
        Core.cc \
        DAA.hh \
        DWORD.hh \
        Core.hh \
        Core.cc \
        DAA.hh \
        DWORD.hh \
+       FPS.hh \
+       FPS.cc \
        GameboyCart \
        GameboyCart.cc \
        GameboyGraphics.hh \
        GameboyCart \
        GameboyCart.cc \
        GameboyGraphics.hh \
@@ -60,7 +54,6 @@ glBoy_SOURCES = \
        hq4x.cc \
        Log.hh \
        Log.cc \
        hq4x.cc \
        Log.hh \
        Log.cc \
-       Main.cc \
        MemoryMap.hh \
        MemoryMap.cc \
        MBC1.hh \
        MemoryMap.hh \
        MemoryMap.cc \
        MBC1.hh \
@@ -77,7 +70,14 @@ glBoy_SOURCES = \
        Registers.cc \
        ROM.hh \
        ROM.cc \
        Registers.cc \
        ROM.hh \
        ROM.cc \
-       util.hh
+       util.hh \
+       sdl/GLGraphics.hh \
+       sdl/GLGraphics.cc \
+       sdl/Main.cc \
+       sdl/SDLKeys.hh \
+       sdl/SDLKeys.cc \
+       sdl/SDLSound.hh \
+       sdl/SDLSound.cc
 
 BUILT_SOURCES = \
        InstructionSet_Opcodes.cc
 
 BUILT_SOURCES = \
        InstructionSet_Opcodes.cc
@@ -93,12 +93,10 @@ glBoy_LDADD = libzipper/libzipper.la
 
 glBoy_LDFLAGS = -lGL -lGLU
 
 
 glBoy_LDFLAGS = -lGL -lGLU
 
-CXXFLAGS=-g -O3 -march=native -W -Wall -Werror -std=c++0x
+CXXFLAGS+=-g -O3 -march=native -W -Wall -Werror -std=c++0x
+#CXXFLAGS=-g -march=native -W -Wall -Werror -std=c++0x
+#CPPFLAGS+=-DGLBOY_DEBUG
 
 InstructionSet_Opcodes.cc: InstructionSet.opcode makeOpcodes.pl
        perl $(srcdir)/makeOpcodes.pl $<
 
 
 InstructionSet_Opcodes.cc: InstructionSet.opcode makeOpcodes.pl
        perl $(srcdir)/makeOpcodes.pl $<
 
-#package: clean
-#      git commit -a
-#      git tag -a -f $(VERSION)
-
index 3d8df1bafb1419e423932155417f334b95c2b7d1..290c73d19c92ecd0893d9b859e6055d99db15de6 100644 (file)
@@ -60,7 +60,23 @@ MemoryMap::map(
 \r
        TraceMem() <<\r
                "Registering " << glBoy::hex(base) <<\r
 \r
        TraceMem() <<\r
                "Registering " << glBoy::hex(base) <<\r
-               " for " << glBoy::hex(size) << " bytes."<< Log::endl;\r
+               " for " << glBoy::hex(size) << " bytes." << Log::endl;\r
+}\r
+\r
+uint16_t\r
+MemoryMap::getBankIndex(DWORD address) const\r
+{\r
+       uint16_t offsetAddress(address.host());\r
+       const MappedArea& mapping(get(offsetAddress));\r
+       return mapping.memory->getBankIndex(offsetAddress - mapping.base);\r
+}\r
+\r
+uint16_t\r
+MemoryMap::getBankSize(DWORD address) const\r
+{\r
+       uint16_t offsetAddress(address.host());\r
+       const MappedArea& mapping(get(offsetAddress));\r
+       return mapping.memory->getBankSize(offsetAddress - mapping.base);\r
 }\r
 \r
 void\r
 }\r
 \r
 void\r
@@ -199,11 +215,24 @@ MemoryMap::copy(uint8_t* dest, uint16_t srcAddress, int bytes)
 {\r
        const MappedArea& srcMap(get(srcAddress));\r
 \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
+       uint16_t offset(srcAddress - srcMap.base);\r
+       uint16_t copyBytes(std::min(srcMap.size - offset, bytes));\r
+       srcMap.memory->copy(dest, offset, copyBytes);\r
 }\r
 \r
 }\r
 \r
+// Currently only used for loading saved games.\r
+void\r
+MemoryMap::copy(uint16_t destAddress, uint8_t* src, int bytes)\r
+{\r
+       const MappedArea& destMap(get(destAddress));\r
+\r
+       uint16_t offset(destAddress - destMap.base);\r
+\r
+       for (int i = 0; i < bytes && i < (destMap.size - offset); ++i)\r
+       {\r
+               destMap.memory->write8(offset + i, src[i]);\r
+       }\r
+}\r
 void\r
 MemoryMap::Memory::copy(uint8_t* dest, uint16_t address, int bytes)\r
 {\r
 void\r
 MemoryMap::Memory::copy(uint8_t* dest, uint16_t address, int bytes)\r
 {\r
index e01041c07b4b06f276e1c24e1ff6a4730e20472b..bcb09bdcd5c435117b79866f8dcfb64aab4ade99 100644 (file)
@@ -38,6 +38,9 @@ public:
                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
                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
+               virtual uint16_t getBankIndex(uint16_t) const { return 0; }\r
+               virtual uint16_t getBankSize(uint16_t) const { return 0; }\r
        };\r
 \r
        MemoryMap();\r
        };\r
 \r
        MemoryMap();\r
@@ -50,6 +53,9 @@ public:
 \r
        void remove(uint16_t base);\r
 \r
 \r
        void remove(uint16_t base);\r
 \r
+       uint16_t getBankIndex(DWORD address) const;\r
+       uint16_t getBankSize(DWORD address) const;\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
        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
@@ -59,14 +65,20 @@ public:
        void write16(DWORD address, int8_t offset, DWORD value);\r
 \r
        void copy(uint8_t* dest, uint16_t srcAddress, int bytes);\r
        void write16(DWORD address, int8_t offset, DWORD value);\r
 \r
        void copy(uint8_t* dest, uint16_t srcAddress, int bytes);\r
+       void copy(uint16_t destAddress, uint8_t* src, int bytes);\r
 \r
 private:\r
        struct MappedArea\r
        {\r
                MappedArea(\r
 \r
 private:\r
        struct MappedArea\r
        {\r
                MappedArea(\r
-                       uint16_t _base, uint16_t _size, std::shared_ptr<Memory> _mem\r
+                       uint16_t _base,\r
+                       uint16_t _size,\r
+                       std::shared_ptr<Memory> _mem\r
                        ) :\r
                        ) :\r
-                       base(_base), size(_size), memory(_mem) {}\r
+                       base(_base),\r
+                       size(_size),\r
+                       memory(_mem)\r
+               {}\r
 \r
                uint16_t base;\r
                uint16_t size;\r
 \r
                uint16_t base;\r
                uint16_t size;\r
diff --git a/NEWS b/NEWS
index 2483f02bea22aafef5e132284fb7aa57a34755e8..e93883f897fe34f8bf0489b85b93180ee08395c2 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -4,8 +4,10 @@
                settings.
        - Improved MBC1 support.
        - Added MBC2 and MBC3 support.
                settings.
        - Improved MBC1 support.
        - Added MBC2 and MBC3 support.
+       - Added saved game support.
        - Added compressed ROM support (zip and gzip).
        - Numerous z80 emulation bugfixes.
        - Added compressed ROM support (zip and gzip).
        - Numerous z80 emulation bugfixes.
+       - Fixed GameBoy CPU flags (only high 4bits supported, unlike z80)
 
 2011-01-31     Version 1.0.0
        - Initial release
 
 2011-01-31     Version 1.0.0
        - Initial release
diff --git a/RAM.cc b/RAM.cc
index 400eb3383494507470afdf9ae3a5e4ed813e0fe6..0005c450f6b9308d78e467f5dd450dff26b815c8 100644 (file)
--- a/RAM.cc
+++ b/RAM.cc
@@ -30,19 +30,32 @@ RAM::RAM(uint16_t size)
 uint8_t\r
 RAM::read8(uint16_t address)\r
 {\r
 uint8_t\r
 RAM::read8(uint16_t address)\r
 {\r
-       return m_mem[address];\r
+       if (address < m_mem.size())\r
+       {\r
+               return m_mem[address];\r
+       }\r
+       else\r
+       {\r
+               return 0;\r
+       }\r
 }\r
 \r
 void\r
 RAM::write8(uint16_t address, uint8_t value)\r
 {\r
 }\r
 \r
 void\r
 RAM::write8(uint16_t address, uint8_t value)\r
 {\r
-       m_mem[address] = value;\r
+       if (address < m_mem.size())\r
+       {\r
+               m_mem[address] = value;\r
+       }\r
 }\r
 \r
 \r
 void\r
 RAM::copy(uint8_t* dest, uint16_t address, int bytes)\r
 {\r
 }\r
 \r
 \r
 void\r
 RAM::copy(uint8_t* dest, uint16_t address, int bytes)\r
 {\r
-       memcpy(dest, &m_mem[0] + address, bytes);\r
+       memcpy(\r
+               dest,\r
+               &m_mem[0] + address,\r
+               std::min(bytes, int(m_mem.size() - address)));\r
 }\r
 \r
 }\r
 \r
diff --git a/RAM.hh b/RAM.hh
index 636b5ddebfb4da7688aa961ce027868c7d31bb7e..10b25c79769bc33dacccb7cd57fdd60b5e772006 100644 (file)
--- a/RAM.hh
+++ b/RAM.hh
@@ -33,7 +33,6 @@ public:
        virtual void write8(uint16_t address, uint8_t value);\r
        virtual void copy(uint8_t* dest, uint16_t address, int bytes);\r
 \r
        virtual void write8(uint16_t address, uint8_t value);\r
        virtual void copy(uint8_t* dest, uint16_t address, int bytes);\r
 \r
-\r
 private:\r
        bool m_fourBitMode;\r
        std::vector<uint8_t> m_mem;\r
 private:\r
        bool m_fourBitMode;\r
        std::vector<uint8_t> m_mem;\r
index ca4db00f9bee8870b106195d62ad08f4aaed5598..62559ab8750ecd6de4a275643a14f643a3c641eb 100644 (file)
@@ -31,19 +31,32 @@ RAM_4bit::RAM_4bit(uint16_t size)
 uint8_t\r
 RAM_4bit::read8(uint16_t address)\r
 {\r
 uint8_t\r
 RAM_4bit::read8(uint16_t address)\r
 {\r
-       return m_mem[address] & 0xF;\r
+       if (address < m_mem.size())\r
+       {\r
+               return m_mem[address] & 0xF;\r
+       }\r
+       else\r
+       {\r
+               return 0;\r
+       }\r
 }\r
 \r
 void\r
 RAM_4bit::write8(uint16_t address, uint8_t value)\r
 {\r
 }\r
 \r
 void\r
 RAM_4bit::write8(uint16_t address, uint8_t value)\r
 {\r
-       m_mem[address] = value & 0xF;\r
+       if (address < m_mem.size())\r
+       {\r
+               m_mem[address] = value & 0xF;\r
+       }\r
 }\r
 \r
 \r
 void\r
 RAM_4bit::copy(uint8_t* dest, uint16_t address, int bytes)\r
 {\r
 }\r
 \r
 \r
 void\r
 RAM_4bit::copy(uint8_t* dest, uint16_t address, int bytes)\r
 {\r
-       memcpy(dest, &m_mem[0] + address, bytes);\r
+       memcpy(\r
+               dest,\r
+               &m_mem[0] + address,\r
+               std::min(bytes, int(m_mem.size() - address)));\r
 }\r
 \r
 }\r
 \r
index 1f8343609aca218c2d267f54fee339b53dc00bc4..0ce7ea950e41a76761b6589692b8b311eb928982 100644 (file)
 using namespace glBoy;\r
 \r
 void\r
 using namespace glBoy;\r
 \r
 void\r
-Flags::set(op8_t operand, const Core& context)\r
+Flags::set(op8_t operand, const Core& /*context*/)\r
 {\r
 {\r
-       S = static_cast<int8_t>(operand) < 0 ? 1 : 0;\r
+       //S = static_cast<int8_t>(operand) < 0 ? 1 : 0;\r
        Z = operand == 0 ? 1 : 0;\r
        Z = operand == 0 ? 1 : 0;\r
-       U5 = (operand & 0b00010000) >> 4;\r
+       //U5 = (operand & 0b00010000) >> 4;\r
        H = 0;\r
        H = 0;\r
-       U3 = (operand & 0b00000100) >> 2;\r
-       P = context.getIFF2();\r
+       //U3 = (operand & 0b00000100) >> 2;\r
+       //P = context.getIFF2();\r
        N = 0;\r
        // C is not affected\r
 }\r
 \r
 void\r
        N = 0;\r
        // C is not affected\r
 }\r
 \r
 void\r
-Flags::setCounter(DWORD BC)\r
+Flags::setCounter(DWORD /*BC*/)\r
 {\r
        // S is not affected\r
        // Z is not affected\r
 {\r
        // S is not affected\r
        // Z is not affected\r
-       U5 = (BC.host() & 0b00010000) >> 4;\r
+       //U5 = (BC.host() & 0b00010000) >> 4;\r
        H = 0;\r
        H = 0;\r
-       U3 = (BC.host() & 0b00000100) >> 2;\r
-       P = BC.host() ? 1 : 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
        N = 0;\r
        // C is not affected\r
 }\r
 \r
 void\r
-Flags::setSub(op8_t A, op8_t B, DWORD BC)\r
+Flags::setSub(op8_t A, op8_t B, DWORD /*BC*/)\r
 {\r
 {\r
-       S = (A > B) ? 1 : 0;\r
+       //S = (A > B) ? 1 : 0;\r
        Z = (A == B) ? 1 : 0;\r
        Z = (A == B) ? 1 : 0;\r
-       U5 = (A & 0b00010000) >> 4;\r
+       //U5 = (A & 0b00010000) >> 4;\r
        H = (B & 0xF) > (A & 0xF);\r
        H = (B & 0xF) > (A & 0xF);\r
-       U3 = (A & 0b00000100) >> 2;\r
-       P = BC.host() ? 1 : 0;\r
+       //U3 = (A & 0b00000100) >> 2;\r
+       //P = BC.host() ? 1 : 0;\r
        N = 1;\r
        // C is not affected\r
 }\r
        N = 1;\r
        // C is not affected\r
 }\r
index 4f460cb5081db63bdb3772b0edbb4d119aea7c58..23b43cbf98dc5b6ec35da77bf9b5b057ac659489 100644 (file)
@@ -43,14 +43,14 @@ struct __attribute__ ((__packed__)) Flags
        {\r
                struct __attribute__ ((__packed__))\r
                {\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 U1:1; // LSB\r
+                       unsigned U2:1;\r
+                       unsigned U3:1;\r
+                       unsigned U4:1;\r
+                       unsigned C:1; /// Carry.\r
                        unsigned H:1; /// Half-Carry (BCD)\r
                        unsigned H:1; /// Half-Carry (BCD)\r
-                       unsigned U5:1; /// 5th bit of last 8bit op that altered flags\r
+                       unsigned N:1; /// Add/Subtract\r
                        unsigned Z:1; /// Zero Flag\r
                        unsigned Z:1; /// Zero Flag\r
-                       unsigned S:1; /// Sign Flag. MSB of Flag register\r
                };\r
                reg8_t byte;\r
        };\r
                };\r
                reg8_t byte;\r
        };\r
@@ -86,11 +86,6 @@ struct Registers
                h; \\r
        };\r
 \r
                h; \\r
        };\r
 \r
-       // Note: The order of these unions/structs is important, as\r
-       // offsets are taken into the Register struct\r
-       reg8_t begin8[0];\r
-       DWORD begin16[0];\r
-\r
        union\r
        {\r
                Z80_REG_STRUCT(reg8_t B, reg8_t C);\r
        union\r
        {\r
                Z80_REG_STRUCT(reg8_t B, reg8_t C);\r
diff --git a/Test.cc b/Test.cc
deleted file mode 100644 (file)
index 458b8a2..0000000
--- a/Test.cc
+++ /dev/null
@@ -1,275 +0,0 @@
-//     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;
-}
diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..2d31089
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="com.codesrc.glboy"
+      android:versionCode="1"
+      android:versionName="1.0">
+    <uses-sdk android:minSdkVersion="10" />
+    <uses-feature android:required="true" android:glEsVersion="0x00020000"></uses-feature>
+
+    <application android:icon="@drawable/icon" android:label="@string/app_name">
+        <activity android:name=".GlBoyActivity"
+                  android:label="@string/app_name"
+                  android:configChanges="keyboardHidden|orientation">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+    </application>
+</manifest>
\ No newline at end of file
diff --git a/android/jni/Android.mk b/android/jni/Android.mk
new file mode 100644 (file)
index 0000000..ccd788d
--- /dev/null
@@ -0,0 +1,34 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_CPP_EXTENSION := .cc
+
+LOCAL_LDLIBS := -llog  -lz
+LOCAL_STATIC_LIBRARIES := libzipper
+
+LOCAL_MODULE    := glBoy
+
+LOCAL_SRC_FILES :=\
+       AndroidInterface.cc \
+       ../../Core.cc \
+       ../../FPS.cc \
+       ../../GameboyCart.cc \
+       ../../GameboyGraphics.cc \
+       ../../GameboyJoypad.cc \
+       ../../GameboySound.cc \
+       ../../GameboyTimer.cc \
+       ../../Log.cc \
+       ../../MBC1.cc \
+       ../../MBC2.cc \
+       ../../MBC3.cc \
+       ../../MemoryMap.cc \
+       ../../RAM.cc \
+       ../../RAM_4bit.cc \
+       ../../Registers.cc \
+       ../../ROM.cc \
+
+include $(BUILD_SHARED_LIBRARY)
+
+$(call import-module,zipper)
+
diff --git a/android/jni/AndroidInterface.cc b/android/jni/AndroidInterface.cc
new file mode 100644 (file)
index 0000000..fd373fd
--- /dev/null
@@ -0,0 +1,212 @@
+#include <jni.h>
+#include <string.h>
+#include <android/log.h>
+
+#include "../../Core.hh"
+#include "../../GameboyCart.hh"
+#include "../../GameboyGraphics.hh"
+#include "../../GameboyJoypad.hh"
+#include "../../GameboySound.hh"
+#include "../../GameboyTimer.hh"
+#include "../../RAM.hh"
+
+using namespace glBoy;
+
+#define DEBUG_TAG "libglBoy.so"
+
+struct State
+{
+       Core* m_core;
+       GameboyCart* m_cart;
+       GameboyGraphics* m_graphics;
+       pthread_t m_thread;
+} GlobalState = {0, 0, 0, 0};
+
+extern "C"
+{
+
+static void* start(void* val)
+{
+       Core* core = reinterpret_cast<Core*>(val);
+       core->run();
+       return NULL;
+}
+
+JNIEXPORT jboolean JNICALL Java_com_codesrc_glboy_GlBoyActivity_loadCart(
+       JNIEnv * j_env,
+       jobject /*this_*/,
+       jstring j_filename)
+{
+       jboolean result = false;
+
+       std::string filename;
+       {
+               jboolean isCopy;
+               const char* filenameStr = j_env->GetStringUTFChars(j_filename, &isCopy);
+               filename = filenameStr;
+               j_env->ReleaseStringUTFChars(j_filename, filenameStr);
+       }
+
+       __android_log_print(
+               ANDROID_LOG_DEBUG, DEBUG_TAG, "Opening cart: %s", filename.c_str());
+
+       try
+       {
+               GlobalState.m_core = new Core();
+               Core& core = *GlobalState.m_core;
+               GlobalState.m_cart = new GameboyCart(core, filename);
+
+               if (!GlobalState.m_cart->isValid())
+               {
+                       throw GameboyCart::Invalid("Unknown");
+               }
+
+               __android_log_print(
+                       ANDROID_LOG_DEBUG, DEBUG_TAG, "Cart opened.");
+
+               GlobalState.m_graphics = new GameboyGraphics(core);
+
+               // Map the standard internal RAM, plus shadow.
+               MemoryMap& map(core.getMemoryMap());
+               {
+                       std::shared_ptr<MemoryMap::Memory> mem(new 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.
+               // Exclude 0xFFFF, which is handled directly within Core for interrupt
+               // handling
+               {
+                       std::shared_ptr<MemoryMap::Memory> mem(new RAM(127));
+                       map.map(0xFF80, 127, mem);
+               }
+
+               // Map other devices
+               std::shared_ptr<GameboyJoypad> joypad(
+                       new GameboyJoypad(core)
+                       );
+               map.map(0xFF00, 1, joypad);
+               std::shared_ptr<GameboyTimer> timer(
+                       new GameboyTimer(
+                               core,
+                               std::bind(
+                                       &GameboyGraphics::setFrameSkip, GlobalState.m_graphics),
+                               std::bind(
+                                       &GameboyGraphics::clearFrameSkip, GlobalState.m_graphics)
+                               )
+                       );
+               map.map(0xFF04, 4, timer);
+               std::shared_ptr<GameboySound> sound(new 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);
+
+               // Run the core in a new thread.
+               // OpenGL output must be in the main thread.
+               pthread_create(&GlobalState.m_thread, NULL, start, &core);
+
+               result = true;
+       }
+       catch (std::exception& e)
+       {
+               __android_log_print(
+                       ANDROID_LOG_DEBUG, DEBUG_TAG, "Exception caught: %s", e.what());
+
+               // Don't worry about m_thread, as exceptions can't be thrown after it is
+               // created.
+               delete GlobalState.m_graphics;
+               delete GlobalState.m_cart;
+               delete GlobalState.m_core;
+               GlobalState.m_graphics = 0;
+               GlobalState.m_cart = 0;
+               GlobalState.m_core = 0;
+       }
+
+       return result;
+}
+
+JNIEXPORT void JNICALL Java_com_codesrc_glboy_GlBoyActivity_stop(
+       JNIEnv* /*j_env*/,
+       jobject /*this_*/)
+{
+       __android_log_print(
+               ANDROID_LOG_DEBUG, DEBUG_TAG, "Requested to stop");
+
+       if (GlobalState.m_core)
+       {
+               GlobalState.m_core->stop();
+       }
+
+       if (GlobalState.m_thread)
+       {
+               pthread_join(GlobalState.m_thread, NULL);
+       }
+       delete GlobalState.m_graphics;
+       delete GlobalState.m_cart;
+       delete GlobalState.m_core;
+       GlobalState.m_graphics = 0;
+       GlobalState.m_cart = 0;
+       GlobalState.m_core = 0;
+       GlobalState.m_thread = 0;
+}
+
+JNIEXPORT void JNICALL Java_com_codesrc_glboy_GlBoyActivity_pause(
+       JNIEnv* /*j_env*/,
+       jobject /*this_*/)
+{
+       __android_log_print(
+               ANDROID_LOG_DEBUG, DEBUG_TAG, "Requested to pause");
+       if (GlobalState.m_core)
+       {
+               GlobalState.m_core->stop();
+       }
+       if (GlobalState.m_thread)
+       {
+               pthread_join(GlobalState.m_thread, NULL);
+       }
+       GlobalState.m_thread = 0;
+}
+
+JNIEXPORT void JNICALL Java_com_codesrc_glboy_GlBoyActivity_resume(
+       JNIEnv* /*j_env*/,
+       jobject /*this_*/)
+{
+       __android_log_print(
+               ANDROID_LOG_DEBUG, DEBUG_TAG, "Requested to resume");
+
+       // Run the core in a new thread.
+       // OpenGL output must be in the main thread.
+       if (!GlobalState.m_thread && GlobalState.m_core)
+       {
+               GlobalState.m_core->resume();
+               pthread_create(&GlobalState.m_thread, NULL, start, GlobalState.m_core);
+       }
+}
+
+JNIEXPORT void JNICALL Java_com_codesrc_glboy_GlBoyActivity_getFrame(
+       JNIEnv * j_env,
+       jobject /*this_*/,
+       jbyteArray frameBuffer)
+{
+       if (GlobalState.m_graphics)
+       {
+               uint8_t* rawFrame = static_cast<uint8_t*>(
+                               j_env->GetPrimitiveArrayCritical(frameBuffer, 0));
+               GlobalState.m_graphics->getGreyscaleFrame(rawFrame); // thread safe
+               j_env->ReleasePrimitiveArrayCritical(frameBuffer, rawFrame, 0);
+       }
+}
+
+
+} // extern "C"
+
+
+
diff --git a/android/jni/Application.mk b/android/jni/Application.mk
new file mode 100644 (file)
index 0000000..7a6ef43
--- /dev/null
@@ -0,0 +1,14 @@
+APP_CPPFLAGS += -fexceptions -frtti
+APP_STL := gnustl_static
+APP_MODULES := zipper glBoy
+
+# "Fat" binary. Let the installer choose which is appropriate.
+APP_ABI := armeabi armeabi-v7a
+
+# Yes, the NDK has confused the meaning of CPPFLAGS vs CXXFLAGS :-(
+APP_CFLAGS := -W -Wall -Werror -D_POSIX_C_SOURCE=200112 -UNDEBUG
+
+# bionic /sys/types.h fails with -std=c++0x, as it doesn't include
+# stdint.h, but tries to use uint64_t.
+APP_CPPFLAGS := -std=gnu++0x
+
diff --git a/android/jni/autoconfig.h b/android/jni/autoconfig.h
new file mode 100644 (file)
index 0000000..ac0a8d7
--- /dev/null
@@ -0,0 +1,75 @@
+/* autoconfig.h.  Generated from autoconfig.h.in by configure.  */
+/* autoconfig.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Compile for big-endian host cpu */
+/* #undef HOST_BIG_ENDIAN */
+
+/* Compile for litte-endian host cpu */
+#define HOST_LITTLE_ENDIAN /**/
+
+
+/* Name of package */
+#define PACKAGE "glboy"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "michael@codesrc.com"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "glBoy"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "glBoy 1.0.1"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "glboy"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "1.0.1"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "1.0.1"
+
+/* Android-specific dodgy c-library hacks */
+/* http://code.google.com/p/android/issues/detail?id=20140 */
+#include <time.h>
+extern "C"
+{
+extern int clock_nanosleep(clockid_t, int, const struct timespec *, struct timespec*);
+}
+
diff --git a/android/src/com/codesrc/glboy/GlBoyActivity.java b/android/src/com/codesrc/glboy/GlBoyActivity.java
new file mode 100644 (file)
index 0000000..bd769f6
--- /dev/null
@@ -0,0 +1,403 @@
+package com.codesrc.glboy;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+
+import android.app.Activity;
+
+import android.os.Bundle;
+import android.opengl.GLSurfaceView;
+import android.opengl.GLES20;
+import android.opengl.Matrix;
+import android.view.MotionEvent;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+
+public class GlBoyActivity extends Activity
+{
+       static {
+               System.loadLibrary("glBoy");
+       }
+       
+
+       @Override
+       public void onCreate(Bundle savedInstanceState)
+       {
+               super.onCreate(savedInstanceState);
+               stop();
+               //mGLView = new GameboyView(this);
+               //setContentView(mGLView);
+               
+        setContentView(R.layout.main);
+        mGLView = (GLSurfaceView) findViewById(R.id.glsurfaceview);
+        mGLView.setEGLContextClientVersion(2); // GLES 2.0
+        mGLView.setRenderer(new GameboyRenderer(this));
+
+        // Find our buttons
+       // Button aButton = (Button) findViewById(R.id.buttonA);
+
+        // Wire each button to a click listener
+       // aButton.setOnTouchListener(OnTouchListener)
+       // aButton.set.setOnClickListener(mVisibleListener);
+
+               
+       }
+       /*
+    OnClickListener mInvisibleListener = new OnTouchListener() {
+        public void onClick(View v) {
+            mVictim1.setVisibility(View.INVISIBLE);
+            mVictim2.setVisibility(View.INVISIBLE);
+            mVictimContainer.setVisibility(View.INVISIBLE);
+        }
+    };*/
+
+       @Override
+       public void onStart()
+       {
+               super.onStart();
+               loadCart("/sdcard/tetris.zip");
+       }       
+
+       @Override
+       public void onStop()
+       {
+               super.onStop();
+               stop();
+       }
+       
+       @Override
+       public void onPause()
+       {
+               super.onPause();
+               pause();
+               mGLView.onPause();
+       }
+
+       @Override
+       public void onResume()
+       {
+               super.onResume();
+               resume();
+               mGLView.onResume();
+       }
+
+       private native boolean loadCart(String filename);
+       private native void pause();
+       private native void resume();
+       private native void stop();
+       public native void getFrame(byte[] framebuffer);
+
+       private GLSurfaceView mGLView;
+}
+
+class GameboyView extends GLSurfaceView
+{
+       public GameboyView(GlBoyActivity context)
+       {
+               super(context);
+               setEGLContextClientVersion(2); // GLES 2.0
+               mRenderer = new GameboyRenderer(context);
+               setRenderer(mRenderer);
+               
+               // setDebugFlags(GLSurfaceView.DEBUG_CHECK_GL_ERROR | GLSurfaceView.DEBUG_LOG_GL_CALLS);
+       }
+
+       public boolean onTouchEvent(final MotionEvent event) {
+               /*
+                  queueEvent(new Runnable(){
+                  public void run() {
+                  mRenderer.setColor(event.getX() / getWidth(),
+                  event.getY() / getHeight(), 1.0f);
+                  }});
+                  */
+                  return true; 
+       }
+
+       GameboyRenderer mRenderer;
+}
+
+class GameboyRenderer implements GLSurfaceView.Renderer
+{
+       GameboyRenderer(GlBoyActivity context)
+       {
+               m_activity = context;
+               m_frameBuffer = ByteBuffer.wrap(m_frameBufferBytes);
+               
+       }
+       public void onSurfaceCreated(GL10 obsolete, EGLConfig config)
+       {
+        m_program = createProgram(m_vertexShader, m_fragmentShader);
+        if (m_program == 0) {
+            return;
+        }
+
+        m_aPosition = GLES20.glGetAttribLocation(m_program, "a_position");
+        checkGlError("glGetAttribLocation a_position");
+        if (m_aPosition == -1) {
+            throw new RuntimeException("Could not get attrib location for a_position");
+        }
+        
+        m_aTexCoord = GLES20.glGetAttribLocation(m_program, "a_texCoord");
+        checkGlError("glGetAttribLocation a_texCoord");
+        if (m_aTexCoord == -1) {
+            throw new RuntimeException("Could not get attrib location for a_texCoord");
+        }
+   
+        m_sTexture = GLES20.glGetUniformLocation(m_program, "s_texture");
+        checkGlError("glGetUniformLocation s_texture");
+        if (m_sTexture == -1) {
+            throw new RuntimeException("Could not get uniform location for s_texture");
+        }
+
+        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+               GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+               GLES20.glDisable(GLES20.GL_DEPTH_TEST);
+               
+               // Allocate texture
+               int[] textures = new int[1];
+               GLES20.glGenTextures(1, textures, 0);
+               m_textureId = textures[0];
+               GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, m_textureId);
+               GLES20.glActiveTexture(GLES20.GL_TEXTURE0); // Must be called before glTexParameterf
+               
+               GLES20.glTexParameterf(
+                       GLES20.GL_TEXTURE_2D,
+                       GLES20.GL_TEXTURE_MIN_FILTER,
+                       GLES20.GL_LINEAR);
+               GLES20.glTexParameterf(
+                       GLES20.GL_TEXTURE_2D,
+                       GLES20.GL_TEXTURE_MAG_FILTER,
+                       GLES20.GL_LINEAR);
+               
+               // Clamp-to-edge required for non-power-of-two texture
+               GLES20.glTexParameterf(
+                       GLES20.GL_TEXTURE_2D,
+                       GLES20.GL_TEXTURE_WRAP_S,
+                       GLES20.GL_CLAMP_TO_EDGE);
+               GLES20.glTexParameterf(
+                       GLES20.GL_TEXTURE_2D,
+                       GLES20.GL_TEXTURE_WRAP_T,
+                       GLES20.GL_CLAMP_TO_EDGE);               
+               
+               ByteBuffer byteBuf = ByteBuffer.allocateDirect(m_vertices.length * 4);
+               byteBuf.order(ByteOrder.nativeOrder());
+               m_vertexBuffer = byteBuf.asFloatBuffer();
+               m_vertexBuffer.put(m_vertices);
+               m_vertexBuffer.position(0);
+               
+               ByteBuffer byteBuf2 = ByteBuffer.allocateDirect(m_textureCoords.length * 4);
+               byteBuf2.order(ByteOrder.nativeOrder());
+               m_textureBuffer = byteBuf2.asFloatBuffer();
+               m_textureBuffer.put(m_textureCoords);
+               m_textureBuffer.position(0);
+       }
+
+       public void onSurfaceChanged(GL10 obsolete, int w, int h)
+       {
+               float aspect = 144.0f/160.f;
+               if (h > w)
+               {
+                       int gameHeight = (int)(w*aspect);
+                       GLES20.glViewport(0, h - gameHeight, w, gameHeight);
+               }
+               else
+               {
+                       int gameWidth = (int)(h/aspect);
+                       GLES20.glViewport((w - gameWidth)/2, 0, gameWidth, h);
+               }
+               
+        // this projection matrix is applied to object coodinates
+        // in the onDrawFrame() method      
+               // Camera is at (0,0,2), looking down the -z axis,
+               // with (0,1,0) "up" vector.
+               // The near and far values supplied to glOrthof define the -distance-
+               // of the clipping frames from the camera. ie. near/far of 1, 10
+               // will only draw points with a z value between 1 and -8.
+               Matrix.orthoM(m_ProjMatrix, 0, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 10.0f);
+               m_uMVPMatrixHandle = GLES20.glGetUniformLocation(m_program, "uMVPMatrix");
+
+               // Change the "view" matrix to set everything to z = -1, so it is within
+               // our clipping planes.
+        Matrix.setLookAtM(m_VMatrix, 0, 0, 0, 2, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
+        
+        // Our textures increase Y when heading down. This is the opposite
+        // of the opengl coordinates, so we'll flip it here.
+               Matrix.setIdentityM(m_MMatrix, 0);
+               Matrix.scaleM(m_MMatrix, 0, 1.0f, -1.0f, 1.0f);
+               
+               // May as well MVP here, since it doesn't change.
+        Matrix.multiplyMM(m_MVPMatrix, 0, m_VMatrix, 0, m_MMatrix, 0);
+        Matrix.multiplyMM(m_MVPMatrix, 0, m_ProjMatrix, 0, m_MVPMatrix, 0);
+       }
+
+       public void onDrawFrame(GL10 obsolete)
+       {
+               GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
+        GLES20.glUseProgram(m_program);
+        //checkGlError("glUseProgram");
+
+        m_activity.getFrame(m_frameBufferBytes);
+
+               GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
+               GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, m_textureId);
+        
+               // Create the texture image
+               GLES20.glTexImage2D(
+                               GLES20.GL_TEXTURE_2D,
+                               0,
+                               GLES20.GL_LUMINANCE,
+                               160,
+                               144,
+                               0,
+                               GLES20.GL_LUMINANCE,
+                               GLES20.GL_UNSIGNED_BYTE,
+                               m_frameBuffer
+                               );
+               GLES20.glUniform1i(m_sTexture, 0);
+
+        GLES20.glUniformMatrix4fv(m_uMVPMatrixHandle, 1, false, m_MVPMatrix, 0);
+    
+               m_vertexBuffer.position(0);
+               GLES20.glVertexAttribPointer(
+                       m_aPosition,
+                       3, // x, y, z
+                       GLES20.GL_FLOAT,
+                       false,
+                       0, // Tightly packed
+                       m_vertexBuffer);
+               GLES20.glEnableVertexAttribArray(m_aPosition);
+               
+               m_textureBuffer.position(0);
+               GLES20.glVertexAttribPointer(
+                               m_aTexCoord,
+                               2, // x, y
+                               GLES20.GL_FLOAT,
+                               false,
+                               0, // Tightly packed
+                               m_textureBuffer);
+               GLES20.glEnableVertexAttribArray(m_aTexCoord);          
+       
+               GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
+        //checkGlError("glDrawArrays");
+       }
+       
+    private int loadShader(int shaderType, String source) {
+        int shader = GLES20.glCreateShader(shaderType);
+        if (shader != 0) {
+            GLES20.glShaderSource(shader, source);
+            GLES20.glCompileShader(shader);
+            int[] compiled = new int[1];
+            GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
+            if (compiled[0] == 0) {
+                Log.e(TAG, "Could not compile shader " + shaderType + ":");
+                Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
+                GLES20.glDeleteShader(shader);
+                shader = 0;
+            }
+        }
+        return shader;
+    }
+
+    private int createProgram(String vertexSource, String fragmentSource) {
+        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
+        if (vertexShader == 0) {
+            return 0;
+        }
+
+        int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
+        if (pixelShader == 0) {
+            return 0;
+        }
+
+        int program = GLES20.glCreateProgram();
+        if (program != 0) {
+            GLES20.glAttachShader(program, vertexShader);
+            checkGlError("glAttachShader");
+            GLES20.glAttachShader(program, pixelShader);
+            checkGlError("glAttachShader");
+            GLES20.glLinkProgram(program);
+            int[] linkStatus = new int[1];
+            GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
+            if (linkStatus[0] != GLES20.GL_TRUE) {
+                Log.e(TAG, "Could not link program: ");
+                Log.e(TAG, GLES20.glGetProgramInfoLog(program));
+                GLES20.glDeleteProgram(program);
+                program = 0;
+            }
+        }
+        return program;
+    }
+    
+    private void checkGlError(String op) {
+        int error;
+        while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
+            Log.e(TAG, op + ": glError " + error);
+            throw new RuntimeException(op + ": glError " + error);
+        }
+    }
+
+       private GlBoyActivity m_activity;
+       
+       // Native code fills this out for us, and we then display it using
+       // opengl
+       byte[] m_frameBufferBytes = new byte[144*160];
+       ByteBuffer m_frameBuffer;
+       
+       private int m_textureId;
+    private int m_program;
+    private int m_aPosition;
+    private int m_aTexCoord;
+    private int m_sTexture;
+    private int m_uMVPMatrixHandle;
+    private float[] m_MVPMatrix = new float[16];
+    private float[] m_VMatrix = new float[16];
+    private float[] m_MMatrix = new float[16];
+    private float[] m_ProjMatrix = new float[16];
+       
+       private FloatBuffer m_vertexBuffer;
+       private FloatBuffer m_textureBuffer; // texture coords
+       private float m_vertices[] =
+       {
+               -1.0f, 1.0f, 0, // Top Left
+               1.0f, 1.0f, 0,  // Top Right  
+               -1.0f, -1.0f, 0, // Bottom Left
+               1.0f, -1.0f, 0 // Bottom Right
+       };
+    private float m_textureCoords[] =
+    {
+           0.0f, 1.0f,
+            1.0f, 1.0f,
+            0.0f, 0.0f,
+            1.0f, 0.0f
+    };
+    
+    private final String m_vertexShader =
+        "uniform mat4 uMVPMatrix; \n" +
+       "attribute vec4 a_position; \n" +
+       "attribute vec2 a_texCoord;   \n" +
+       "varying vec2 c5;     \n" +
+       "void main(){              \n" +
+       " gl_Position = uMVPMatrix * a_position; \n" +
+       " c5 = a_texCoord; \n" +
+       "}\n";
+
+
+        private final String m_fragmentShader =
+            "precision mediump float;\n" +
+            "varying vec2 c5;\n" + 
+            "uniform sampler2D s_texture;\n" +
+            "void main() {\n" +
+            "  gl_FragColor = texture2D(s_texture, c5);\n" +
+       //   "  gl_FragColor = vec4(0.5, 0.3, 0.9, 1.0);\n" +
+            "}\n";
+        private static String TAG = "glBoy Renderer";
+}
index 5c8413c125260ae0b35d690fcbb7782b8a08dbed..7cca0080cd99e0391be04a51f339d6befe28db33 100644 (file)
@@ -1,7 +1,6 @@
 # glBoy example configuration file
 
 TODO list available keys here in a comment.
 # glBoy example configuration file
 
 TODO list available keys here in a comment.
-change name to ~/.glBoy/config as we need a directory for saved games as well.
 
 glBoy:
 {
 
 glBoy:
 {
diff --git a/hq4x.cc b/hq4x.cc
index 278011c416fc8456ea612036e722443d77219499..e0b0054322935e30f8844de001d143322617edbc 100644 (file)
--- a/hq4x.cc
+++ b/hq4x.cc
 // - 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
 // - 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
 #include "hq4x.hh"\r
 \r
 #include "hq4x.hh"\r
 \r
-#include <GL/glu.h>\r
-#include <GL/glext.h>\r
-\r
 #include <cassert>\r
 #include <cstdint>\r
 #include <cmath>\r
 #include <cassert>\r
 #include <cstdint>\r
 #include <cmath>\r
 \r
 using namespace glBoy;\r
 \r
 \r
 using namespace glBoy;\r
 \r
-namespace\r
-{\r
-\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
-const char*\r
-s_FragmentSource =\r
-"\\r
-// Enable bitwise operators\n\\r
-#version 130\n\\r
-\n\\r
-#ifdef __GLSL_CG_DATA_TYPES\n\\r
-// NVidia specific\n\\r
-// These pragmas make a MASSIVE difference on some quadro cards.\n\\r
-// But no difference on my 6600.\n\\r
-#pragma optionNV(fastmath on)\n\\r
-#pragma optionNV(fastprecision on)\n\\r
-#pragma optionNV(ifcvt none)\n\\r
-#pragma optionNV(inline all)\n\\r
-#pragma optionNV(strict on)\n\\r
-#pragma optionNV(unroll all)\n\\r
-#endif\n\\r
-\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
-void abortGLError()\r
-{\r
-       std::cerr << "Fatal OpenGL error in hq4x shader: " <<\r
-               gluErrorString(glGetError()) << std::endl;\r
-       abort();\r
-}\r
-\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
+Hq4x::Hq4x()\r
 {\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
-\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
 }\r
 \r
 //--------------------------------------------------\r
@@ -548,10 +273,10 @@ inline void Interp8(uint32_t& c1, uint32_t& c2)
 \r
 \r
 \r
 \r
 \r
 \r
-uint32_t*\r
+Hq4x::Pixel*\r
 Hq4x::getHq4xLookupTable() const\r
 {\r
 Hq4x::getHq4xLookupTable() const\r
 {\r
-       static uint32_t* s_table(0);\r
+       static Pixel* s_table(0);\r
 \r
        if (s_table)\r
        {\r
 \r
        if (s_table)\r
        {\r
@@ -559,7 +284,7 @@ Hq4x::getHq4xLookupTable() const
        }\r
        else\r
        {\r
        }\r
        else\r
        {\r
-               s_table = new uint32_t[4096*48];\r
+               s_table = new Pixel[4096*48];\r
 \r
                for (int pattern = 0; pattern < 256; ++pattern)\r
                {\r
 \r
                for (int pattern = 0; pattern < 256; ++pattern)\r
                {\r
@@ -5554,29 +5279,20 @@ Hq4x::getHq4xLookupTable() const
                                                        );\r
 \r
 \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
+                                               s_table[index].r = w.w1;\r
+                                               s_table[index].g = w.w2;\r
+                                               s_table[index].b = w.w3;\r
+                                               s_table[index].a = 0;\r
 \r
 \r
-                                               s_table[index + 2] =\r
-                                                       (w.w7 << 0) |\r
-                                                       (w.w8 << 8) |\r
-                                                       (w.w9 << 16);\r
+                                               s_table[index + 1].r = w.w4;\r
+                                               s_table[index + 1].g = w.w5;\r
+                                               s_table[index + 1].b = w.w6;\r
+                                               s_table[index + 1].a = 0;\r
 \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
+                                               s_table[index + 2].r = w.w7;\r
+                                               s_table[index + 2].g = w.w8;\r
+                                               s_table[index + 2].b = w.w9;\r
+                                               s_table[index + 2].a = 0;\r
                                        }\r
                                }\r
                        }\r
                                        }\r
                                }\r
                        }\r
diff --git a/hq4x.hh b/hq4x.hh
index 1242141526faf7a72c501eef8b3957bef788a27e..70cb4dae232833198f89e03822baf5e096492084 100644 (file)
--- a/hq4x.hh
+++ b/hq4x.hh
 
 #include <cstdint>
 
 
 #include <cstdint>
 
-#include <GL/gl.h>
-
 namespace glBoy
 {
        class Hq4x
        {
        public:
 namespace glBoy
 {
        class Hq4x
        {
        public:
-               Hq4x(size_t textureWidth, size_t textureHeight);
+               Hq4x();
 
 
-               void prepareShader(GLuint texture);
+               struct Pixel
+               {
+                       uint8_t b;
+                       uint8_t g;
+                       uint8_t r;
+                       uint8_t a;
+               };
+               Pixel* getHq4xLookupTable() const;
 
        private:
                enum Diff { Diff_2_6 = 1, Diff_6_8 = 2, Diff_8_4 = 4, Diff_4_2 = 8 };
 
        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;
        };
 }
 
        };
 }
 
index f275ba42be259b90a32a5b7f9399bc175c6e4c4e..b9f320b74352a88649f498af9a9fc608fa45d558 160000 (submodule)
--- a/libzipper
+++ b/libzipper
@@ -1 +1 @@
-Subproject commit f275ba42be259b90a32a5b7f9399bc175c6e4c4e
+Subproject commit b9f320b74352a88649f498af9a9fc608fa45d558
index d6f8847f9741e62f05fc2c2f56c3271343bddf92..d27bd5a13e93d9bb2e868cbb2cab6864c77b5de1 100644 (file)
@@ -58,7 +58,7 @@ while ($node)
                }
                elsif ($reader->name eq "instruction")
                {
                }
                elsif ($reader->name eq "instruction")
                {
-                       processInstruction($subtree)
+                       expandInstruction($subtree)
                }
                else
                {
                }
                else
                {
@@ -133,31 +133,22 @@ sub processRegisterMask
        my $max = 1 << $maskBits;
        my $count = 0;
 
        my $max = 1 << $maskBits;
        my $count = 0;
 
-       print TABLES "static size_t regMask_$name\[$max\] =\n{\n";
-
-       my @masks = ();
+       my %masks = ();
 
        while ($count < $max)
        {
                my $mask = sprintf("%0$maskBits"."b", $count);
                my $reg = $node->findvalue("./reg[\@mask='$mask']/\@name");
 
 
        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)
                {
                if ($reg && length $reg != 0)
                {
-                       print TABLES "\toffsetof(glBoy::Registers, $reg) / $regBytes";
-                       push @masks, $mask;
-               }
-               else
-               {
-                       print TABLES "\tsize_t(-1)";
+                       $masks{$mask} = $reg;
                }
 
                $count = $count + 1;
        }
                }
 
                $count = $count + 1;
        }
-       print TABLES "\n};\n\n";
 
 
-       $RegMasks{$name}{'masks'} = \@masks;
+       $RegMasks{$name}{'masks'} = \%masks;
        $RegMasks{$name}{'maskBits'} = $maskBits;
        $RegMasks{$name}{'regBytes'} = $regBytes;
 
        $RegMasks{$name}{'maskBits'} = $maskBits;
        $RegMasks{$name}{'regBytes'} = $regBytes;
 
@@ -187,124 +178,110 @@ sub processBitMask
        $BitMasks{$name}{'maskBits'} = $maskBits;
 }
 
        $BitMasks{$name}{'maskBits'} = $maskBits;
 }
 
-sub expandMask
+sub expandInstruction
 {
 {
+       my $node = shift;
+       my $name = shift;
        my $mask = shift;
        my $mask = shift;
+       my $code = shift;
 
 
-       my @result = ();
+       if (!$code)
+       {
+               $code = "";
+       }
 
 
-       if ($mask =~ /([a-zA-Z])/)
+       if (!$name)
+       {
+               $name = $node->findvalue('./@name') || die;
+       }
+       my $clock = $node->findvalue('./@clock');
+       if (!$mask)
        {
        {
-               my $match = $1;
+               $mask = $node->findvalue('./opcode/@mask');
+       }
 
 
-               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);
-                       }
+       my $prefix = $node->findvalue('./opcode/@prefix');
+       my $operand = $node->findvalue('./opcode/@operand');
+       my $signedOperand = $node->findvalue('./opcode/@signed_operand');
+       my $variant = $node->findvalue('./@variant');
 
 
-               }
-               $mask =~ s/$match/_/;
+       if ($mask !~ /[a-zA-Z]/)
+       {
+               $name =~ s/'//g;
+               processInstruction($node, $name, $mask, $code)
        }
        else
        {
                my $pos = 0;
        }
        else
        {
                my $pos = 0;
-               my $value = 0;
-
-               if (length $mask != 8) { croak "Bad Mask $mask\n"; }
-               while ($pos < 8)
+               my $bitPos = 0;
+               while ($pos < length $mask)
                {
                        my $char = substr($mask, $pos, 1);
                {
                        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
+                       if ($char =~ /[a-zA-Z]/ && $RegMasks{$char})
                        {
                        {
-                               $seenChars{$outputName} = 1;
+                               my $outputName = $char;
+
+                               my $maskBits = $RegMasks{$char}{'maskBits'};
+                               my $regBytes = $RegMasks{$char}{'regBytes'};
+
+                               foreach my $bits (keys %{$RegMasks{$char}{'masks'}})
+                               {
+                                       my $reg = ${$RegMasks{$char}{'masks'}}{$bits};
+                                       my $var = $char;
+                                       if($code =~ /uint8_t& $char/)
+                                       {
+                                               $var = $char."2";
+                                       }
+                                       my $nextMask = $mask;
+                                       $nextMask =~ s/$char/$bits/;
+                                       my $nextName = $name;
+                                       $nextName =~ s/$char/$reg/;
+                                       my $nextCode = $code;
+                                       if ($regBytes == 1)
+                                       {
+                                               $nextCode .= "uint8_t& $var(m_reg.$reg);\n";
+                                       }
+                                       else
+                                       {
+                                               $nextCode .= "DWORD& $var(m_reg.$reg);\n";
+                                       }
+
+                                       expandInstruction($node, $nextName, $nextMask, $nextCode);
+                               }
+                               last;
                        }
                        }
-
-                       push @dbgResult, $outputName;
-
-                       my $maskBits = $RegMasks{$char}{'maskBits'};
-                       my $shiftNum = 8 - ($bitPos + $RegMasks{$char}{'maskBits'});
-                       if ($RegMasks{$char}{'regBytes'} == 1)
+                       elsif ($char =~ /[a-zA-Z]/ && $BitMasks{$char})
                        {
                        {
-                               print CODE "\tuint8_t& $outputName(m_reg.begin8[regMask_$char\[(opcode >> $shiftNum) & (uint8_t(-1) >> (8 - $maskBits)) \]\]);\n";
+                               my $maskBits = $BitMasks{$char}{'maskBits'};
+                               my $bitCount = 0;
+                               foreach my $bits (@{$BitMasks{$char}{'masks'}})
+                               {
+                                       my $nextMask = $mask;
+                                       $nextMask =~ s/$char/$bits/;
+                                       my $nextName = $name;
+                                       $nextName =~ s/$char/$bitCount/;
+                                       my $nextCode = $code;
+                                       $nextCode .= "uint8_t $char($bitCount);\n";
+                                       $bitCount = $bitCount + 1;
+                                       expandInstruction($node, $nextName, $nextMask, $nextCode);
+                               }
+
+                               last;
                        }
                        }
-                       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;
                }
                }
-
-               $pos = $pos + 1;
        }
        }
-       return @dbgResult;
 }
 
 sub processInstruction
 {
        my $node = shift;
 }
 
 sub processInstruction
 {
        my $node = shift;
+       my $name = shift;
+       my $mask = shift;
+       my $initialCode = shift;
 
 
-       my $name = $node->findvalue('./@name') || die;
        my $clock = $node->findvalue('./@clock');
        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 $prefix = $node->findvalue('./opcode/@prefix');
        my $operand = $node->findvalue('./opcode/@operand');
        my $signedOperand = $node->findvalue('./opcode/@signed_operand');
@@ -325,12 +302,20 @@ sub processInstruction
        $label =~ tr/ ,'+()/______/;
        print CODE "$label:\n";
        print CODE "{\n";
        $label =~ tr/ ,'+()/______/;
        print CODE "$label:\n";
        print CODE "{\n";
+       print CODE $initialCode;
 
 
+       my $trace = "\tdbg << \"$name\t\";\n";
+       if (length $name < 10)
+       {
+               $trace .= "\tdbg << \"\t\";\n";
+
+       }
 
        # Note: May have both signed operand (index offset) and operand
        if ($signedOperand && length $signedOperand == 1)
        {
                print CODE "\tint8_t $signedOperand(op8());\n";
 
        # Note: May have both signed operand (index offset) and operand
        if ($signedOperand && length $signedOperand == 1)
        {
                print CODE "\tint8_t $signedOperand(op8());\n";
+               $trace .= "\tdbg << \" $signedOperand=\" << hex($signedOperand);\n";
        }
 
        if ($operand && length $operand == 1)
        }
 
        if ($operand && length $operand == 1)
@@ -338,30 +323,21 @@ sub processInstruction
                if (length($prefix) > 4)
                {
                        print CODE "\t uint8_t& $operand(doublePrefixOperand);\n";
                if (length($prefix) > 4)
                {
                        print CODE "\t uint8_t& $operand(doublePrefixOperand);\n";
+                       $trace .= "\tdbg << \" $operand=\" << hex($operand);\n";
                }
                elsif (uc($operand) eq $operand)
                {
                        print CODE "\tDWORD $operand(op16());\n";
                }
                elsif (uc($operand) eq $operand)
                {
                        print CODE "\tDWORD $operand(op16());\n";
+                       $trace .= "\tdbg << \" $operand=\" << $operand;\n";
                }
                else
                {
                        print CODE "\tuint8_t $operand(op8());\n";
                }
                else
                {
                        print CODE "\tuint8_t $operand(op8());\n";
+                       $trace .= "\tdbg << \" $operand=\" << hex($operand);\n";
                }
        }
 
                }
        }
 
-       my @dbgRegs = createRegReference($mask);
-
-       print CODE "//#ifdef GLBOY_DEBUG\n";
-       print CODE "std::ostream& trace(std::cerr);//(log(Log::DBG_TRACE));\n".
-               "trace << ".
-               "hex(opcodePC) << \": \";\n".
-               "if (prefix) trace << hex(prefix) << \" \";\n".
-               "trace << hex(opcode) << \" $name\" << ".
-               ($operand ? "\" ($operand=\" << $operand << \")\" << " : "") .
-               ($dbgRegs[0] ? "\" ($dbgRegs[0]=\" << $dbgRegs[0] << \")\" << " : "") .
-               " \"    \" << hex(m_reg.A) << ".
-               "std::endl;\n";
-       print CODE "//#endif\n";
+       print CODE "#ifdef GLBOY_DEBUG\n$trace\n#endif";
 
        my $statements = $node->findvalue('./text()');
        chomp($statements);
 
        my $statements = $node->findvalue('./text()');
        chomp($statements);
@@ -370,22 +346,29 @@ sub processInstruction
        print CODE "\tclock += $clock;\n";
 
        print "Processing $name\n";
        print CODE "\tclock += $clock;\n";
 
        print "Processing $name\n";
-       my @opcodes = expandMask($mask);
 
        if (!$prefix || length $prefix == 0)
        {
                $prefix = "default";
        }
 
 
        if (!$prefix || length $prefix == 0)
        {
                $prefix = "default";
        }
 
-       foreach my $opcode (@opcodes)
+       my $pos = 0;
+       my $value = 0;
+       if (length $mask != 8) { croak "Bad Mask $mask\n"; }
+       while ($pos < 8)
        {
        {
-               if ($Instructions{$prefix}{$opcode})
+               my $char = substr($mask, $pos, 1);
+               if ($char == 1)
                {
                {
-                       die "Duplicate opcode. Prefix = $prefix. Mask = $mask, $name.\n".
-                               "Conflicting label = $Instructions{$prefix}{$opcode}\n";
+                       $value = $value + (1 << (7 - $pos));
                }
                }
-               $Instructions{$prefix}{$opcode} = $label;
+               $pos = $pos + 1;
+       }
+       if ($Instructions{$prefix}{$value})
+       {
+               die "Duplicate opcode. Prefix = $prefix. Mask = $mask, $name.\n";
        }
        }
+       $Instructions{$prefix}{$value} = $label;
 
        print CODE "\n}\n";
        print CODE "goto END_INSTRUCTIONS;\n";
 
        print CODE "\n}\n";
        print CODE "goto END_INSTRUCTIONS;\n";
diff --git a/sdl/GLGraphics.cc b/sdl/GLGraphics.cc
new file mode 100644 (file)
index 0000000..0f74599
--- /dev/null
@@ -0,0 +1,427 @@
+//     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 "GLGraphics.hh"
+#include "Log.hh"
+
+#include <GL/gl.h>
+#include <GL/glext.h>
+#include <SDL/SDL_opengl.h>
+#include <SDL/SDL_video.h>
+
+#include <SDL/SDL_opengl.h>
+
+#include <cassert>
+
+using namespace glBoy;
+
+namespace
+{
+
+const char*
+s_VectorSource =
+"\
+//#version 130\n\
+\
+uniform vec4 mmTextureSize;\n\
+\
+varying vec4 c5;\n\
+\
+void main()\n\
+{\n\
+       c5 = gl_MultiTexCoord0;\n\
+\
+       gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n\
+}\n\
+";
+
+const char*
+s_FragmentSource =
+"\
+// Enable bitwise operators\n\
+#version 130\n\
+\n\
+#ifdef __GLSL_CG_DATA_TYPES\n\
+// NVidia specific\n\
+// These pragmas make a MASSIVE difference on some quadro cards.\n\
+// But no difference on my 6600.\n\
+#pragma optionNV(fastmath on)\n\
+#pragma optionNV(fastprecision on)\n\
+#pragma optionNV(ifcvt none)\n\
+#pragma optionNV(inline all)\n\
+#pragma optionNV(strict on)\n\
+#pragma optionNV(unroll all)\n\
+#endif\n\
+\n\
+\
+uniform sampler2D mmTexture;\n\
+uniform sampler2D mmLookup;\n\
+uniform vec4 mmTextureSize;\n\
+\
+in vec4 c5;\n\
+\
+out vec4 fragColour;\n\
+\
+vec4 RGB2YUV(in vec4 val)\n\
+{\n\
+       return mat4(\
+               0.299, 0.587, 0.114, 0,\n\
+               -0.14713, -0.28886, 0.436, 0,\n\
+               0.615, -0.51499, -0.10001, 0,\n\
+               0, 0, 0, 0\n\
+               ) * val;\
+}\n\
+\
+bool Diff(in vec4 YUV1, in vec4 YUV2)\n\
+{\n\
+       return any(\n\
+               greaterThan(\n\
+                       abs(YUV1 - YUV2),\n\
+                       vec4(48.0/255.0, 7.0/255.0, 6.0/255.0, 0)\n\
+                       ).xyz\n\
+               );\n\
+}\n\
+\
+void main()\n\
+{\n\
+       vec4 c_offsetX = vec4(1.0/(mmTextureSize.x-1), 0, 0, 0);\n\
+       vec4 c_offsetY = vec4(0, 1.0/(mmTextureSize.y-1), 0, 0);\n\
+\
+       vec4 w[9];\n\
+       w[0] = texture2DProj(mmTexture, c5 - c_offsetY - c_offsetX);\n\
+       w[1] = texture2DProj(mmTexture, c5 - c_offsetY);\n\
+       w[2] = texture2DProj(mmTexture, c5 - c_offsetY + c_offsetX);\n\
+\
+       w[3] = texture2DProj(mmTexture, c5 - c_offsetX);\n\
+       w[4] = texture2DProj(mmTexture, c5);\n\
+       w[5] = texture2DProj(mmTexture, c5 + c_offsetX);\n\
+\
+       w[6] = texture2DProj(mmTexture, c5 + c_offsetY - c_offsetX);\n\
+       w[7] = texture2DProj(mmTexture, c5 + c_offsetY);\n\
+       w[8] = texture2DProj(mmTexture, c5 + c_offsetY + c_offsetX);\n\
+\
+       vec4 YUV[9];\n\
+       YUV[0] = RGB2YUV(w[0]);\n\
+       YUV[1] = RGB2YUV(w[1]);\n\
+       YUV[2] = RGB2YUV(w[2]);\n\
+       YUV[3] = RGB2YUV(w[3]);\n\
+       YUV[4] = RGB2YUV(w[4]);\n\
+       YUV[5] = RGB2YUV(w[5]);\n\
+       YUV[6] = RGB2YUV(w[6]);\n\
+       YUV[7] = RGB2YUV(w[7]);\n\
+       YUV[8] = RGB2YUV(w[8]);\n\
+\
+       int pattern = 0;\n\
+       int flag = 1;\n\
+       int k;\n\
+       for (k = 0; k < 9; k++)\n\
+       {\n\
+               if (k == 4) continue;\n\
+\
+               if (Diff(YUV[4], YUV[k]))\n\
+               {\n\
+                       pattern = pattern + flag;\n\
+               }\n\
+               flag = flag * 2;\n\
+       }\n\
+\
+       // k is now diff.\n\
+       k = 0;\n\
+       if (Diff(YUV[1], YUV[5]))\n\
+       {\n\
+               k = k + 1;\n\
+       }\n\
+       if (Diff(YUV[5], YUV[7]))\n\
+       {\n\
+               k = k + 2;\n\
+       }\n\
+       if (Diff(YUV[7], YUV[3]))\n\
+       {\n\
+               k = k + 4;\n\
+       }\n\
+       if (Diff(YUV[3], YUV[1]))\n\
+       {\n\
+               k = k + 8;\n\
+       }\n\
+\
+       vec2 lookupMax = vec2(1.0/47.0, 1.0/4095);\n\
+       ivec2 thisPixel = ivec2(fract(c5 * mmTextureSize) * 4.0);\n\
+       ivec2 lookupIndex = ivec2((thisPixel.y*4+thisPixel.x)*3, (pattern * 16) + k);\n\
+       vec4 weight1 = texture2D(mmLookup, lookupIndex * lookupMax);\n\
+       vec4 weight2 = texture2D(mmLookup, (lookupIndex + ivec2(1, 0)) * lookupMax);\n\
+       vec4 weight3 = texture2D(mmLookup, (lookupIndex + vec2(2, 0)) * lookupMax);\n\
+\
+       // Dodgy fix for inaccurate lookups :-( Seemed to work on Quadro NVS 290 without this\n\
+       float allW = dot(\n\
+               vec4(1,1,1,0),\n\
+               vec4(\n\
+                       dot(weight1, vec4(1,1,1,0)),\n\
+                       dot(weight2, vec4(1,1,1,0)),\n\
+                       dot(weight3, vec4(1,1,1,0)),\n\
+                       0\n\
+                       )\n\
+               );\n\
+       fragColour =\n\
+               w[0]*weight1.x + w[1]*weight1.y + w[2]*weight1.z +\n\
+               w[3]*weight2.x + w[4]*weight2.y + w[5]*weight2.z +\n\
+               w[6]*weight3.x + w[7]*weight3.y + w[8]*weight3.z;\n\
+       fragColour = fragColour / allW;\n\
+\
+//if (allW > 1.100) { fragColour =  vec4(0.0,0.0,1.0,0); } \n\
+//if (allW < 0.9) { fragColour =  vec4(0.0,1.0,0.0,0); } \n\
+\
+}\n\
+";
+
+} // namespace
+
+GLGraphics::GLGraphics(
+       Core& core,
+       GameboyGraphics& graphics,
+       bool fullscreen,
+       const double& scaleX,
+       const double& scaleY,
+       PixelScaler scaler,
+       ResampleMethod resampler
+       ) :
+       m_core(core),
+       m_graphics(graphics),
+       m_scaler(scaler),
+       m_resampler(resampler),
+       m_surface(0)
+{
+       initSurface(scaleX, scaleY, fullscreen);
+       initShader();
+}
+
+GLGraphics::~GLGraphics()
+{
+       if (m_surface)
+       {
+               SDL_FreeSurface(m_surface);
+       }
+}
+
+void
+GLGraphics::initSurface(
+       const double& xScale, const double& yScale, bool fullscreen)
+{
+       Info() << "Enabling OpenGL" << Log::endl;
+
+       SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+       SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); //vsync
+
+       unsigned int options(SDL_OPENGL);
+       if (fullscreen)
+       {
+               options |= SDL_FULLSCREEN;
+       }
+
+       m_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();
+
+       SDL_WM_SetCaption("glBoy", "glBoy");
+       // TODO SDL_WM_SetIcon
+}
+
+void
+GLGraphics::initShader()
+{
+       m_vertexShader = glCreateShader(GL_VERTEX_SHADER);
+       m_fragShader = glCreateShader(GL_FRAGMENT_SHADER);
+
+       if (!m_vertexShader || !m_fragShader) openglError();
+
+       glShaderSource(m_vertexShader, 1, &s_VectorSource, NULL);
+       glShaderSource(m_fragShader, 1, &s_FragmentSource, NULL);
+
+       glCompileShader(m_vertexShader);
+       if (glGetError() != GL_NO_ERROR) shaderError(m_vertexShader);
+       glCompileShader(m_fragShader);
+       if (glGetError() != GL_NO_ERROR) shaderError(m_fragShader);
+
+       m_program = glCreateProgram();
+       if (!m_program) openglError();
+
+       glAttachShader(m_program, m_vertexShader);
+       if (glGetError() != GL_NO_ERROR) programError();
+       glAttachShader(m_program, m_fragShader);
+       if (glGetError() != GL_NO_ERROR) programError();
+       glLinkProgram(m_program);
+       if (glGetError() != GL_NO_ERROR) programError();
+
+       glGenTextures(1, &m_lookupTexture);
+       glActiveTexture(GL_TEXTURE0);
+       glBindTexture(GL_TEXTURE_2D, m_lookupTexture);
+
+       // Only nearest neighbour will work
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+       // Create the texture image
+       glTexImage2D(
+               GL_TEXTURE_2D,
+               0,
+               GL_RGBA8,
+               48,
+               4096,
+               0,
+               GL_BGRA, // Must match struct Pixel
+               GL_UNSIGNED_BYTE,
+               m_hq4x.getHq4xLookupTable()
+               );
+
+       glUseProgram(m_program);
+       if (glGetError() != GL_NO_ERROR) programError();
+
+       glActiveTexture(GL_TEXTURE1);
+       glBindTexture(GL_TEXTURE_2D, m_lookupTexture);
+       GLint tex = glGetUniformLocation(m_program, "mmLookup");
+       glUniform1i(tex, 1); // Bind Texture 1
+}
+
+void
+GLGraphics::prepareShader(GLuint texture)
+{
+       glUseProgram(m_program);
+       if (glGetError() != GL_NO_ERROR) programError();
+
+       //glBindFragDataLocationEXT(m_program,0,"fragColor");
+
+       GLint size(glGetUniformLocation(m_program, "mmTextureSize"));
+       glUniform4f(size, 160, 144, 0, 0);
+
+       glActiveTexture(GL_TEXTURE0);
+       glBindTexture(GL_TEXTURE_2D, texture);
+       GLint tex(glGetUniformLocation(m_program, "mmTexture"));
+       glUniform1i(tex, 0); // Bind Texture 0
+}
+
+void
+GLGraphics::render()
+{
+       if (SDL_MUSTLOCK(m_surface))
+       {
+               if (SDL_LockSurface(m_surface) < 0)
+               {
+                       return;
+               }
+       }
+
+       uint8_t textureData[144*160];
+       m_graphics.getGreyscaleFrame(&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
+               );
+
+       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);
+       }
+}
+
+void
+GLGraphics::openglError()
+{
+       Err() <<
+               "Fatal OpenGL error in hq4x shader: " <<
+               gluErrorString(glGetError()) << Log::endl;
+       abort();
+}
+
+void
+GLGraphics::shaderError(GLint shader)
+{
+       char buffer[2048];
+       int len;
+       glGetShaderInfoLog(shader, sizeof(buffer), &len, &buffer[0]);
+
+       Err() <<
+               "Fatal error using hq4x shader: " <<
+               gluErrorString(glGetError()) << ": " <<
+               buffer << Log::endl;
+
+       abort();
+}
+
+void
+GLGraphics::programError()
+{
+       char buffer[2048];
+       int len;
+       glGetProgramInfoLog(m_program, sizeof(buffer), &len, &buffer[0]);
+
+       Err() <<
+               "Fatal error using hq4x program: " <<
+               gluErrorString(glGetError()) << ": " <<
+               buffer << Log::endl;
+
+       abort();
+}
diff --git a/sdl/GLGraphics.hh b/sdl/GLGraphics.hh
new file mode 100644 (file)
index 0000000..b36ec90
--- /dev/null
@@ -0,0 +1,85 @@
+//     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_GLGRAPHICS_HH
+#define GLBOY_GLGRAPHICS_HH
+
+#include "glBoy.hh"
+#include "Core.hh"
+#include "GameboyGraphics.hh"
+
+#include "hq4x.hh"
+
+#include <SDL/SDL.h>
+#include <SDL/SDL_opengl.h>
+
+#include <vector>
+
+#include <pthread.h>
+
+namespace glBoy
+{
+
+class GLGraphics
+{
+public:
+       enum PixelScaler { Scaler_None, Scaler_Scale2x, Scaler_hq4x };
+       enum ResampleMethod { Resample_Nearest, Resample_Bilinear };
+
+       GLGraphics(
+               Core& core,
+               GameboyGraphics& graphics,
+               bool fullscreen,
+               const double& scaleX,
+               const double& scaleY,
+               PixelScaler scaler,
+               ResampleMethod resampler
+               );
+       ~GLGraphics();
+
+       void render();
+private:
+       void initSurface(
+               const double& xScale, const double& yScale, bool fullscreen);
+       void initShader();
+
+       void openglError();
+       void shaderError(GLint shader);
+       void programError();
+
+       void prepareShader(GLuint texture);
+
+       Core& m_core;
+       GameboyGraphics& m_graphics;
+
+       PixelScaler m_scaler;
+       ResampleMethod m_resampler;
+
+       GLint m_vertexShader;
+       GLint m_fragShader;
+       GLint m_program;
+       GLuint m_lookupTexture;
+       Hq4x m_hq4x;
+
+       // Output device
+       SDL_Surface* m_surface;
+};
+
+} // namespace glBoy
+
+#endif
+
similarity index 80%
rename from Main.cc
rename to sdl/Main.cc
index bd7ae88fed3eab3e7f941063a999f1217df03522..29727ac46af4912e303f0e248057fafd4302c7cf 100644 (file)
--- a/Main.cc
@@ -15,7 +15,6 @@
 //     You should have received a copy of the GNU General Public License
 //     along with glBoy.  If not, see <http://www.gnu.org/licenses/>.
 
 //     You should have received a copy of the GNU General Public License
 //     along with glBoy.  If not, see <http://www.gnu.org/licenses/>.
 
-#define GLX_GLXEXT_PROTOTYPES 1
 #include "glBoy.hh"
 #include "Core.hh"
 #include "GameboyCart.hh"
 #include "glBoy.hh"
 #include "Core.hh"
 #include "GameboyCart.hh"
 #include "GameboyTimer.hh"
 #include "RAM.hh"
 #include "ROM.hh"
 #include "GameboyTimer.hh"
 #include "RAM.hh"
 #include "ROM.hh"
+#include "sdl/GLGraphics.hh"
+#include "sdl/SDLKeys.hh"
+#include "sdl/SDLSound.hh"
 #include "Log.hh"
 
 #include "Log.hh"
 
-#include <GL/glext.h>
 #include <SDL/SDL.h>
 #include <SDL/SDL.h>
-#include <SDL/SDL_opengl.h>
-#include <SDL/SDL_video.h>
 
 #include <libconfig.h++>
 
 #include <libconfig.h++>
-#include <zipper.hh>
 
 #include <cassert>
 #include <set>
 
 #include <cassert>
 #include <set>
@@ -47,6 +45,8 @@
 #include <unistd.h>
 #include <pwd.h>
 
 #include <unistd.h>
 #include <pwd.h>
 
+#include <pthread.h>
+
 namespace
 {
        enum Constants
 namespace
 {
        enum Constants
@@ -58,39 +58,6 @@ namespace
                GLBOY_MAX_HEIGHT = 1440,
        };
 
                GLBOY_MAX_HEIGHT = 1440,
        };
 
-       SDL_Surface*
-       initOpenGL(double xScale, double yScale, bool fullscreen)
-       {
-               glBoy::Info() << "Enabling OpenGL" << glBoy::Log::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)
-                       );
-
-               // v-sync
-               //::glXSwapIntervalSGI(1);
-
-               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()
        {
                std::cout << std::endl <<
        void usage()
        {
                std::cout << std::endl <<
@@ -123,7 +90,7 @@ namespace
                if (entry && entry->pw_dir)
                {
                        std::stringstream file;
                if (entry && entry->pw_dir)
                {
                        std::stringstream file;
-                       file << entry->pw_dir << "/" << ".glBoy";
+                       file << entry->pw_dir << "/.glBoy/config";
                        try
                        {
                                config.readFile(file.str().c_str());
                        try
                        {
                                config.readFile(file.str().c_str());
@@ -150,10 +117,20 @@ namespace
                        }
                }
        }
                        }
                }
        }
+}
 
 
+extern "C"
+{
+       static void* start(void* val)
+       {
+               glBoy::Core* core = reinterpret_cast<glBoy::Core*>(val);
+               core->run();
+               return NULL;
+       }
 }
 
 int main(int argc, char** argv)
 }
 
 int main(int argc, char** argv)
+{
 {
        std::cout << PACKAGE_STRING << " <" << PACKAGE_BUGREPORT << ">" << std::endl;
        libconfig::Config config;
 {
        std::cout << PACKAGE_STRING << " <" << PACKAGE_BUGREPORT << ">" << std::endl;
        libconfig::Config config;
@@ -175,8 +152,8 @@ int main(int argc, char** argv)
        std::string filename("tetris.gb");
        config.lookupValue("glBoy.rom", filename);
 
        std::string filename("tetris.gb");
        config.lookupValue("glBoy.rom", filename);
 
-       glBoy::GameboyGraphics::PixelScaler scaler(glBoy::GameboyGraphics::Scaler_hq4x);
-       glBoy::GameboyGraphics::ResampleMethod resampler(glBoy::GameboyGraphics::Resample_Bilinear);
+       glBoy::GLGraphics::PixelScaler scaler(glBoy::GLGraphics::Scaler_hq4x);
+       glBoy::GLGraphics::ResampleMethod resampler(glBoy::GLGraphics::Resample_Bilinear);
        bool fullscreen(false);
        double scaleX(4.0);
        double scaleY(4.0);
        bool fullscreen(false);
        double scaleX(4.0);
        double scaleY(4.0);
@@ -228,11 +205,11 @@ int main(int argc, char** argv)
                        {
                                if (std::string(optarg) == "bilinear")
                                {
                        {
                                if (std::string(optarg) == "bilinear")
                                {
-                                       resampler = glBoy::GameboyGraphics::Resample_Bilinear;
+                                       resampler = glBoy::GLGraphics::Resample_Bilinear;
                                }
                                else
                                {
                                }
                                else
                                {
-                                       resampler = glBoy::GameboyGraphics::Resample_Nearest;
+                                       resampler = glBoy::GLGraphics::Resample_Nearest;
                                }
                        }; break;
                        case 's':
                                }
                        }; break;
                        case 's':
@@ -250,15 +227,15 @@ int main(int argc, char** argv)
                        {
                                if (std::string(optarg) == "hq4x")
                                {
                        {
                                if (std::string(optarg) == "hq4x")
                                {
-                                       scaler = glBoy::GameboyGraphics::Scaler_hq4x;
+                                       scaler = glBoy::GLGraphics::Scaler_hq4x;
                                }
                                else if (std::string(optarg) == "scale2x")
                                {
                                }
                                else if (std::string(optarg) == "scale2x")
                                {
-                                       scaler = glBoy::GameboyGraphics::Scaler_Scale2x;
+                                       scaler = glBoy::GLGraphics::Scaler_Scale2x;
                                }
                                else
                                {
                                }
                                else
                                {
-                                       scaler = glBoy::GameboyGraphics::Scaler_None;
+                                       scaler = glBoy::GLGraphics::Scaler_None;
                                }
                        }; break;
                        case 'w':
                                }
                        }; break;
                        case 'w':
@@ -294,6 +271,7 @@ int main(int argc, char** argv)
        glBoy::Info() <<
                "Loading cartridge from file " << filename << glBoy::Log::endl;
 
        glBoy::Info() <<
                "Loading cartridge from file " << filename << glBoy::Log::endl;
 
+//TODO catch GameboyCart::exception
        glBoy::GameboyCart cart(core, filename);
        std::stringstream cartInfo;
        cart.dump(cartInfo);
        glBoy::GameboyCart cart(core, filename);
        std::stringstream cartInfo;
        cart.dump(cartInfo);
@@ -309,13 +287,9 @@ int main(int argc, char** argv)
 
        SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO);
 
 
        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);
-
-       SDL_WM_SetCaption("glBoy", "glBoy");
-       // TODO SDL_WM_SetIcon
-
+       glBoy::GameboyGraphics graphics(core);
+       glBoy::GLGraphics renderer(
+               core, graphics, fullscreen, scaleX, scaleY, scaler, resampler);
 
        // Map the standard internal RAM, plus shadow.
        glBoy::MemoryMap& map(core.getMemoryMap());
 
        // Map the standard internal RAM, plus shadow.
        glBoy::MemoryMap& map(core.getMemoryMap());
@@ -328,19 +302,12 @@ int main(int argc, char** argv)
        // Most interaction between GB hardware and the program occurs here.
        {
                std::shared_ptr<glBoy::MemoryMap::Memory> mem(new glBoy::RAM(128));
        // 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.map(0xFF80, 127, mem);
        }
 
        // Map other devices
        std::shared_ptr<glBoy::GameboyJoypad> joypad(
        }
 
        // Map other devices
        std::shared_ptr<glBoy::GameboyJoypad> joypad(
-               new glBoy::GameboyJoypad(core, config)
+               new glBoy::GameboyJoypad(core)
                );
        map.map(0xFF00, 1, joypad);
        std::shared_ptr<glBoy::GameboyTimer> timer(
                );
        map.map(0xFF00, 1, joypad);
        std::shared_ptr<glBoy::GameboyTimer> timer(
@@ -362,11 +329,37 @@ int main(int argc, char** argv)
        core.getRegisters().DE = glBoy::htoz(0x00D8);
        core.getRegisters().HL = glBoy::htoz(0x014D);
 
        core.getRegisters().DE = glBoy::htoz(0x00D8);
        core.getRegisters().HL = glBoy::htoz(0x014D);
 
-       core.run();
+       // Run the core in a new thread.
+       // OpenGL output must be in the main thread.
+       pthread_t thread(
+               pthread_create(&thread, NULL, start, &core));
+       sleep(1);
+
+       glBoy::SDLKeys keys(core, *joypad, config);
+       glBoy::SDLSound sdlSound(*sound);
+
+       timespec loopTimer;
+       while (core.isRunning())
+       {
+               clock_gettime(CLOCK_REALTIME, &loopTimer);
+
+               keys.poll();
+               renderer.render();
+
+               loopTimer.tv_nsec += 8000000; // Cap at 120fps
+               if (loopTimer.tv_nsec > 1e9)
+               {
+                       loopTimer.tv_sec++;
+                       loopTimer.tv_nsec -= 1e9;
+               }
+
+               keys.poll();
+               clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &loopTimer, NULL);
+       }
 
        std::cerr << "Done" << std::endl;
 
 
        std::cerr << "Done" << std::endl;
 
-       SDL_FreeSurface(surface);
+}
        SDL_Quit();
 }
 
        SDL_Quit();
 }
 
diff --git a/sdl/SDLKeys.cc b/sdl/SDLKeys.cc
new file mode 100644 (file)
index 0000000..a258074
--- /dev/null
@@ -0,0 +1,140 @@
+//     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 "SDLKeys.hh"
+
+#include <algorithm>
+#include <cassert>
+#include <cstdlib>
+
+using namespace glBoy;
+
+namespace
+{
+       void setConfiguredKey(
+               const std::map<std::string, int>& keyMap,
+               const libconfig::Config& config,
+               const std::string& configParam,
+               int& mappedKey
+               )
+       {
+               if (config.exists(configParam))
+               {
+                       std::string value;
+                       config.lookupValue(configParam, value);
+                       std::map<std::string, int>::const_iterator it(
+                               keyMap.find(value)
+                               );
+                       if (!value.empty() && it != keyMap.end())
+                       {
+                               mappedKey = it->second;
+                       }
+               }
+       }
+}
+
+SDLKeys::SDLKeys(
+       Core& core,
+       GameboyJoypad& joypad,
+       const libconfig::Config& config
+       ) :
+       m_core(core),
+       m_joypad(joypad)
+{
+       std::map<std::string, int> sdlKeyMap;
+
+       for (int i = SDLK_FIRST; i < SDLK_LAST; ++i)
+       {
+               std::string keyName = SDL_GetKeyName(static_cast<SDLKey>(i));
+               if (keyName.find("SDLK_") == 0)
+               {
+                       keyName = keyName.substr(5);
+               }
+               sdlKeyMap[keyName] = i;
+       }
+
+       m_inputs[DOWN].sdlKey = SDLK_DOWN;
+       //m_inputs[DOWN].sdlJoystick = 0 or none or something;
+       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;
+
+       setConfiguredKey(sdlKeyMap, config, "glBoy.keys.up", m_inputs[UP].sdlKey);
+       setConfiguredKey(sdlKeyMap, config, "glBoy.keys.down", m_inputs[DOWN].sdlKey);
+       setConfiguredKey(sdlKeyMap, config, "glBoy.keys.left", m_inputs[LEFT].sdlKey);
+       setConfiguredKey(sdlKeyMap, config, "glBoy.keys.right", m_inputs[RIGHT].sdlKey);
+       setConfiguredKey(sdlKeyMap, config, "glBoy.keys.start", m_inputs[START].sdlKey);
+       setConfiguredKey(sdlKeyMap, config, "glBoy.keys.select", m_inputs[SELECT].sdlKey);
+       setConfiguredKey(sdlKeyMap, config, "glBoy.keys.a", m_inputs[A].sdlKey);
+       setConfiguredKey(sdlKeyMap, config, "glBoy.keys.b", m_inputs[B].sdlKey);
+
+       poll();
+}
+
+void
+SDLKeys::poll()
+{
+       SDL_Event event;
+
+       while (SDL_PollEvent(&event))
+       {
+               switch(event.type)
+               {
+                       case SDL_KEYUP:
+                       case SDL_KEYDOWN:
+                       {
+                               if (event.key.keysym.sym == SDLK_ESCAPE)
+                               {
+                                       m_core.stop();
+                               }
+
+                               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:
+                               m_core.stop();
+                               break;
+
+                       default:
+                               break;
+               }
+       }
+
+       m_joypad.setInput(
+               m_inputs[UP].depressed,
+               m_inputs[DOWN].depressed,
+               m_inputs[LEFT].depressed,
+               m_inputs[RIGHT].depressed,
+               m_inputs[START].depressed,
+               m_inputs[SELECT].depressed,
+               m_inputs[A].depressed,
+               m_inputs[B].depressed
+               );
+}
+
diff --git a/sdl/SDLKeys.hh b/sdl/SDLKeys.hh
new file mode 100644 (file)
index 0000000..ab6db46
--- /dev/null
@@ -0,0 +1,59 @@
+//     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_SDLKEYS_HH
+#define GLBOY_SDLKEYS_HH
+
+#include "glBoy.hh"
+#include "Core.hh"
+#include "GameboyJoypad.hh"
+
+#include <libconfig.h++>
+#include <SDL/SDL.h>
+
+namespace glBoy
+{
+
+class SDLKeys
+{
+public:
+       SDLKeys(
+               Core& core,
+               GameboyJoypad& joypad,
+               const libconfig::Config& config);
+
+       void poll();
+
+private:
+       Core& m_core;
+       GameboyJoypad& m_joypad;
+
+       struct Input
+       {
+               Input() : sdlKey(0), depressed(false) {}
+               int sdlKey;
+               int sdlJoystick;
+               bool depressed;
+       };
+
+       enum Inputs { UP = 0, DOWN, LEFT, RIGHT, A, B, START, SELECT };
+       Input m_inputs[8];
+};
+
+} // namespace glBoy
+
+#endif
diff --git a/sdl/SDLSound.cc b/sdl/SDLSound.cc
new file mode 100644 (file)
index 0000000..11580b9
--- /dev/null
@@ -0,0 +1,53 @@
+//     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 "SDLSound.hh"
+
+#include <cassert>
+#include <cstdlib>
+#include <cmath>
+
+using namespace glBoy;
+
+extern "C"
+{
+       void mixaudio(void *userData, Uint8 *stream, int len)
+       {
+               GameboySound* ourObject(reinterpret_cast<GameboySound*>(userData));
+               ourObject->mixSounds(reinterpret_cast<int16_t*>(stream), len / 2);
+       }
+}
+
+SDLSound::SDLSound(GameboySound& gameboySound)
+{
+       SDL_AudioSpec fmt;
+
+       fmt.freq = GameboySound::SAMPLE_RATE;
+       fmt.format = AUDIO_S16SYS;
+       fmt.channels = 1;
+       fmt.samples = 512;
+       fmt.callback = mixaudio;
+       fmt.userdata = &gameboySound;
+
+       if ( SDL_OpenAudio(&fmt, NULL) < 0 )
+       {
+               assert(false);
+       }
+       SDL_PauseAudio(0);
+}
+
diff --git a/sdl/SDLSound.hh b/sdl/SDLSound.hh
new file mode 100644 (file)
index 0000000..0b3eae8
--- /dev/null
@@ -0,0 +1,40 @@
+//     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_SDLSOUND_HH
+#define GLBOY_SDLSOUND_HH
+
+#include "glBoy.hh"
+#include "GameboySound.hh"
+
+#include <SDL/SDL.h>
+
+namespace glBoy
+{
+
+class SDLSound
+{
+public:
+       SDLSound(GameboySound& gameboySound);
+
+       void mixSounds(Uint8* stream, int len);
+private:
+};
+
+} // namespace glBoy
+
+#endif