]> localhost Git - glBoy.git/commitdiff
Interim checkin. Bugfixes Galore, plus MBC1,2,3 support.
authorMichael McMaster <michael@codesrc.com>
Sun, 11 Sep 2011 22:58:49 +0000 (08:58 +1000)
committerMichael McMaster <michael@codesrc.com>
Sun, 11 Sep 2011 22:58:49 +0000 (08:58 +1000)
31 files changed:
.gitmodules [new file with mode: 0644]
ALU.hh
Core.cc
Core.hh
GameboyCart.cc
GameboyCart.hh
GameboyGraphics.cc
GameboyJoypad.cc
GameboySound.cc
InstructionSet.opcode
Log.cc
Log.hh
MBC1.cc [new file with mode: 0644]
MBC1.hh [new file with mode: 0644]
MBC2.cc [new file with mode: 0644]
MBC2.hh [new file with mode: 0644]
MBC3.cc [new file with mode: 0644]
MBC3.hh [new file with mode: 0644]
Main.cc
Makefile.am
MemoryMap.cc
NEWS
RAM.hh
RAM_4bit.cc [new file with mode: 0644]
RAM_4bit.hh [new file with mode: 0644]
ROM.cc
Unzip.cc [deleted file]
autogen.sh
configure.ac
libzipper [new submodule]
makeOpcodes.pl

diff --git a/.gitmodules b/.gitmodules
new file mode 100644 (file)
index 0000000..a77e7a7
--- /dev/null
@@ -0,0 +1,3 @@
+[submodule "libzipper"]
+       path = libzipper
+       url = git://www.codesrc.com/git/libzipper
diff --git a/ALU.hh b/ALU.hh
index aa51fda24e0781f4cdfaa4c68cc032834c1ccde7..d71a1e644e3ec5ff90b10e404a9c8fd93df497ba 100644 (file)
--- a/ALU.hh
+++ b/ALU.hh
@@ -32,12 +32,8 @@ extern const uint8_t ParityFlagLookup[256];
 inline uint8_t\r
 add(uint8_t a, uint8_t b, Flags& flags, bool carry = false)\r
 {\r
-       uint8_t result(a + b);\r
-\r
-       if (carry && flags.C)\r
-       {\r
-               ++result;\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
@@ -56,7 +52,7 @@ add(uint8_t a, uint8_t b, Flags& flags, bool carry = false)
                ((((~(a ^ b)) & (result ^ a)) & 0x80) >> 5) |// P\r
 \r
                                                                                                // N\r
-               (((uint16_t(a) + uint16_t(b)) & 0x100) >> 8); // C\r
+               (((uint16_t(a) + uint16_t(b) + c) & 0x100) >> 8); // C\r
 \r
        return result;\r
 }\r
@@ -70,15 +66,11 @@ adc(uint8_t a, uint8_t b, Flags& flags)
 inline DWORD\r
 add(DWORD a, DWORD b, Flags& flags, bool carry = false)\r
 {\r
+       uint8_t c = (carry && flags.C) ? 1 : 0;\r
        const uint16_t aHost(a.host());\r
        const uint16_t bHost(b.host());\r
 \r
-       uint16_t result(aHost + bHost);\r
-\r
-       if (carry && flags.C)\r
-       {\r
-               ++result;\r
-       }\r
+       uint16_t result(aHost + bHost + c);\r
 \r
        if (carry)\r
        {\r
@@ -91,7 +83,7 @@ add(DWORD a, DWORD b, Flags& flags, bool carry = false)
 \r
        flags.N = 0;\r
        flags.H = ((aHost ^ bHost ^ result) >> 10) & 0x1;\r
-       flags.C = (((uint32_t(aHost) + uint32_t(bHost)) & 0x10000) >> 16);\r
+       flags.C = (((uint32_t(aHost) + uint32_t(bHost) + c) & 0x10000) >> 16);\r
 \r
        return glBoy::htoz(result);\r
 }\r
@@ -123,7 +115,7 @@ sub(uint8_t a, uint8_t b, Flags& flags, bool borrow = false)
                        static_cast<int8_t>(result) ? 0 : 0x4) | // P\r
 \r
                0x2 |                                                                           // N\r
-               (((uint16_t(a) - uint16_t(b)) & 0x100) >> 8); // C\r
+               (((uint16_t(a) - uint16_t(b) - c) & 0x100) >> 8); // C\r
 \r
        return result;\r
 }\r
@@ -158,7 +150,7 @@ sub(DWORD a, DWORD b, Flags& flags, bool borrow = false)
                        static_cast<int16_t>(result) ? 0 : 0x4) | // P\r
 \r
                0x2 |                                                                           // N\r
-               (((uint32_t(aHost) - uint32_t(bHost)) & 0x10000) >> 16); // C\r
+               (((uint32_t(aHost) - uint32_t(bHost) - c) & 0x10000) >> 16); // C\r
 \r
        return htoz(result);\r
 }\r
diff --git a/Core.cc b/Core.cc
index 9e8224e62d0aeb0c734c46b42cd18f5a5b5c22a5..8daf450ccd969233b569c7c2c0c2ff0466ff5bd9 100644 (file)
--- a/Core.cc
+++ b/Core.cc
 #include "util.hh"\r
 \r
 #include <cassert>\r
+#include <cstddef>\r
 #include <limits>\r
 #include <sstream>\r
 \r
+#include <string.h> // ffs\r
+\r
 using namespace glBoy;\r
 \r
 Core::Core() :\r
        m_iff1(0),\r
        m_iff2(0),\r
+       m_pendingInterrupts(0),\r
        m_halted(false),\r
        m_clock(0),\r
        m_nextPeriodicCallback(std::numeric_limits<cycle_t>::max())\r
