From b209d2e40d76a6b095d7ce14f2ae9f63e6324ebb Mon Sep 17 00:00:00 2001 From: Michael McMaster Date: Thu, 1 Sep 2011 17:06:35 +1000 Subject: [PATCH] Taking a backup before including libzipper submodule --- .gitignore | 2 +- ALU.hh | 0 Config.cc | 291 ++++++++++++++++++++++++++++++++++++++++++ GameboyCart.cc | 12 +- GameboyCart.hh | 3 +- GameboyJoypad.cc | 52 +++++++- GameboyJoypad.hh | 4 +- InstructionSet.opcode | 0 Main.cc | 198 ++++++++++++++++++++++++---- Makefile | 71 ----------- Makefile.am | 92 +++++++++++++ MemoryMap.cc | 8 +- MemoryMap.hh | 3 +- NEWS | 8 ++ README | 2 +- Unzip.cc | 129 +++++++++++++++++++ VERSION | 2 +- autogen.sh | 2 + configure.ac | 43 +++++++ glBoy.config | 27 ++++ glBoy.hh | 2 + hq4x.cc | 13 ++ makeOpcodes.pl | 2 +- 23 files changed, 855 insertions(+), 111 deletions(-) mode change 100755 => 100644 ALU.hh create mode 100644 Config.cc mode change 100755 => 100644 InstructionSet.opcode delete mode 100644 Makefile create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 Unzip.cc create mode 100755 autogen.sh create mode 100644 configure.ac create mode 100644 glBoy.config diff --git a/.gitignore b/.gitignore index 7533894..e25d00d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,6 @@ *.bin InstructionSet_Opcodes.cc InstructionSet_Tables.cc -awesomeBoy +glBoy test diff --git a/ALU.hh b/ALU.hh old mode 100755 new mode 100644 diff --git a/Config.cc b/Config.cc new file mode 100644 index 0000000..3ced34d --- /dev/null +++ b/Config.cc @@ -0,0 +1,291 @@ +// Copyright (C) 2011 Michael McMaster +// +// 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 . + + +#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 +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +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 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 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 mem(new glBoy::RAM(128)); + map.map(0xFF80, 128, mem); + } + + // Map the Interrupt flags. Currently handled in Core.cc + { + std::shared_ptr mem(new glBoy::RAM(1)); + mem->write8(0, 0); + map.map(0xFF0F, 1, mem); + } + + // Map other devices + std::shared_ptr joypad(new glBoy::GameboyJoypad(core)); + map.map(0xFF00, 1, joypad); + std::shared_ptr 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 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/GameboyCart.cc b/GameboyCart.cc index f0566ec..28badeb 100644 --- a/GameboyCart.cc +++ b/GameboyCart.cc @@ -30,7 +30,7 @@ namespace class MBC1 : public MemoryMap::Memory { public: - MBC1(GameboyCart* parent, uint8_t* begin, uint8_t* end) : + MBC1(GameboyCart* parent, const uint8_t* begin, const uint8_t* end) : m_parent(parent), m_rom(begin, end), m_mode(ROM_SELECT), @@ -117,11 +117,11 @@ assert(m_romOffset < m_rom.size()); } GameboyCart::GameboyCart( - Core& core, uint8_t* rom, size_t size + Core& core, const std::vector& rom ) : m_core(core) { - if (size < 16384) + if (rom.size() < 16384) { std::cerr << "Invalid ROM (too small)" << std::endl; abort(); @@ -137,7 +137,7 @@ GameboyCart::GameboyCart( mbc = "None"; shared_ptr mem( - new MBC1(this, rom, rom + size) + new MBC1(this, &rom[0], &rom[0] + rom.size()) ); /* shared_ptr mem( new ROM(rom, size) @@ -152,7 +152,7 @@ GameboyCart::GameboyCart( { mbc = "MBC1"; shared_ptr mem( - new MBC1(this, rom, rom + size) + new MBC1(this, &rom[0], &rom[0] + rom.size()) ); m_core.getMemoryMap().map(0, 0x8000, mem); m_romBanks.push_back(mem); @@ -209,7 +209,7 @@ GameboyCart::GameboyCart( cerr << "Invalid RAM size" << endl; } - string title(rom + 0x134, rom + 0x134 + 11); + string title(&rom[0x134], &rom[0x134 + 11]); cout << "Loading Cart" << endl; cout << "\tTitle:\t\t" << title << endl; cout << "\tRegion:\t\t" << (rom[0x14A] ? "Worldwide" : "Japan") << endl; diff --git a/GameboyCart.hh b/GameboyCart.hh index d674931..0588cd1 100644 --- a/GameboyCart.hh +++ b/GameboyCart.hh @@ -20,6 +20,7 @@ #include "glBoy.hh" #include "Core.hh" +#include namespace glBoy { @@ -27,7 +28,7 @@ namespace glBoy class GameboyCart { public: - GameboyCart(Core& core, uint8_t* rom, size_t size); + GameboyCart(Core& core, const std::vector& rom); void setRamBank(size_t bank); private: diff --git a/GameboyJoypad.cc b/GameboyJoypad.cc index 96cb681..73868dd 100644 --- a/GameboyJoypad.cc +++ b/GameboyJoypad.cc @@ -27,11 +27,47 @@ using namespace glBoy; // TODO implement interrupt! +namespace +{ + void setConfiguredKey( + const std::map& keyMap, + const libconfig::Config& config, + const std::string& configParam, + int& mappedKey + ) + { + if (config.exists(configParam)) + { + std::string value; + config.lookupValue(configParam, value); + std::map::const_iterator it( + keyMap.find(value) + ); + if (!value.empty() && it != keyMap.end()) + { + mappedKey = it->second; + } + } + } +} -GameboyJoypad::GameboyJoypad(Core& core) : +GameboyJoypad::GameboyJoypad(Core& core, const libconfig::Config& config) : m_state(Joypad_Off) { + std::map sdlKeyMap; + + for (int i = SDLK_FIRST; i < SDLK_LAST; ++i) + { + std::string keyName = SDL_GetKeyName(static_cast(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; @@ -40,6 +76,15 @@ GameboyJoypad::GameboyJoypad(Core& core) : 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; @@ -104,6 +149,11 @@ GameboyJoypad::pollInput(Core::CallbackId) 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) diff --git a/GameboyJoypad.hh b/GameboyJoypad.hh index 8fafbfd..8662875 100644 --- a/GameboyJoypad.hh +++ b/GameboyJoypad.hh @@ -23,6 +23,7 @@ #include "MemoryMap.hh" +#include #include namespace glBoy @@ -31,7 +32,7 @@ namespace glBoy class GameboyJoypad : public MemoryMap::Memory { public: - GameboyJoypad(Core& core); + GameboyJoypad(Core& core, const libconfig::Config& config); void pollInput(Core::CallbackId id); @@ -46,6 +47,7 @@ private: { Input() : sdlKey(0), depressed(false) {} int sdlKey; + int sdlJoystick; bool depressed; }; diff --git a/InstructionSet.opcode b/InstructionSet.opcode old mode 100755 new mode 100644 diff --git a/Main.cc b/Main.cc index b4db93c..c8c2786 100644 --- a/Main.cc +++ b/Main.cc @@ -15,7 +15,7 @@ // You should have received a copy of the GNU General Public License // along with glBoy. If not, see . - +#define GLX_GLXEXT_PROTOTYPES 1 #include "glBoy.hh" #include "Core.hh" #include "GameboyCart.hh" @@ -27,10 +27,14 @@ #include "ROM.hh" #include "Log.hh" +#include #include #include #include +#include +#include + #include #include #include @@ -41,9 +45,19 @@ #include #include #include +#include namespace { + enum Constants + { + GLBOY_MIN_WIDTH = 16, + GLBOY_MAX_WIDTH = 1600, + + GLBOY_MIN_HEIGHT = 14, + GLBOY_MAX_HEIGHT = 1440, + }; + SDL_Surface* initOpenGL(double xScale, double yScale, bool fullscreen) { @@ -61,6 +75,9 @@ namespace 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); @@ -94,10 +111,123 @@ namespace "\t--resample-method\n\t\t\t(Default=\"bilinear\")" << std::endl << "\t\tSets the resampling method." << std::endl;*/ } + + void readConfigFile(libconfig::Config& config) + { + // Grab the current user's home dir + struct passwd* entry(getpwuid(getuid())); + if (entry && entry->pw_dir) + { + std::stringstream file; + file << entry->pw_dir << "/" << ".glBoy"; + try + { + config.readFile(file.str().c_str()); + } + catch (libconfig::FileIOException& e) + { + // Ignore. config file probably doesn't exist. + } + catch (libconfig::ParseException& e) + { + std::cerr << "Error while parsing file " << + file.str() << std::endl << + e.getError() << " at line " << e.getLine() << + std::endl; + + // Continue with default options. + } + catch (libconfig::ConfigException& e) + { + std::cerr << "Error while reading config file " << + file.str() << " : " << std::endl; + + // Continue with default options. + } + } + } + + std::vector readCart(const std::string& filename) + { + std::vector result; + + // Map the cart memory. + int fd(open(filename.c_str(), O_RDONLY)); + if (fd <= 0) + { + char* err(strerror(errno)); + std::cerr << "Failed to load file " << filename << std::endl << + err << std::endl; + exit(1); + } + + struct stat buf; + if (fstat(fd, &buf)) + { + assert(!"Failed fstat"); + } + + if (buf.st_size > 1024*1024) + { + // Probably not a rom at all. + std::cerr << "Cart too large" << std::endl; + exit(1); + } + + char zipHeader[2] = {'\0', '\0'}; + read(fd, zipHeader, sizeof(zipHeader)); + lseek(fd, 0, SEEK_SET); + + if (zipHeader[0] == 'P' && zipHeader[1] == 'K') + { + // Unzip + using namespace zipios; + try + { + ZipFile zf(filename); + ConstEntries entries(zf.entries()); + if (!entries.empty()) + { + glBoy::Log::Instance()(glBoy::Log::DBG_STAT) << + "Uncompressing " << entries.front()->getName() << std::endl; + std::auto_ptr is( + zf.getInputStream(entries.front()) + ); +std::cerr << "*" << std::endl; + result.resize(entries.front()->getSize()); +std::cerr << "*" << std::endl; + is->rdbuf()->sgetn( + reinterpret_cast(&result[0]), + result.size() + ); +std::cerr << "*" << std::endl; + } + } catch (std::exception& e) + { + std::cerr << "Error while reading file " << filename << + std::endl << + e.what() << std::endl; + } + } + else + { + result.resize(buf.st_size); + read(fd, &result[0], result.size()); + } + close(fd); + + return result; + } + } int main(int argc, char** argv) { + std::cout << PACKAGE_STRING << " <" << PACKAGE_BUGREPORT << ">" << std::endl; + libconfig::Config config; + config.setAutoConvert(true); // Allow conversions between int/float + readConfigFile(config); + static struct option longOptions[] = { {"video", 1, NULL, 'v'}, @@ -110,12 +240,33 @@ int main(int argc, char** argv) }; 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); bool fullscreen(false); double scaleX(4.0); double scaleY(4.0); + if (config.exists("glBoy.screen.width")) + { + int width(0); + config.lookupValue("glBoy.screen.width", width); + if (width >= GLBOY_MIN_WIDTH && width <= GLBOY_MAX_WIDTH) + { + scaleX = width / 160.0; + } + } + if (config.exists("glBoy.screen.height")) + { + int height(0); + config.lookupValue("glBoy.screen.height", height); + if (height >= GLBOY_MIN_HEIGHT && height <= GLBOY_MAX_HEIGHT) + { + scaleY = height / 144.0; + } + } + int optionIndex(0); int c; while ( @@ -129,7 +280,9 @@ int main(int argc, char** argv) int height(160); std::stringstream convert(optarg); convert >> height; - if (convert && height > 14 && height < 1440) + if (convert && + height >= GLBOY_MIN_HEIGHT && height <= GLBOY_MAX_HEIGHT + ) { scaleY = height / 144.0; } @@ -176,7 +329,9 @@ int main(int argc, char** argv) int width(160); std::stringstream convert(optarg); convert >> width; - if (convert && width > 16 && width < 1600) + if (convert && + width >= GLBOY_MIN_WIDTH && width <= GLBOY_MAX_WIDTH + ) { scaleX = width / 160.0; } @@ -197,6 +352,14 @@ int main(int argc, char** argv) 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()); @@ -206,29 +369,12 @@ int main(int argc, char** argv) glBoy::GameboyGraphics graphics(core, surface, scaler, resampler); - // Map the cart memory. - int fd(open(filename.c_str(), O_RDONLY)); - assert(fd >= 0); + SDL_WM_SetCaption("glBoy", "glBoy"); + // TODO SDL_WM_SetIcon - 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); + std::vector cart(readCart(filename)); + glBoy::GameboyCart tmp(core, cart); - 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 biosMem(new glBoy::ROM(bios, 256)); - map.map(0, 256, biosMem); -*/ // Map the cart mem. // Map the standard internal RAM, plus shadow. @@ -252,7 +398,9 @@ int main(int argc, char** argv) } // Map other devices - std::shared_ptr joypad(new glBoy::GameboyJoypad(core)); + std::shared_ptr joypad( + new glBoy::GameboyJoypad(core, config) + ); map.map(0xFF00, 1, joypad); std::shared_ptr timer( new glBoy::GameboyTimer( diff --git a/Makefile b/Makefile deleted file mode 100644 index a348a23..0000000 --- a/Makefile +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (C) 2011 Michael McMaster -# -# 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 . - -VERSION=$(shell cat VERSION) - -all: glBoy - -OBJS = \ - ALU.o \ - Core.o \ - GameboyCart.o \ - GameboyGraphics.o \ - GameboyJoypad.o \ - GameboySound.o \ - GameboyTimer.o \ - hq4x.o \ - Log.o \ - Main.o \ - MemoryMap.o \ - RAM.o \ - Registers.o \ - ROM.o \ - -DEFINES= -# -DGLBOY_DEBUG - -CPPFLAGS=-DHOST_LITTLE_ENDIAN -I. $(DEFINES) -CXXFLAGS=-g -O3 -march=native -W -Wall -Werror -std=c++0x - -LDFLAGS=-lSDL -lGL -lGLU -lglut - -glBoy: InstructionSet_Opcodes.cc $(OBJS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@ $(OBJS) - -InstructionSet_Opcodes.cc: InstructionSet.opcode makeOpcodes.pl - perl ./makeOpcodes.pl - -Core.o: InstructionSet_Opcodes.cc - -test: InstructionSet_Opcodes.cc Test.cc ALU.cc - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ Test.cc ALU.cc - -package: - git commit -a - git tag -a -f $(VERSION) - -mkdir /tmp/glBoy-$(VERSION) - -rm /tmp/glBoy-$(VERSION)/* - cp *.cc *.hh *.opcode *.pl Makefile COPYING README VERSION /tmp/glBoy-$(VERSION)/ - tar cvf glBoy-$(VERSION).tar -C /tmp glBoy-$(VERSION) - gzip glBoy-$(VERSION).tar - -clean: - -rm -f *.o - -rm -f glBoy - -rm -f InstructionSet_Opcodes.cc - -rm -f InstructionSet_Tables.cc - -rm -f gmon.out diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..2d21f24 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,92 @@ +# Copyright (C) 2011 Michael McMaster +# +# 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 . + +check_PROGRAMS = \ + test_alu + +TESTS = $(check_PROGRAMS) + +test_alu_SOURCES = Test.cc ALU.cc + +dist_noinst_SCRIPTS = autogen.sh + +EXTRA_DIST = \ + configure.ac \ + glBoy.config \ + InstructionSet.opcode \ + Makefile.am \ + COPYING \ + NEWS \ + README \ + VERSION + +bin_PROGRAMS = glBoy + +glBoy_SOURCES = \ + ALU.hh \ + ALU.cc \ + Core.hh \ + Core.cc \ + DAA.hh \ + DWORD.hh \ + GameboyCart \ + GameboyCart.cc \ + GameboyGraphics.hh \ + GameboyGraphics.cc \ + GameboyJoypad.hh \ + GameboyJoypad.cc \ + GameboySound.hh \ + GameboySound.cc \ + GameboyTimer.hh \ + GameboyTimer.cc \ + glBoy.hh \ + hq4x.hh \ + hq4x.cc \ + Log.hh \ + Log.cc \ + Main.cc \ + MemoryMap.hh \ + MemoryMap.cc \ + RAM.hh \ + RAM.cc \ + Registers.hh \ + Registers.cc \ + ROM.hh \ + ROM.cc \ + util.hh + +BUILT_SOURCES = \ + InstructionSet_Opcodes.cc + +nodist_glBoy_SOURCES = + InstructionSet_Opcodes.cc + +CLEANFILES = \ + InstructionSet_Opcodes.cc \ + InstructionSet_Tables.cc + +glBoy_LDFLAGS = -lGL -lGLU -lzipios + +CXXFLAGS=-g -O3 -march=native -W -Wall -Werror -std=c++0x + +InstructionSet_Opcodes.cc: InstructionSet.opcode makeOpcodes.pl + perl $(srcdir)/makeOpcodes.pl $< + +#package: clean +# git commit -a +# git tag -a -f $(VERSION) + diff --git a/MemoryMap.cc b/MemoryMap.cc index 6264756..caf563f 100644 --- a/MemoryMap.cc +++ b/MemoryMap.cc @@ -82,7 +82,7 @@ MemoryMap::remove( } -const MemoryMap::MappedArea& +inline const MemoryMap::MappedArea& MemoryMap::get(uint16_t address) const { @@ -122,6 +122,12 @@ MemoryMap::get(uint16_t address) const } } + return getFailed(address); +} + +const MemoryMap::MappedArea& +MemoryMap::getFailed(uint16_t address) const +{ Log::Instance()(Log::DBG_MEMORY) << "Returning NULL memory for address " << glBoy::hex(address) << std::endl; diff --git a/MemoryMap.hh b/MemoryMap.hh index 7e7eb8c..e01041c 100644 --- a/MemoryMap.hh +++ b/MemoryMap.hh @@ -74,7 +74,8 @@ private: bool operator<(const MappedArea& b) const { return base < b.base; } }; - const MappedArea& get(uint16_t address) const; + inline const MappedArea& get(uint16_t address) const; + const MappedArea& getFailed(uint16_t address) const; typedef std::vector MapType; MapType m_map; diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..455df39 --- /dev/null +++ b/NEWS @@ -0,0 +1,8 @@ +2011-FIXME Version 1.0.1 + - Increased performance for some NVidia Quadro cards (tested with NVS 290) + - Added configurable key mappings. Copy glBoy.config to ~/.glBoy to change + settings. + +2011-01-31 Version 1.0.0 + - Initial release + diff --git a/README b/README index e33c6da..2b3ce97 100644 --- a/README +++ b/README @@ -1,5 +1,5 @@ glBoy -Original gameboy emulator. +Original gameboy emulator by Michael McMaster Usage: Usage: ./glBoy [--scale ratio] [--width w] [--height h] rom.gb diff --git a/Unzip.cc b/Unzip.cc new file mode 100644 index 0000000..2f102cf --- /dev/null +++ b/Unzip.cc @@ -0,0 +1,129 @@ +// Copyright (C) 2011 Michael McMaster +// +// 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 . + +#include "glBoy.hh" + +// Simple Unzip implementation +// Limitations: +// - Does not support spanned archives. +// - Does not support spanned encryption. +// - Does not support zip64 + + +uint32_t +read32(const std::vector& zipData, size_t pos) +{ + // Read 4 bytes in little-endian order. + // Return results in host-endian. + return uint32_t( + zipData[pos] | + (static_cast(zipData[pos+1]) << 8) | + (static_cast(zipData[pos+2]) << 16) | + (static_cast(zipData[pos+3]) << 24) + ); +} + +uint16_t +read16(const std::vector& zipData, size_t pos) +{ + // Read 2 bytes in little-endian order. + // Return results in host-endian. + return uint16_t( + zipData[pos] | + (static_cast(zipData[pos+1]) << 8) | + ); +} + +struct EndCentralDir +{ + size_t centralDirectoryBytes; + size_t centralDirectoryOffset; + size_t centralDirectoryEntries; + std::string zipFileComment; +}; + +TODO lets make this somewhat full-featured. +Accept a CLASS as input, with a readZipBytes virtual method. +And accept a different class with a writeUncompressedBytes method when +uncompressing. +EndCentralDir readEndRecord(const std::vector& zipData) +{ + // Read the end of central directory record. This + // record enables us to find the remainding + // records without searching for record signatures. + + enum + { + MinRecordBytes = 22, // Minimum size with no comment + MaxCommentBytes = 65535, // 2 bytes to store comment length + Signature = 0x06054b50 + }; + + if (zipData.size() < MinRecordBytes) + { + throw ZipFormatException("Too small"); + } + + // Need to search for this record, as it ends in a variable-length + // comment field. Search backwards, with the assumption that the + // comment doesn't exist, or is much smaller than the maximum + // length + size_t end(0); + if (zipData.size() > MinRecordBytes + MaxCommentBytes) + { + end = zipData.size() - MinRecordBytes + MaxCommentBytes; + } + size_t start(zipData.size() - MinRecordBytes); + + bool recordFound(false); + size_t pos(start); + for (; pos >= end; --pos) + { + recordFound = (read32(zipData, pos) == Signature); + break; + } + + if (!recordFound) + { + throw ZipFormatException("ZIP directory records not found"); + } + + if (read16(zipData, pos + 4) != 1) + { + throw ZipUnsupportedException("Spanned disks not supported"); + } + + EndCentralDir result; + result.entralDirectoryBytes = read32(zipData, pos + 12); + result.centralDirectoryOffset = read32(zipData, pos + 16); + result.centralDirectoryEntries = read16(zipData, pos + 10); + + size_t commentLength(read16(zipData, pos + 20)); + size_t commentStart(pos + MinRecordBytes); + if (commentStart + commentLength > zipData.size()) + { + throw ZipFormatException("ZIP comment is too long"); + } + result.zipFileComment = + std::string( + &zipData[commentStart], + &zipData[commentStart + commentLength] + ); + + return result; +} + diff --git a/VERSION b/VERSION index 3eefcb9..7dea76e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.0 +1.0.1 diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..3f026c4 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,2 @@ +#!/bin/sh +autoreconf --force --install diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..0042776 --- /dev/null +++ b/configure.ac @@ -0,0 +1,43 @@ +AC_INIT([glBoy], [1.0.1], [michael@codesrc.com]) +AC_CANONICAL_HOST +AC_CANONICAL_TARGET +AM_INIT_AUTOMAKE([foreign]) +AC_CONFIG_HEADERS([autoconfig.h]) +AC_CONFIG_FILES([Makefile]) + +AC_C_BIGENDIAN( + AC_DEFINE([HOST_BIG_ENDIAN], [], [Compile for big-endian host cpu]), + AC_DEFINE([HOST_LITTLE_ENDIAN], [], [Compile for litte-endian host cpu]) + ) + +AC_PROG_CXX + +SDL_VERSION=1.2.0 +AM_PATH_SDL( + $SDL_VERSION, + :, + AC_MSG_ERROR([*** SDL version $SDL_VERSION not found!]) + ) +CFLAGS="$CFLAGS $SDL_CFLAGS" LIBS="$LIBS $SDL_LIBS" + +#AC_ARG_ENABLE([GLBOY_DEBUG], +# AS_HELP_STRING([--enable-debug_trace], +# [Enable debug trace logging]), +# [AC_DEFINE(GLBOY_DEBUG), +#FIXME +# []) + +PKG_CHECK_MODULES([LIBCONFIGXX], [libconfig++ >= 1.3],, + AC_MSG_ERROR([libconfig++ 1.3 or newer not found.]) +) +CFLAGS="$CFLAGS $LIBCONFIGXX_CFLAGS" LIBS="$LIBS $LIBCONFIGXX_LIBS" + +#AX_CXXFLAGS_GCC_OPTION([-std=c++0x]) +#- warning flags +#- opt flags ? +#- debug flags ? +#GL lib, GLU lib +#flto! + +AC_OUTPUT + diff --git a/glBoy.config b/glBoy.config new file mode 100644 index 0000000..5c8413c --- /dev/null +++ b/glBoy.config @@ -0,0 +1,27 @@ +# 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: +{ + keys: + { + up = "UP"; + down = "DOWN"; + left = "LEFT"; + right = "RIGHT"; + start = "RETURN"; + select = "BACKSPACE"; + a = "z"; + b = "x"; + }; + + screen: + { + width = 160; + height = 144; + }; + + rom = "tetris.gb"; +}; diff --git a/glBoy.hh b/glBoy.hh index 0fb885c..d81ff45 100644 --- a/glBoy.hh +++ b/glBoy.hh @@ -18,6 +18,8 @@ #ifndef GLBOY_HH #define GLBOY_HH +#include "autoconfig.h" + #include #include diff --git a/hq4x.cc b/hq4x.cc index d5521ad..278011c 100644 --- a/hq4x.cc +++ b/hq4x.cc @@ -65,6 +65,19 @@ 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\ diff --git a/makeOpcodes.pl b/makeOpcodes.pl index 2c4c5be..05698d9 100644 --- a/makeOpcodes.pl +++ b/makeOpcodes.pl @@ -31,7 +31,7 @@ my $VARIANT = "gb80"; # -my $reader = new XML::LibXML::Reader(location => "InstructionSet.opcode") +my $reader = new XML::LibXML::Reader(location => $ARGV[0]) || die "Cannot read opcode file\n"; open (TABLES, '>InstructionSet_Tables.cc') || die "Could not write to file\n"; -- 2.38.5