--- /dev/null
+[submodule "libzipper"]
+ path = libzipper
+ url = git://www.codesrc.com/git/libzipper
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
((((~(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
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
\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
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
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
#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
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
// 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
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
#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
\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
#include "MemoryMap.hh"\r
#include "Registers.hh"\r
\r
-#include <deque>\r
#include <tr1/functional>\r
#include <functional>\r
#include <vector>\r
// 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
#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;
{
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:
{
}; 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;
}
}
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;
+}
#include "glBoy.hh"
#include "Core.hh"
+#include <ostream>
#include <vector>
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
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 (
(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;
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;
GameboySound::GameboySound(Core& core)
{
- extern void mixaudio(void *unused, Uint8 *stream, int len);
SDL_AudioSpec fmt;
fmt.freq = SAMPLE_RATE;
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 << 4;\r
+ r |= (tmp >> 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
#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()
{
}
Log::Log() :
- m_levelMask(7),
- m_nullStream(&m_nullBuf)
+ m_levelMask(15),
+ m_currentLevel(DBG_NONE)
{
char* env(getenv("GLBOY_DEBUG_LEVEL"));
if (env)
}
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;
}
#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&);
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
--- /dev/null
+// 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;
+ }
+ }
+}
+
--- /dev/null
+// 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
+
--- /dev/null
+// 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.
+ }
+}
+
--- /dev/null
+// 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
+
--- /dev/null
+// 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;
+ }
+ }
+}
+
--- /dev/null
+// 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
+
#include <SDL/SDL_video.h>
#include <libconfig.h++>
-#include <zipios++/zipfile.h>
+#include <zipper.hh>
#include <cassert>
#include <set>
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);
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;*/
}
}
}
- 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)
static struct option longOptions[] =
{
+ {"info", 0, NULL, 'I'},
{"video", 1, NULL, 'v'},
{"scale", 1, NULL, 's'},
{"width", 1, NULL, 'w'},
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);
}; break;
case '?':
- usage(argv[0]);
+ usage();
exit(1);
break;
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);
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);
# 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
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 \
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
);\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
\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
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
- 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
\r
\r
private:\r
+ bool m_fourBitMode;\r
std::vector<uint8_t> m_mem;\r
};\r
\r
--- /dev/null
+// 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
--- /dev/null
+// 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
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
+++ /dev/null
-// 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;
-}
-
#!/bin/sh
+libzipper/autogen.sh
autoreconf --force --install
AC_CANONICAL_HOST
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE([foreign])
+AC_CONFIG_SUBDIRS([libzipper])
AC_CONFIG_HEADERS([autoconfig.h])
AC_CONFIG_FILES([Makefile])
)
AC_PROG_CXX
+AC_PROG_LIBTOOL
SDL_VERSION=1.2.0
AM_PATH_SDL(
--- /dev/null
+Subproject commit f275ba42be259b90a32a5b7f9399bc175c6e4c4e
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);