@@ -42,26 +46,15 @@ Core::Core() :
 void\r
 Core::raiseInterrupt(int interrupt)\r
 {\r
-       if (m_iff1 != 0) // Are interrupts enabled ?\r
-       {\r
-               // TODO this interrupt handling is GB80 specific\r
-               bool enabled(false);\r
+       // TODO this interrupt handling is GB80 specific\r
 \r
-               uint8_t flags(m_mem.read8(htoz(0xFFFF)));\r
-               uint8_t outFlags(m_mem.read8(htoz(0xFF0F)));\r
-\r
-               switch (interrupt)\r
-               {\r
-               case 0x40: enabled = flags & 0x1; outFlags |= 0x1; break;\r
-               case 0x48: enabled = flags & 0x2; outFlags |= 0x2; break;\r
-               case 0x50: enabled = flags & 0x4; outFlags |= 0x4; break;\r
-               case 0x58: enabled = flags & 0x8; outFlags |= 0x8; break;\r
-               case 0x60: enabled = flags & 0x10; outFlags += 0x10; break;\r
-               }\r
-               if (enabled)\r
-               {\r
-                       m_interruptQueue.push_back(interrupt);\r
-               }\r
+       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
 }\r
 \r
@@ -76,8 +69,6 @@ Core::run()
        // handler\r
        // TODO z80 only. Unused for gb80 uint8_t doublePrefixOperand(0);\r
 \r
-       Log& log(Log::Instance());\r
-\r
        while (true)\r
        {\r
                cycle_t clock = 0;\r
@@ -85,21 +76,37 @@ Core::run()
                uint16_t prefix(0); // Used for debug logging only.\r
 \r
                // Check for interrupts.\r
-               if (!m_interruptQueue.empty())\r
+               if (m_pendingInterrupts)\r
                {\r
-                       int interrupt = m_interruptQueue.front();\r
-                       m_interruptQueue.pop_front();\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
-                       m_iff1 = 0;\r
-                       m_iff2 = 0;\r
+                       uint8_t ie(m_mem.read8(htoz(0xFFFF)));\r
+\r
+                       if (m_iff1 && (ie & priorityBit))\r
+                       {\r
+                               // Clear the IF of ONLY the interrupt we end up calling.\r
+                               iFlag ^= priorityBit;\r
 \r
-                       // TODO interrupt modes!\r
-                       m_reg.SP.dec(2);\r
-                       m_mem.write16(m_reg.SP, m_reg.PC);\r
+                               m_iff1 = 0;\r
+                               m_iff2 = 0;\r
 \r
-                       m_reg.PC = htoz(interrupt);\r
+                               m_reg.SP.dec(2);\r
+                               m_mem.write16(m_reg.SP, m_reg.PC);\r
 \r
-                       m_halted = false;\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
+                       }\r
+                       m_mem.write8(htoz(0xFF0F), iFlag);\r
                }\r
                else if (!m_halted)\r
                {\r
@@ -111,9 +118,10 @@ Core::run()
 #include "InstructionSet_Opcodes.cc"\r
 \r
 BAD_OPCODE:\r
-                       log(Log::DBG_ERROR) <<\r
-                               "Unknown Opcode: " << glBoy::hex(opcode) << " (PC=" << opcodePC << ")" <<\r
-                               std::endl;\r
+                       Err() <<\r
+                               "Unknown Opcode: " << glBoy::hex(opcode) <<\r
+                               " (PC=" << opcodePC << ")" <<\r
+                               Log::endl;\r
                        assert(false);\r
 \r
 END_INSTRUCTIONS: ;\r
@@ -125,7 +133,8 @@ END_INSTRUCTIONS: ;
 \r
                m_clock += clock;\r
 \r
-               if (m_halted && m_interruptQueue.empty())\r
+\r
+               if (m_halted && (m_pendingInterrupts == 0))\r
                {\r
                        m_clock = std::max(m_clock, m_nextPeriodicCallback);\r
                }\r
diff --git a/Core.hh b/Core.hh
index 167fbe548b36d2888f12746bb2979114ee1cc2be..a2b10b02cdc64df4b143ac0ac73011b3c7b4a159 100644 (file)
--- a/Core.hh
+++ b/Core.hh
@@ -23,7 +23,6 @@
 #include "MemoryMap.hh"\r
 #include "Registers.hh"\r
 \r
-#include <deque>\r
 #include <tr1/functional>\r
 #include <functional>\r
 #include <vector>\r
@@ -67,12 +66,12 @@ private:
        // Interrupt enablement\r
        bit m_iff1;\r
        bit m_iff2;\r
+       uint8_t m_pendingInterrupts;\r
 \r
        enum InterruptMode { IM_0, IM_1, IM_2 };\r
        InterruptMode m_interruptMode;\r
 \r
        bool m_halted;\r
-       std::deque<uint8_t> m_interruptQueue;\r
 \r
        MemoryMap m_mem;\r
 \r
index 28badebb911e7f1d83ac4c65ba96a71f1486c6c4..a48313fba1bcdcdb7aed3e3384c3897477883dda 100644 (file)
 
 #include "glBoy.hh"
 #include "GameboyCart.hh"
+#include "Log.hh"
+#include "MBC1.hh"
+#include "MBC2.hh"
+#include "MBC3.hh"
 #include "RAM.hh"
+#include "RAM_4bit.hh"
 #include "ROM.hh"
 
+#include "libzipper/zipper.hh"
+
+#include <algorithm>
+#include <vector>
+
 #include <cassert>
 
 using namespace glBoy;
+using namespace zipper;
 using namespace std;
 
 namespace
 {
-       class MBC1 : public MemoryMap::Memory
-       {
-       public:
-               MBC1(GameboyCart* parent, const uint8_t* begin, const uint8_t* end) :
-                       m_parent(parent),
-                       m_rom(begin, end),
-                       m_mode(ROM_SELECT),
-                       m_romLow(1),
-                       m_romHigh(0),
-                       m_romOffset(0)
-               {
-               }
+       class CartWriter : public zipper::Writer
+       {
+               public:
+                       std::vector<uint8_t> data;
 
-               virtual uint8_t read8(uint16_t address)
-               {
-                       if (address < 0x4000)
+                       CartWriter() :
+                               data(GameboyCart::MaxRomBytes)
                        {
-                               return m_rom[address];
                        }
-                       else if ((m_romOffset + address) < m_rom.size())
-                       {
-                               return m_rom[m_romOffset + address];
-                       }
-                       else
-                       {
-std::cerr << address << "  " << m_rom.size() << " " << m_romOffset << " " << (int) m_romLow << " " << (int) m_romHigh << std::endl;
-assert(false);
-                               return 0;
-                       }
-               }
 
-               virtual void write8(uint16_t address, uint8_t value)
-               {
-                       if (address < 0x1FFF)
+                       bool isValid() const
                        {
-                               // TODO enable/disable ram
-                       }
-                       else if (address < 0x4000)
-                       {
-                               m_romLow = (value == 0) ? 1 : value;
+                               bool isValid = true;
+                               isValid &= data.size() >= GameboyCart::MinRomBytes;
+                               isValid &= data.size() <= GameboyCart::MaxRomBytes;
 
-                               m_romOffset =
-                                       ((((m_romHigh & 0x3) << 4) | (m_romLow & 0x1F)) - 1) * 0x4000;
-assert(m_romOffset < m_rom.size());
-                       }
-                       else if (address < 0x6000)
-                       {
-                               if (m_mode == RAM_SELECT)
-                               {
-                                       m_parent->setRamBank(value & 0x3);
-                               }
-                               else
-                               {
-                                       m_romHigh = value;
+                               // Check some magic bytes. This is part of the "Nintendo"
+                               // power-on logo that is verified on boot.
+                               isValid &= data[0x104] == 0xCE;
+                               isValid &= data[0x105] == 0xED;
+                               isValid &= data[0x132] == 0x33;
+                               isValid &= data[0x133] == 0x3E;
 
-                                       m_romOffset =
-                                               ((((m_romHigh & 0x3) << 4) | (m_romLow & 0x1F)) - 1) * 0x4000;
-assert(m_romOffset < m_rom.size());
-                               }
+                               return isValid;
                        }
-                       else
+
+                       virtual void writeData(
+                                       zsize_t offset, zsize_t bytes, const uint8_t* inData)
                        {
-                               if (value & 1)
+                               if (offset + bytes <= GameboyCart::MaxRomBytes)
                                {
-                                       m_mode = RAM_SELECT;
+                                       data.resize(std::max(offset + bytes, data.size()));
+                                       std::copy(inData, inData + bytes, &data[offset]);
                                }
                                else
                                {
-                                       m_mode = ROM_SELECT;
-                                       m_parent->setRamBank(0);
+                                       data.clear();
                                }
                        }
-               }
-
-       private:
-               GameboyCart* m_parent;
-
-               std::vector<uint16_t> m_rom;
-
-               enum Mode { ROM_SELECT, RAM_SELECT };
-               Mode m_mode;
-
-               uint8_t m_romLow;
-               uint8_t m_romHigh;
-
-               uint32_t m_romOffset;
+                       virtual zsize_t getSize() const { return data.size(); }
        };
 }
 
 GameboyCart::GameboyCart(
        Core& core, const std::vector<uint8_t>& rom
        ) :
-       m_core(core)
+       m_core(core),
+       m_isValid(false),
+       m_battery(false),
+       m_rtc(false),
+       m_rumble(false)
+{
+       init(rom);
+}
+
+GameboyCart::GameboyCart(
+       Core& core, const std::string& filename
+       ) :
+       m_core(core),
+       m_isValid(false),
+       m_battery(false),
+       m_rtc(false),
+       m_rumble(false)
 {
-       if (rom.size() < 16384)
+       CartWriter writer;
+       zipper::FileReader reader(filename);
+       zipper::Decompressor decomp(reader);
+
+       typedef std::vector<zipper::CompressedFilePtr> cfptr;
+       cfptr entries(decomp.getEntries());
+
+       bool found(false);
+       for (cfptr::iterator it(entries.begin());
+               !found && it != entries.end();
+               ++it)
+       {
+               writer.data.clear();
+               (*it)->decompress(writer);
+               found = writer.isValid();;
+       }
+
+       if (!found)
        {
-               std::cerr << "Invalid ROM (too small)" << std::endl;
+               Err() << "Could not find valid ROM file." << Log::endl;
                abort();
        }
+       init(writer.data);
+}
+
+void
+GameboyCart::init(const std::vector<uint8_t>& rom)
+{
+       m_isValid = false;
+
+       if (rom.size() < GameboyCart::MinRomBytes)
+       {
+               Err() << "Invalid ROM (too small)" << Log::endl;
+               return;
+       }
 
-       bool error(false);
+       bool supported(true);
 
-       string mbc;
        switch (rom[0x147])
        {
-       case 0:
+       case 0x9: m_battery = true;
+       case 0x8:
+       case 0x0:
+       {
+               m_mbc = "ROM";
+
+               size_t romSize(std::min(0x8000ul, rom.size()));
+               shared_ptr<MemoryMap::Memory> mem(
+                       new ROM(&rom[0], romSize)
+                       );
+               m_core.getMemoryMap().map(0, romSize, mem);
+               m_romBanks.push_back(mem);
+       }; break;
+
+       case 0x3: m_battery = true;
+       case 0x1:
+       case 0x2:
        {
-               mbc = "None";
+               m_mbc = "MBC1";
 
+               size_t romSize(std::min(0x200000ul, rom.size()));
                shared_ptr<MemoryMap::Memory> mem(
-                       new MBC1(this, &rom[0], &rom[0] + rom.size())
+                       new MBC1(this, &rom[0], &rom[0] + romSize)
                        );
-/*             shared_ptr<MemoryMap::Memory> mem(
-                       new ROM(rom, size)
-                       );*/
                m_core.getMemoryMap().map(0, 0x8000, mem);
                m_romBanks.push_back(mem);
        }; break;
 
-       case 1:
-       case 2:
-       case 3:
+
+       case 0x6: m_battery = true;
+       case 0x5:
        {
-               mbc = "MBC1";
+               m_mbc = "MBC2";
+
+               size_t romSize(std::min(0x40000ul, rom.size()));
                shared_ptr<MemoryMap::Memory> mem(
-                       new MBC1(this, &rom[0], &rom[0] + rom.size())
+                       new MBC2(this, &rom[0], &rom[0] + romSize)
                        );
                m_core.getMemoryMap().map(0, 0x8000, mem);
                m_romBanks.push_back(mem);
-       };
-       break;
+
+               // MBC2 contains 512x4bits built-in ram.
+               shared_ptr<MemoryMap::Memory> ram(new RAM_4bit(512));
+               m_core.getMemoryMap().map(0xA000, 512, ram);
+               m_ramBanks.push_back(ram);
+       }; break;
+
+       case 0x0F:
+       case 0x10: m_rtc = true;
+       case 0x13: m_battery = true;
+       case 0x11:
+       case 0x12:
+       {
+               m_mbc = "MBC3";
+
+               size_t romSize(std::min(0x200000ul, rom.size()));
+               shared_ptr<MemoryMap::Memory> mem(
+                       new MBC3(this, &rom[0], &rom[0] + romSize)
+                       );
+               m_core.getMemoryMap().map(0, 0x8000, mem);
+               m_romBanks.push_back(mem);
+       }; break;
+
+       case 0x0D: m_battery = true;
+       case 0x0B:
+       case 0x0C:
+               m_mbc = "MMM01 (unsupported)";
+               supported = false;
+               break;
+
+       case 0x17: m_battery = true;
+       case 0x15:
+       case 0x16:
+               m_mbc = "MBC4 (unsupported)";
+               supported = false;
+               break;
+
+       case 0x1B:
+       case 0x1E: m_battery = true;
+       case 0x1A:
+       case 0x1C:
+       case 0x1D:
+               m_mbc = "MBC5 (unsupported)";
+               supported = false;
+
+               if (rom[0x147] >= 0x1C ||
+                       rom[0x147] <= 0x1E)
+               {
+                       m_rumble = true;
+               }
+               break;
+
+       case 0xFC:
+               m_mbc = "Pocket Camera (unsupported)";
+               supported = false;
+               break;
+
+       case 0xFD:
+               m_mbc = "Bandai TAMA5 (unsupported)";
+               supported = false;
+               break;
+
+       case 0xFE:
+               m_mbc = "HuC3 (unsupported)";
+               supported = false;
+               break;
+
+       case 0xFF:
+               m_mbc = "HuC1 (unsupported)";
+               m_battery = true;
+               supported = false;
+               break;
+
        default:
-               mbc = "Unsupported";
-               error = true;
+       {
+               std::stringstream ss;
+               ss << "Unsupported (" << glBoy::hex(rom[0x147]) << ")";
+               m_mbc = ss.str();
+               supported = false;
+       }
        };
 
        int cartRam;
@@ -168,11 +269,7 @@ GameboyCart::GameboyCart(
        {
        case 0:
        {
-               // Mis-reported on some carts.
                cartRam = 0;
-               shared_ptr<MemoryMap::Memory> mem(new RAM(8192));
-               m_core.getMemoryMap().map(0xA000, 8192, mem);
-               m_ramBanks.push_back(mem);
        }; break;
        case 1:
        {
@@ -204,23 +301,29 @@ GameboyCart::GameboyCart(
        }; break;
 
        default:
-               error = true;
+               supported = false;
                cartRam = 0;
-               cerr << "Invalid RAM size" << endl;
+               Err() << "Invalid RAM size" << Log::endl;
        }
 
-       string title(&rom[0x134], &rom[0x134 + 11]);
-       cout << "Loading Cart" << endl;
-       cout << "\tTitle:\t\t" << title << endl;
-       cout << "\tRegion:\t\t" << (rom[0x14A] ? "Worldwide" : "Japan") << endl;
-       cout << "\tVersion:\t" << int(rom[0x14D]) << endl;
-       cout << "\tMBC:\t\t" << mbc << endl;
-       cout << "\tROM size:\t" << (32 << rom[0x148]) << "KB" << endl;
-       cout << "\tExt RAM size:\t" << (cartRam / 1024) << "KB" << endl;
+       m_isValid = supported;
 
-       if (error)
+       m_title = string(&rom[0x134], &rom[0x134 + 11]);
+       m_region = rom[0x14A] ? "Worldwide" : "Japan";
        {
-               abort();
+               std::stringstream tmp;
+               tmp << int(rom[0x14D]);
+               tmp >> m_version;
+       }
+       {
+               std::stringstream tmp;
+               tmp << (32 << rom[0x148]) << "KB";
+               tmp >> m_romSize;
+       }
+       {
+               std::stringstream tmp;
+               tmp << (cartRam / 1024) << "KB";
+               tmp >> m_extRamSize;
        }
 }
 
@@ -233,3 +336,24 @@ GameboyCart::setRamBank(size_t bank)
                m_core.getMemoryMap().map(0xA000, 8192, m_ramBanks[bank]);
        }
 }
+
+void
+GameboyCart::dump(std::ostream& out)
+{
+       out <<
+               "\tTitle:\t\t" << m_title << endl <<
+               "\tRegion:\t\t" << m_region << endl <<
+               "\tVersion:\t" << m_version << endl <<
+               "\tMemory Type:\t" << m_mbc << endl <<
+               "\tROM size:\t" << m_romSize << endl <<
+               "\tExt RAM size:\t" << m_extRamSize << endl <<
+               "\tBattery:\t" << (m_battery ? "true" : "false") << endl <<
+               "\tClock:\t\t" << (m_rtc ? "true" : "false") << endl <<
+               "\tRumble:\t\t" << (m_rumble ? "true" : "false") << endl;
+}
+
+bool
+GameboyCart::isValid() const
+{
+       return m_isValid;
+}
index 0588cd1c39f44455475bd038cd4b7983eb0043a8..c5c1589630c17395eeb10f21d95130c445749cd5 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "glBoy.hh"
 #include "Core.hh"
+#include <ostream>
 #include <vector>
 
 namespace glBoy
@@ -28,14 +29,38 @@ namespace glBoy
 class GameboyCart
 {
 public:
+       enum
+       {
+               MinRomBytes = 16384,
+               MaxRomBytes = 0x4000000 // 4MByte, 256 banks
+       };
+
        GameboyCart(Core& core, const std::vector<uint8_t>& rom);
+       GameboyCart(Core& core, const std::string& filename);
+
+       bool isValid() const;
+
+       void dump(std::ostream& out);
 
        void setRamBank(size_t bank);
 private:
+       void init(const std::vector<uint8_t>& rom);
        Core& m_core;
+       bool m_isValid;
 
        std::vector<std::shared_ptr<MemoryMap::Memory> > m_romBanks;
        std::vector<std::shared_ptr<MemoryMap::Memory> > m_ramBanks;
+
+
+       std::string m_title;
+       std::string m_region;
+       std::string m_version;
+       std::string m_romSize;
+       std::string m_extRamSize;
+       std::string m_mbc;
+       bool m_battery;
+       bool m_rtc;
+       bool m_rumble;
 };
 
 } // namespace glBoy
index 57cae8a0a8076a26a3db54252c3281e54d94f144..651666f3131c14c58aee7e7c1635b3692735687e 100644 (file)
@@ -372,10 +372,10 @@ GameboyGraphics::startScanLine()
                m_frameSkip = false;
                displayStats();
 
-               //if (m_reg->STAT & 0x10) Seems to need to trigger regardless ?
-               // Maybe we need to trigger 0x40 as well, depending on STAT ?
+               m_core.raiseInterrupt(0x40); // Start of vblank
+               if (m_reg->STAT & 0x10)
                {
-                       m_core.raiseInterrupt(0x40); // Start of vblank
+                       m_core.raiseInterrupt(0x48);
                }
        }
        else if (
@@ -668,9 +668,11 @@ GameboyGraphics::displayStats()
                                (startTime.tv_sec + startTime.tv_usec / usec)
                        );
 
-               Log::Instance()(Log::DBG_STAT) <<
-                       "FPS = " << fps << " (error=" << (100 - (fps * 100 / 59.73)) << "%) "
-                       "backround redraw=" << m_stat_redraw << std::endl;
+               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;
index 73868dd2d840ea6a1254d602150204cfc9c4e5ea..dd5244a68045a542d178e223e82525bfee08f74f 100644 (file)
@@ -121,8 +121,12 @@ GameboyJoypad::read8(uint16_t)
 void
 GameboyJoypad::write8(uint16_t, uint8_t value)
 {
-       switch (value)
+       switch (value & 0x30)
        {
+       case 0x00:
+               // Should really be some sort of odd "both on" situation ?
+               m_state = Joypad_Off;
+               break;
        case 0x20:
                m_state = Joypad_Directional;
                break;
index 606db98e343173e505b190a57cff36c4080a9aed..09c0734db8997ac5c3b282c539c7eb4591d4b319 100644 (file)
@@ -37,7 +37,6 @@ extern "C"
 
 GameboySound::GameboySound(Core& core)
 {
-       extern void mixaudio(void *unused, Uint8 *stream, int len);
        SDL_AudioSpec fmt;
 
        fmt.freq = SAMPLE_RATE;
index e4acc1f759884d6c6f1883bfd58b1628236f2621..ca022d8e6d82b01f9e894e3eb44b8c2b8bb8ec28 100644 (file)
@@ -1613,6 +1613,19 @@ TODO NOT IMPLEMENTED
        m_reg.F.C = 0;\r
 </instruction>\r
 \r
+<instruction name="SWAP (HL)" clock="16" variant="gb80">\r
+<opcode prefix="0xCB" mask="00110110"/>\r
+       uint8_t tmp(m_mem.read8(m_reg.HL));\r
+       uint8_t r = tmp &lt;&lt; 4;\r
+       r |= (tmp &gt;&gt; 4);\r
+       m_mem.write8(m_reg.HL, r);\r
+\r
+       m_reg.F.Z = r == 0;\r
+       m_reg.F.N = 0;\r
+       m_reg.F.H = 0;\r
+       m_reg.F.C = 0;\r
+</instruction>\r
+\r
 <!-- Zero-page gameboy instructions -->\r
 <instruction name="LD (FF00+n),A" clock="4" variant="gb80">\r
 <opcode mask="11100000" operand="n"/>\r
diff --git a/Log.cc b/Log.cc
index b77d0ca6b5733bcd2ceeb1b1ed09956878f1c631..62dc21d47159f700b2b5be79c80e5cd812a83c4f 100644 (file)
--- a/Log.cc
+++ b/Log.cc
 
 #include "Log.hh"
 
+#include <cassert>
 #include <cstdlib>
 #include <iostream>
 #include <sstream>
 
 using namespace glBoy;
 
+Log::End::End() {}
+Log::End Log::endl;
+
+Log& glBoy::GetLog(int level)
+{
+       Log& log(Log::Instance());
+       assert(log.m_currentLevel == Log::DBG_NONE);
+       log.m_currentLevel = Log::DebugLevel(level);
+       return log;
+}
+
+Log& glBoy::Err()
+{
+       return GetLog(Log::DBG_ERROR);
+}
+
+Log& glBoy::Warn()
+{
+       return GetLog(Log::DBG_WARNING);
+}
+
+Log& glBoy::Info()
+{
+       return GetLog(Log::DBG_INFO);
+}
+
+Log& glBoy::Stat()
+{
+       return GetLog(Log::DBG_STAT);
+}
+
+Log& glBoy::TraceMem()
+{
+       return GetLog(Log::DBG_TRACE_MEM);
+}
+
+
 Log&
 Log::Instance()
 {
@@ -31,8 +69,8 @@ Log::Instance()
 }
 
 Log::Log() :
-       m_levelMask(7),
-       m_nullStream(&m_nullBuf)
+       m_levelMask(15),
+       m_currentLevel(DBG_NONE)
 {
        char* env(getenv("GLBOY_DEBUG_LEVEL"));
        if (env)
@@ -52,21 +90,31 @@ Log::setDebugLevel(uint32_t levelMask)
 }
 
 bool
-Log::isDebug(DebugLevel level) const
+Log::isDebugLevelOn(DebugLevel level) const
 {
        return (m_levelMask & level) != 0;
 }
 
-std::ostream&
-Log::operator()(DebugLevel level)
+void
+Log::operator<<(const End&)
 {
-       if (isDebug(level))
+       if (isDebugLevelOn(m_currentLevel))
        {
-               return std::cout;
-       }
-       else
-       {
-               return m_nullStream;
+               switch (m_currentLevel)
+               {
+               case DBG_ERROR:
+                       std::cerr << "ERROR: " << m_collector.str() << std::endl;
+                       break;
+               case DBG_WARNING:
+                       std::cerr << "WARNING: " << m_collector.str() << std::endl;
+                       break;
+               default:
+                       std::cout << m_collector.str() << std::endl;
+               };
        }
+
+       m_collector.str(std::string());
+       m_collector.clear(); // clear error flags
+       m_currentLevel = DBG_NONE;
 }
 
diff --git a/Log.hh b/Log.hh
index 1afdb656212dbe0456e2f7e0e07ee4f6e3bc9da7..8aaf4192e1f638448e4831cc00557b4c27cd02b1 100644 (file)
--- a/Log.hh
+++ b/Log.hh
 
 #include "glBoy.hh"
 
+#include <cassert>
 #include <cstdint>
-#include <ostream>
-#include <streambuf>
+#include <sstream>
 
 namespace glBoy
 {
+       class Log;
+
+       Log& GetLog(int level);
+       Log& Err();
+       Log& Warn();
+       Log& Info();
+       Log& Stat();
+       Log& TraceMem();
+
        class Log
        {
        public:
+               class End
+               {
+               public:
+                       End();
+               };
+               static End endl;
 
-               enum DebugLevel 
+               enum DebugLevel
                {
-                       DBG_STAT = 1, DBG_ERROR = 2, DBG_WARNING = 4, DBG_MEMORY = 8, DBG_TRACE = 16
+                       DBG_ERROR = 1,
+                       DBG_WARNING = 2,
+                       DBG_INFO = 4,
+                       DBG_STAT = 8,
+                       DBG_TRACE_MEM = 16,
+
+                       DBG_NONE = 0xFFFFFFFF
                };
                static Log& Instance();
 
-               void setDebugLevel(uint32_t levelMask);
+               void operator<<(const End&);
+
+               template<class T> Log&
+               operator<<(const T& val)
+               {
+                       assert(m_currentLevel != DBG_NONE);
+                       m_collector << val;
+                       return *this;
+               }
+
+               // << endl support
+               Log&
+               operator<<(std::ostream& (*val)(std::ostream&))
+               {
+                       assert(m_currentLevel != DBG_NONE);
+                       m_collector << val;
+                       return *this;
+               }
+
+               Log&
+               operator<<(std::ios& (*val)(std::ios&))
+               {
+                       assert(m_currentLevel != DBG_NONE);
+                       m_collector << val;
+                       return *this;
+               }
+
+               Log&
+               operator<<(std::ios_base& (*val)(std::ios_base&))
+               {
+                       assert(m_currentLevel != DBG_NONE);
+                       m_collector << val;
+                       return *this;
+               }
 
-               bool isDebug(DebugLevel level) const;
-               std::ostream& operator()(DebugLevel level);
+               void setDebugLevel(uint32_t levelMask);
+               bool isDebugLevelOn(DebugLevel level) const;
 
        private:
+               friend Log& GetLog(int);
+
                Log();
 
                Log(const Log&);
@@ -49,13 +105,9 @@ namespace glBoy
 
                uint32_t m_levelMask;
 
-               class NullBuf : public std::streambuf
-               {
-               public:
-               };
+               DebugLevel m_currentLevel;
 
-               NullBuf m_nullBuf;
-               std::ostream m_nullStream;
+               std::stringstream m_collector;
        };
 
 } // namespace glBoy
diff --git a/MBC1.cc b/MBC1.cc
new file mode 100644 (file)
index 0000000..31defe3
--- /dev/null
+++ b/MBC1.cc
@@ -0,0 +1,114 @@
+//     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 "MBC1.hh"
+#include "Log.hh"
+
+#include <cassert>
+
+using namespace glBoy;
+using namespace std;
+
+MBC1::MBC1(
+       GameboyCart* parent, const uint8_t* begin, const uint8_t* end
+       ) :
+       m_parent(parent),
+       m_rom(begin, end),
+       m_mode(ROM_SELECT),
+       m_romLow(1),
+       m_romHigh(0),
+       m_romBank(0)
+{
+       if (end - begin < 0x8000)
+       {
+               Err() << "MBC1 ROM too small (" << end - begin << " bytes)" << Log::endl;
+               abort();
+       }
+}
+
+uint8_t
+MBC1::read8(uint16_t address)
+{
+       if (address < 0x4000)
+       {
+               return m_rom[address];
+       }
+       else if (((m_romBank * 0x4000) + (address - 0x4000)) < m_rom.size())
+       {
+               return m_rom[(m_romBank * 0x4000) + (address - 0x4000)];
+       }
+       else
+       {
+               TraceMem() << "Attempt to read unmapped MBC1 rom data." << Log::endl;
+               abort();
+       }
+}
+
+void
+MBC1::write8(uint16_t address, uint8_t value)
+{
+       if (address < 0x1FFF)
+       {
+               // TODO enable/disable ram
+       }
+       else if (address < 0x4000)
+       {
+               m_romLow = ((value & 0x1F) == 0) ? 1 : (value & 0x1F);
+               m_romBank = m_romLow;
+
+               if (m_mode == ROM_SELECT)
+               {
+                       m_romBank |= (m_romHigh << 5);
+               }
+
+std::cerr << "MBC1 ROM LOW " << (int)m_romBank << std::endl;
+       }
+       else if (address < 0x6000)
+       {
+               m_romHigh = (value & 0x3);
+               if (m_mode == RAM_SELECT)
+               {
+                       m_parent->setRamBank(m_romHigh);
+               }
+               else
+               {
+                       m_romBank = m_romLow | (m_romHigh << 5);
+std::cerr << "MBC1 ROM HIGH " << (int)m_romBank << std::endl;
+               }
+       }
+       else
+       {
+               if (value & 1)
+               {
+                       m_mode = RAM_SELECT;
+                       // In this mode only lower 5 bits of rom bank can be used
+
+                       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);
+std::cerr << "MBC1 ROM SELECT " << (int)m_romBank << std::endl;
+               }
+       }
+}
+
diff --git a/MBC1.hh b/MBC1.hh
new file mode 100644 (file)
index 0000000..a2b61af
--- /dev/null
+++ b/MBC1.hh
@@ -0,0 +1,55 @@
+//     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_MBC1_HH
+#define GLBOY_MBC1_HH
+
+#include "glBoy.hh"
+#include "GameboyCart.hh"
+
+#include <vector>
+
+namespace glBoy
+{
+       class MBC1 : public MemoryMap::Memory
+       {
+       public:
+               MBC1(
+                       GameboyCart* parent,
+                       const uint8_t* begin,
+                       const uint8_t* end);
+
+               virtual uint8_t read8(uint16_t address);
+               virtual void write8(uint16_t address, uint8_t value);
+
+       private:
+               GameboyCart* m_parent;
+
+               std::vector<uint16_t> m_rom;
+
+               enum Mode { ROM_SELECT, RAM_SELECT };
+               Mode m_mode;
+
+               uint8_t m_romLow;
+               uint8_t m_romHigh;
+
+               uint32_t m_romBank;
+       };
+}
+
+#endif
+
diff --git a/MBC2.cc b/MBC2.cc
new file mode 100644 (file)
index 0000000..000a830
--- /dev/null
+++ b/MBC2.cc
@@ -0,0 +1,79 @@
+//     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 "MBC2.hh"
+#include "Log.hh"
+
+#include <cassert>
+
+using namespace glBoy;
+using namespace std;
+
+MBC2::MBC2(
+       GameboyCart* parent, const uint8_t* begin, const uint8_t* end
+       ) :
+       m_parent(parent),
+       m_rom(begin, end),
+       m_romBank(0)
+{
+       if (end - begin < 0x8000)
+       {
+               Err() << "MBC2 ROM too small (" << end - begin << " bytes)" << Log::endl;
+               abort();
+       }
+}
+
+uint8_t
+MBC2::read8(uint16_t address)
+{
+       if (address < 0x4000)
+       {
+               return m_rom[address];
+       }
+       else if (((m_romBank * 0x4000) + (address - 0x4000)) < m_rom.size())
+       {
+               return m_rom[(m_romBank * 0x4000) + (address - 0x4000)];
+       }
+       else
+       {
+               TraceMem() << "Attempt to read unmapped MBC2 rom data." << Log::endl;
+               abort();
+       }
+}
+
+void
+MBC2::write8(uint16_t address, uint8_t value)
+{
+       if (address < 0x1FFF)
+       {
+               // TODO enable/disable ram
+       }
+       else if (address < 0x4000)
+       {
+               // LSB of upper address byte must be 1 to select a ROM bank
+               if (address & 0x100)
+               {
+                       m_romBank= (value == 0) ? 1 : (value & 0xF);
+               }
+       }
+       else
+       {
+               // Do nothing.
+       }
+}
+
diff --git a/MBC2.hh b/MBC2.hh
new file mode 100644 (file)
index 0000000..1445f83
--- /dev/null
+++ b/MBC2.hh
@@ -0,0 +1,49 @@
+//     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_MBC2_HH
+#define GLBOY_MBC2_HH
+
+#include "glBoy.hh"
+#include "GameboyCart.hh"
+
+#include <vector>
+
+namespace glBoy
+{
+       class MBC2 : public MemoryMap::Memory
+       {
+       public:
+               MBC2(
+                       GameboyCart* parent,
+                       const uint8_t* begin,
+                       const uint8_t* end);
+
+               virtual uint8_t read8(uint16_t address);
+               virtual void write8(uint16_t address, uint8_t value);
+
+       private:
+               GameboyCart* m_parent;
+
+               std::vector<uint16_t> m_rom;
+
+               uint32_t m_romBank;
+       };
+}
+
+#endif
+
diff --git a/MBC3.cc b/MBC3.cc
new file mode 100644 (file)
index 0000000..750a38b
--- /dev/null
+++ b/MBC3.cc
@@ -0,0 +1,100 @@
+//     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 "MBC3.hh"
+#include "Log.hh"
+
+#include <cassert>
+
+using namespace glBoy;
+using namespace std;
+
+MBC3::MBC3(
+       GameboyCart* parent, const uint8_t* begin, const uint8_t* end
+       ) :
+       m_parent(parent),
+       m_rom(begin, end),
+       m_romBank(0)
+{
+       if (end - begin < 0x8000)
+       {
+               Err() << "MBC3 ROM too small (" << end - begin << " bytes)" << Log::endl;
+               abort();
+       }
+}
+
+uint8_t
+MBC3::read8(uint16_t address)
+{
+       if (address < 0x4000)
+       {
+               return m_rom[address];
+       }
+       else if (((m_romBank * 0x4000) + (address - 0x4000)) < m_rom.size())
+       {
+               return m_rom[(m_romBank * 0x4000) + (address - 0x4000)];
+       }
+       else
+       {
+               TraceMem() << "Attempt to read unmapped MBC3 rom data." << Log::endl;
+               abort();
+       }
+}
+
+void
+MBC3::write8(uint16_t address, uint8_t value)
+{
+       static bool rtcWarning(false);
+
+       if (address < 0x1FFF)
+       {
+               // TODO enable/disable ram
+       }
+       else if (address < 0x4000)
+       {
+               m_romBank = value;
+       }
+       else if (address < 0x6000)
+       {
+               if (value <= 0x03)
+               {
+                       m_parent->setRamBank(value);
+               }
+               else if (value <= 0x0C)
+               {
+                       // TODO RTC Register Select
+                       if (!rtcWarning)
+                       {
+                               Warn() <<
+                                       "MBC3 RTC functionality not implemented!" << Log::endl;
+                               rtcWarning = true;
+                       }
+               }
+       }
+       else
+       {
+               // TODO Latch Clock Data
+               if (!rtcWarning)
+               {
+                       Warn() <<
+                               "MBC3 RTC functionality not implemented!" << Log::endl;
+                       rtcWarning = true;
+               }
+       }
+}
+
diff --git a/MBC3.hh b/MBC3.hh
new file mode 100644 (file)
index 0000000..0b7081a
--- /dev/null
+++ b/MBC3.hh
@@ -0,0 +1,49 @@
+//     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_MBC3_HH
+#define GLBOY_MBC3_HH
+
+#include "glBoy.hh"
+#include "GameboyCart.hh"
+
+#include <vector>
+
+namespace glBoy
+{
+       class MBC3 : public MemoryMap::Memory
+       {
+       public:
+               MBC3(
+                       GameboyCart* parent,
+                       const uint8_t* begin,
+                       const uint8_t* end);
+
+               virtual uint8_t read8(uint16_t address);
+               virtual void write8(uint16_t address, uint8_t value);
+
+       private:
+               GameboyCart* m_parent;
+
+               std::vector<uint16_t> m_rom;
+
+               uint32_t m_romBank;
+       };
+}
+
+#endif
+
diff --git a/Main.cc b/Main.cc
index c8c27860add7805389fa1c8b4814af3409ed07aa..bd7ae88fed3eab3e7f941063a999f1217df03522 100644 (file)
--- a/Main.cc
+++ b/Main.cc
@@ -33,7 +33,7 @@
 #include <SDL/SDL_video.h>
 
 #include <libconfig.h++>
-#include <zipios++/zipfile.h>
+#include <zipper.hh>
 
 #include <cassert>
 #include <set>
@@ -61,7 +61,7 @@ namespace
        SDL_Surface*
        initOpenGL(double xScale, double yScale, bool fullscreen)
        {
-               glBoy::Log::Instance()(glBoy::Log::DBG_STAT) << "Enabling OpenGL" << std::endl;
+               glBoy::Info() << "Enabling OpenGL" << glBoy::Log::endl;
 
                SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
 
@@ -91,24 +91,28 @@ namespace
                return surface;
        }
 
-       void usage(const std::string& exe)
+       void usage()
        {
-               std::cout <<
-                       "Usage: " << exe << " "
-                               "[--scale ratio] [--width w] [--height h] \n"
+               std::cout << std::endl <<
+                       "Usage: " << std::endl <<
+                               "\tglBoy {-I|--info} rom.gb" << std::endl <<
+                               "\tglBoy [--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 <<
+                               "\t\trom.gb" << std::endl << std::endl <<
                        std::endl <<
-                               "\t--scale\t(Default=4.0)" << std::endl <<
+                               "--info" << std::endl <<
+                                       "\t\tDisplay cartridge information and exit." << std::endl <<
+                               "--scale" << std::endl <<
                                        "\t\tMultiplier applied to the display output dimensions." << std::endl <<
-                               "\t--width" << std::endl <<
+                                       "\t\tDefault: 4.0" << std::endl <<
+                               "--width" << std::endl <<
                                        "\t\tSet the display output dimensions directly" << std::endl <<
-                               "\t--height" << std::endl <<
+                               "--height" << std::endl <<
                                        "\t\tSet the display output dimensions directly" << std::endl;
-                               /*"\t--scaling-method\n\t\t\t(Default=\"hq4x\")" << std::endl <<
+                               /*"--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 <<
+                               "--resample-method\n\t\t\t(Default=\"bilinear\")" << std::endl <<
                                        "\t\tSets the resampling method." << std::endl;*/
        }
 
@@ -147,78 +151,6 @@ namespace
                }
        }
 
-       std::vector<uint8_t> readCart(const std::string& filename)
-       {
-               std::vector<uint8_t> 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<std::istream> is(
-                                               zf.getInputStream(entries.front())
-                                               );
-std::cerr << "*" << std::endl;
-                                       result.resize(entries.front()->getSize());
-std::cerr << "*" << std::endl;
-                                       is->rdbuf()->sgetn(
-                                               reinterpret_cast<char*>(&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)
@@ -230,6 +162,7 @@ int main(int argc, char** argv)
 
        static struct option longOptions[] = 
        {
+               {"info", 0, NULL, 'I'},
                {"video", 1, NULL, 'v'},
                {"scale", 1, NULL, 's'},
                {"width", 1, NULL, 'w'},
@@ -269,12 +202,16 @@ int main(int argc, char** argv)
 
        int optionIndex(0);
        int c;
+       bool loadCartAndExit(false);
        while (
-               (c = getopt_long(argc, argv, "s:w:h:", &longOptions[0], &optionIndex))
+               (c = getopt_long(argc, argv, "s:w:h:I", &longOptions[0], &optionIndex))
                        != -1)
        {
                switch (c)
                {
+                       case 'I':
+                               loadCartAndExit = true;
+                               break;
                        case 'h':
                        {
                                int height(160);
@@ -338,7 +275,7 @@ int main(int argc, char** argv)
                        }; break;
 
                        case '?':
-                               usage(argv[0]);
+                               usage();
                                exit(1);
                                break;
 
@@ -352,16 +289,23 @@ int main(int argc, char** argv)
                filename = argv[optind++];
        }
 
-       if (access(filename.c_str(), R_OK) != 0)
+       glBoy::Core core;
+
+       glBoy::Info() <<
+               "Loading cartridge from file " << filename << glBoy::Log::endl;
+
+       glBoy::GameboyCart cart(core, filename);
+       std::stringstream cartInfo;
+       cart.dump(cartInfo);
+       glBoy::Info() << cartInfo.str() << glBoy::Log::endl;
+       if (loadCartAndExit)
        {
-               char* error(strerror(errno));
-               std::cerr << "Could not read file " << filename <<
-                       " (" << error << ")" << std::endl;
-               exit(1);
+               exit(0);
+       }
+       else if (!cart.isValid())
+       {
+               abort();
        }
-
-       glBoy::Core core;
-       glBoy::MemoryMap& map(core.getMemoryMap());
 
        SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO);
 
@@ -372,12 +316,9 @@ int main(int argc, char** argv)
        SDL_WM_SetCaption("glBoy", "glBoy");
        // TODO SDL_WM_SetIcon
 
-       std::vector<uint8_t> cart(readCart(filename));
-       glBoy::GameboyCart tmp(core, cart);
 
-
-       // Map the cart mem.
        // Map the standard internal RAM, plus shadow.
+       glBoy::MemoryMap& map(core.getMemoryMap());
        {
                std::shared_ptr<glBoy::MemoryMap::Memory> mem(new glBoy::RAM(8192));
                map.map(0xC000, 8192, mem);
index 2d21f248add165509fd80548e4429126f6f10c92..0dad624c91764d079c3ab8634046d4d870c1b837 100644 (file)
@@ -15,6 +15,8 @@
 #      You should have received a copy of the GNU General Public License
 #      along with glBoy.  If not, see <http://www.gnu.org/licenses/>.
 
+SUBDIRS = libzipper
+
 check_PROGRAMS = \
        test_alu
 
@@ -61,8 +63,16 @@ glBoy_SOURCES = \
        Main.cc \
        MemoryMap.hh \
        MemoryMap.cc \
+       MBC1.hh \
+       MBC1.cc \
+       MBC2.hh \
+       MBC2.cc \
+       MBC3.hh \
+       MBC3.cc \
        RAM.hh \
        RAM.cc \
+       RAM_4bit.hh \
+       RAM_4bit.cc \
        Registers.hh \
        Registers.cc \
        ROM.hh \
@@ -79,7 +89,9 @@ CLEANFILES = \
        InstructionSet_Opcodes.cc \
        InstructionSet_Tables.cc
 
-glBoy_LDFLAGS = -lGL -lGLU -lzipios
+glBoy_LDADD = libzipper/libzipper.la
+
+glBoy_LDFLAGS = -lGL -lGLU
 
 CXXFLAGS=-g -O3 -march=native -W -Wall -Werror -std=c++0x
 
index caf563f2d3e31d9c312954d4c354ea9e756ac1b8..3d8df1bafb1419e423932155417f334b95c2b7d1 100644 (file)
@@ -58,9 +58,9 @@ MemoryMap::map(
                );\r
        m_map.insert(it, mapping);\r
 \r
-       Log::Instance()(Log::DBG_MEMORY) <<\r
+       TraceMem() <<\r
                "Registering " << glBoy::hex(base) <<\r
-               " for " << glBoy::hex(size) << " bytes."<< std::endl;\r
+               " for " << glBoy::hex(size) << " bytes."<< Log::endl;\r
 }\r
 \r
 void\r
@@ -73,9 +73,9 @@ MemoryMap::remove(
 \r
        if (it != m_map.end())\r
        {\r
-               Log::Instance()(Log::DBG_MEMORY) <<\r
+               TraceMem() <<\r
                        "Removing " << glBoy::hex(it->base) <<\r
-                       " for " << glBoy::hex(it->size) << " bytes."<< std::endl;\r
+                       " for " << glBoy::hex(it->size) << " bytes."<< Log::endl;\r
 \r
                m_map.erase(it);\r
        }\r
@@ -128,8 +128,9 @@ MemoryMap::get(uint16_t address) const
 const MemoryMap::MappedArea&\r
 MemoryMap::getFailed(uint16_t address) const\r
 {\r
-       Log::Instance()(Log::DBG_MEMORY) <<\r
-               "Returning NULL memory for address " << glBoy::hex(address) << std::endl;\r
+       TraceMem() <<\r
+               "Returning NULL memory for address " << glBoy::hex(address) <<\r
+               Log::endl;\r
 \r
        static NullMemory s_null;\r
        static MappedArea s_nullArea(\r
diff --git a/NEWS b/NEWS
index 455df39193508e8a1d9972779602e9505ba7600f..2483f02bea22aafef5e132284fb7aa57a34755e8 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,10 @@
        - Increased performance for some NVidia Quadro cards (tested with NVS 290)
        - Added configurable key mappings. Copy glBoy.config to ~/.glBoy to change
                settings.
+       - Improved MBC1 support.
+       - Added MBC2 and MBC3 support.
+       - Added compressed ROM support (zip and gzip).
+       - Numerous z80 emulation bugfixes.
 
 2011-01-31     Version 1.0.0
        - Initial release
diff --git a/RAM.hh b/RAM.hh
index b7ae42fc1682e4ed736da7c10f10813929c42d7a..636b5ddebfb4da7688aa961ce027868c7d31bb7e 100644 (file)
--- a/RAM.hh
+++ b/RAM.hh
@@ -35,6 +35,7 @@ public:
 \r
 \r
 private:\r
+       bool m_fourBitMode;\r
        std::vector<uint8_t> m_mem;\r
 };\r
 \r
diff --git a/RAM_4bit.cc b/RAM_4bit.cc
new file mode 100644 (file)
index 0000000..ca4db00
--- /dev/null
@@ -0,0 +1,49 @@
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>\r
+//\r
+//     This file is part of glBoy.\r
+//\r
+//     glBoy is free software: you can redistribute it and/or modify\r
+//     it under the terms of the GNU General Public License as published by\r
+//     the Free Software Foundation, either version 3 of the License, or\r
+//     (at your option) any later version.\r
+//\r
+//     glBoy is distributed in the hope that it will be useful,\r
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+//     GNU General Public License for more details.\r
+//\r
+//     You should have received a copy of the GNU General Public License\r
+//     along with glBoy.  If not, see <http://www.gnu.org/licenses/>.\r
+\r
+#include "glBoy.hh"\r
+#include "RAM_4bit.hh"\r
+\r
+#include <string.h>\r
+\r
+using namespace glBoy;\r
+\r
+RAM_4bit::RAM_4bit(uint16_t size)\r
+{\r
+       m_mem.resize(size);\r
+}\r
+\r
+\r
+uint8_t\r
+RAM_4bit::read8(uint16_t address)\r
+{\r
+       return m_mem[address] & 0xF;\r
+}\r
+\r
+void\r
+RAM_4bit::write8(uint16_t address, uint8_t value)\r
+{\r
+       m_mem[address] = value & 0xF;\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
+}\r
+\r
diff --git a/RAM_4bit.hh b/RAM_4bit.hh
new file mode 100644 (file)
index 0000000..f1d169a
--- /dev/null
@@ -0,0 +1,46 @@
+//     Copyright (C) 2011 Michael McMaster <michael@codesrc.com>\r
+//\r
+//     This file is part of glBoy.\r
+//\r
+//     glBoy is free software: you can redistribute it and/or modify\r
+//     it under the terms of the GNU General Public License as published by\r
+//     the Free Software Foundation, either version 3 of the License, or\r
+//     (at your option) any later version.\r
+//\r
+//     glBoy is distributed in the hope that it will be useful,\r
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+//     GNU General Public License for more details.\r
+//\r
+//     You should have received a copy of the GNU General Public License\r
+//     along with glBoy.  If not, see <http://www.gnu.org/licenses/>.\r
+\r
+#ifndef GLBOY_RAM_4BIT_HH\r
+#define GLBOY_RAM_4BIT_HH\r
+\r
+#include "glBoy.hh"\r
+#include "MemoryMap.hh"\r
+\r
+#include <vector>\r
+namespace glBoy\r
+{\r
+\r
+class RAM_4bit : public MemoryMap::Memory\r
+{\r
+public:\r
+       // 4bit RAM is built-in to the Gameboy MBC2 chip\r
+       RAM_4bit(uint16_t size);\r
+\r
+       virtual uint8_t read8(uint16_t address);\r
+       virtual void write8(uint16_t address, uint8_t value);\r
+       virtual void copy(uint8_t* dest, uint16_t address, int bytes);\r
+\r
+\r
+private:\r
+       std::vector<uint8_t> m_mem;\r
+};\r
+\r
+} // namespace glBoy\r
+\r
+#endif\r
+\r
diff --git a/ROM.cc b/ROM.cc
index 23fd63765825afe8646ed9d5a0de0fb807da9027..c1d62341952d7d5eab7c91cc1b6782b936415abb 100644 (file)
--- a/ROM.cc
+++ b/ROM.cc
@@ -39,10 +39,9 @@ ROM::read8(uint16_t address)
 void\r
 ROM::write8(uint16_t address, uint8_t)\r
 {\r
-       //assert(false);\r
-       Log::Instance()(Log::DBG_MEMORY) <<\r
+       TraceMem() <<\r
                "Attempt to write to read-only address " << address <<\r
-               std::endl;\r
+               Log::endl;\r
 }\r
 \r
 void\r
diff --git a/Unzip.cc b/Unzip.cc
deleted file mode 100644 (file)
index 2f102cf..0000000
--- a/Unzip.cc
+++ /dev/null
@@ -1,129 +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"
-
-// Simple Unzip implementation
-// Limitations:
-// - Does not support spanned archives.
-// - Does not support spanned encryption.
-// - Does not support zip64
-
-
-uint32_t
-read32(const std::vector<uint8_t>& zipData, size_t pos)
-{
-       // Read 4 bytes in little-endian order.
-       // Return results in host-endian.
-       return uint32_t(
-               zipData[pos] |
-               (static_cast<uint32_t>(zipData[pos+1]) << 8) |
-               (static_cast<uint32_t>(zipData[pos+2]) << 16) |
-               (static_cast<uint32_t>(zipData[pos+3]) << 24)
-               );
-}
-
-uint16_t
-read16(const std::vector<uint8_t>& zipData, size_t pos)
-{
-       // Read 2 bytes in little-endian order.
-       // Return results in host-endian.
-       return uint16_t(
-               zipData[pos] |
-               (static_cast<uint16_t>(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<uint8_t>& 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;
-}
-
index 3f026c45f72eff1bbf0ac9f292910399a8ffd1cf..c9b18c06bafb694d4971463c20b0ac4215fd6c99 100755 (executable)
@@ -1,2 +1,3 @@
 #!/bin/sh
+libzipper/autogen.sh
 autoreconf --force --install
index 00427766977009a33ac4b261ed6b680e6b729fca..89fbefb127fa90b16f1c712a9403fc6f95737419 100644 (file)
@@ -2,6 +2,7 @@ AC_INIT([glBoy], [1.0.1], [michael@codesrc.com])
 AC_CANONICAL_HOST
 AC_CANONICAL_TARGET
 AM_INIT_AUTOMAKE([foreign])
+AC_CONFIG_SUBDIRS([libzipper])
 AC_CONFIG_HEADERS([autoconfig.h])
 AC_CONFIG_FILES([Makefile])
 
@@ -11,6 +12,7 @@ AC_C_BIGENDIAN(
        )
 
 AC_PROG_CXX
+AC_PROG_LIBTOOL
 
 SDL_VERSION=1.2.0
 AM_PATH_SDL(
diff --git a/libzipper b/libzipper
new file mode 160000 (submodule)
index 0000000..f275ba4
--- /dev/null
+++ b/libzipper
@@ -0,0 +1 @@
+Subproject commit f275ba42be259b90a32a5b7f9399bc175c6e4c4e
index 05698d983e4913ba1ce3c22878cd41107ff753aa..d6f8847f9741e62f05fc2c2f56c3271343bddf92 100644 (file)
@@ -351,16 +351,17 @@ sub processInstruction
 
        my @dbgRegs = createRegReference($mask);
 
-       print CODE "#ifdef GLBOY_DEBUG\n";
-       print CODE "std::ostream& trace(log(Log::DBG_TRACE));\n".
+       print CODE "//#ifdef GLBOY_DEBUG\n";
+       print CODE "std::ostream& trace(std::cerr);//(log(Log::DBG_TRACE));\n".
                "trace << ".
-               "opcodePC << \": \";\n".
+               "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 "//#endif\n";
 
        my $statements = $node->findvalue('./text()');
        chomp($statements);