From: Michael McMaster Date: Thu, 1 Sep 2011 07:06:35 +0000 (+1000) Subject: Taking a backup before including libzipper submodule X-Git-Url: http://git.codesrc.com/gitweb.cgi?a=commitdiff_plain;h=b209d2e40d76a6b095d7ce14f2ae9f63e6324ebb;p=glBoy.git Taking a backup before including libzipper submodule --- 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";