--- /dev/null
+#include "GBMemory.h"
+#include "MBC.h"
+#include "gbcore.h"
+
+u8& GBMemory::operator[](unsigned int addr)
+{
+ if (addr < 0x8000) return (*mbc)[addr];
+ else if (addr < 0xA000) return VRAM[addr-0x8000];
+ else if (addr < 0xC000) return (*mbc)[addr];
+ else if (addr < 0xD000) return WRAM0[addr-0xC000];
+ else if (addr < 0xE000) return WRAM1[addr-0xD000];
+ else if (addr < 0xFDFF) return (*mbc)[addr-0x2000];
+ else if (addr < 0xFEA0) return OAM[addr-0xFDFF];
+ else if (addr >= 0xFF00 && addr <= 0xFF7F)
+ return core->IO.addr[addr-0xFF00];
+ else if (addr >= 0xFF80 && addr <= 0xFFFE) return HRAM[addr-0xFF80];
+ else {
+ logger.error("Invalid write address");
+ return *(static_cast<u8*>(0));
+ }
+}
+
+
+u8 GBMemory::operator[](unsigned int addr) const
+{
+ if (addr < 0x8000) return (*mbc)[addr];
+ else if (addr < 0xA000) return VRAM[addr-0x8000];
+ else if (addr < 0xC000) return (*mbc)[addr];
+ else if (addr < 0xD000) return WRAM0[addr-0xC000];
+ else if (addr < 0xE000) return WRAM1[addr-0xD000];
+ else if (addr < 0xFDFF) return (*mbc)[addr-0x2000];
+ else if (addr < 0xFEA0) return OAM[addr-0xFDFF];
+ else if (addr >= 0xFF00 && addr <= 0xFF7F)
+ return core->IO.addr[addr-0xFF00];
+ else if (addr >= 0xFF80 && addr <= 0xFFFE) return HRAM[addr-0xFF80];
+ else {
+ logger.error("Invalid write address");
+ return *(static_cast<u8*>(0));
+ }
+}
+
+
+
#define GBMEMORY_H
#include "sized_types.h"
-#include "MBC.h"
+
+class GameBoy;
+class MBC;
class GBMemory
{
+ GameBoy *core;
MBC *mbc;
// 0000-3FFF: ROM Bank 0 (in cart)
// 4000-7FFF: Switchable ROM Bank (in cart)
u8 HRAM[126]; // FF80-FFFE: High RAM
public:
- GBMemory(): mbc(0) {}
+ GBMemory(GameBoy *core): core(core), mbc(0) {}
void init(MBC *mbc) { this->mbc = mbc; }
- int& operator[](int index)=0;
- int operator[](int index) const=0;
+ u8& operator[](unsigned int addr);
+ u8 operator[](unsigned int addr) const;
};
#include "sized_types.h"
#include <string>
-namespace cartrigde_types {
+namespace cartridge_types {
const u8 ROM_ONLY=0x00;
const u8 MBC1=0x01;
const u8 MBC1_RAM=0x02;
--- /dev/null
+#include "MBC.h"
+#include "logger.h"
+
+using namespace cartridge_types;
+
+MBC *create_MBC(GBRom *rom)
+{
+ switch (rom->header.cartridge_type)
+ {
+ case ROM_ONLY: return new NoMBC(rom);
+ //case MBC1: return new MBC1(rom);
+ default:
+ logger.critical("Unsupported cartridge type");
+ return 0;
+ }
+}
#define MBC_H
#include "sized_types.h"
+#include "GBRom.h"
+#include "logger.h"
+#include <cstring>
class MBC
{
- virtual u8 read_byte() const=0;
- virtual void write_byte(u8)=0;
+ public:
+ virtual u8 operator[](unsigned int addr) const=0;
+ virtual u8& operator[](unsigned int addr)=0;
+ virtual ~MBC();
};
+class NoMBC: public MBC
+{
+ u8 ROM[32768];
+ u8 RAM[8192];
+
+ public:
+ NoMBC(GBRom *rom) { memcpy(ROM, rom->data, 32768); }
+
+ u8 operator[](unsigned int addr) const
+ {
+ if (addr <= 0x7FFF)
+ return ROM[addr];
+ else if ((addr&0xE000) == 0xA000) //(addr >= 0xA000 && addr <= 0xBFFF)
+ return RAM[addr-0xA000];
+ else
+ logger.error("NoMBC: Incorrect read");
+ return 0;
+ }
+
+ u8& operator[](unsigned int addr)
+ {
+ if ((addr&0xE000) == 0xA000) //(addr >= 0xA000 && addr <= 0xBFFF)
+ return RAM[addr-0xA000];
+ else
+ logger.error("NoMBC: trying to write in ROM");
+ return *(static_cast<u8*>(0)); // Shouldn't happen
+ }
+};
+
+
+MBC *create_MBC(GBRom *rom);
+
#endif // MBC_H
CXXFLAGS=-g -Wall -Weffc++ -Wstrict-null-sentinel -Wold-style-cast \
-Woverloaded-virtual
-all: gbcore.o
+all: gbcore.o MBC.o GBMemory.o logger.o
tests: tests/test_gbrom
-gbcore.o: gbcore.cc gbcore.h gbrom.h
+logger.o: logger.cc logger.h
+ g++ $(CXXFLAGS) -c -o $@ $<
+
+GBMemory.o: GBMemory.cc GBMemory.h
+ g++ $(CXXFLAGS) -c -o $@ $<
+
+MBC.o: MBC.cc MBC.h
+ g++ $(CXXFLAGS) -c -o $@ $<
+
+gbcore.o: gbcore.cc gbcore.h GBRom.h logger.h MBC.h GBMemory.h
g++ $(CXXFLAGS) -c -o $@ $<
-tests/test_gbrom: gbrom.cc gbrom.h
+tests/test_gbrom: GBRom.cc GBRom.h
g++ -DTEST_GBROM -o $@ $<
clean:
#include "gbcore.h"
-#include "sized_types.h"
#include "GBRom.h"
-#include "GBMemory.h"
#include "MBC.h"
#include "logger.h"
#include <string>
#include <cstring>
-class MBC
-{
- virtual u8 read_byte() const=0;
- virtual void write_byte(u8)=0;
-};
-
-class GBMemory
-{
- MBC *mbc;
- // 0000-3FFF: ROM Bank 0 (in cart)
- // 4000-7FFF: Switchable ROM Bank (in cart)
- u8 VRAM[8192]; // 8000-9FFF: Video RAM
- // A000-BFFF: External RAM (in cart, switchable)
- u8 WRAM0[4096]; // C000-CFFF: Work RAM Bank 0
- u8 WRAM1[4096]; // D000-DFFF: Work RAM Bank 1 (TODO: In GBC mode switchable bank 1-7)
- // E000-FDFF: ECHO: Same as C000-DDFF
- u8 OAM[160]; // FE00-FE9F: Sprite Attribute Table
- u8 HRAM[126]; // FF80-FFFE: High RAM
-
- public:
- GBMemory(): mbc(0) {}
- void init(MBC *mbc) { this->mbc = mbc; }
-
-
- int& operator[](int index)=0;
- int operator[](int index) const=0;
-
-};
-
-class GameBoy
-{
- GBMemory memory;
- GBRom *rom;
-
- enum flags_enum
- {
- ZERO_FLAG=0x80,
- ADD_SUB_FLAG=0x40,
- HALF_CARRY_FLAG=0x20,
- CARRY_FLAG=0x10,
- };
-
- // CPU Registers
- // ENDIANNESS WARNING!
- struct
- {
- union
- {
- u16 AF;
- struct { u8 flags; u8 A; };
- };
- union
- {
- u16 BC;
- struct { u8 C; u8 B; };
- };
- union
- {
- u16 DE;
- struct { u8 E; u8 D; };
- };
- union
- {
- u16 HL;
- struct { u8 L; u8 H; };
- };
- u16 SP;
- u16 PC;
-
- } __attribute__((packed)) regs;
-
- u8 IME; // Interrupt master enable flag
- u8 HALT; // Is the CPU halted waiting for an interrupt?
-
- void set_flag(const u8 f) { regs.flags |= f; }
- void reset_flag(const u8 f) { regs.flags &= (~f); }
- bool check_flag(const u8 f) { return (regs.flags & f != 0); }
-
- public:
- GameBoy(std::string rom_name);
-
- void reset();
- void run_cycle();
- void run();
-
-};
-
GameBoy::GameBoy(std::string rom_name):
- rom(0), regs(), IME(1), HALT(0)
+ memory(this),rom(0), regs(), IO(), IME(1), HALT(0)
{
logger.info("GameBoy init");
rom = read_gbrom(rom_name);
+
+ MBC *mbc = create_MBC(rom);
+ memory.init(mbc);
+
reset();
}
void GameBoy::reset()
{
logger.info("GameBoy reset");
- std::memcpy(memory, rom->data, 16384);
regs.PC = 0x100;
}
// LD HL, SP+n
// LDHL SP, n
case 0xF8: {
- s8 offset = *(reinterpret_cast<s8*>(memory+regs.PC++));
+ s8 offset = memory[regs.PC++];
int res = regs.SP + offset;
// TODO: Verificar si los flags van asi
// ADD SP, #
case 0xE8: {
// FIXME: No se que hacer con el half carry, en 4 o en 11?
- int n = *(reinterpret_cast<s8*>(memory+regs.PC++));
+ int n = static_cast<s8>(memory[regs.PC++]);
int res = regs.SP + n;
regs.SP = static_cast<u8>(res);
reset_flag(ZERO_FLAG);
+#ifndef GBCORE_H
+#define GBCORE_H
+
+#include "sized_types.h"
+#include "GBMemory.h"
+#include <string>
+
+union GBRom;
+
+class GameBoy
+{
+ friend class GBMemory;
+ GBMemory memory;
+ GBRom *rom;
+
+ enum flags_enum
+ {
+ ZERO_FLAG=0x80,
+ ADD_SUB_FLAG=0x40,
+ HALF_CARRY_FLAG=0x20,
+ CARRY_FLAG=0x10,
+ };
+
+ // CPU Registers
+ // ENDIANNESS WARNING!
+ struct
+ {
+ union
+ {
+ u16 AF;
+ struct { u8 flags; u8 A; };
+ };
+ union
+ {
+ u16 BC;
+ struct { u8 C; u8 B; };
+ };
+ union
+ {
+ u16 DE;
+ struct { u8 E; u8 D; };
+ };
+ union
+ {
+ u16 HL;
+ struct { u8 L; u8 H; };
+ };
+ u16 SP;
+ u16 PC;
+
+ } __attribute__((packed)) regs;
+
+
+ union
+ {
+ struct
+ {
+ };
+
+ u8 addr[128];
+
+ } IO;
+
+ u8 IME; // Interrupt master enable flag
+ u8 HALT; // Is the CPU halted waiting for an interrupt?
+
+ void set_flag(const u8 f) { regs.flags |= f; }
+ void reset_flag(const u8 f) { regs.flags &= (~f); }
+ bool check_flag(const u8 f) { return (regs.flags & f != 0); }
+
+ public:
+ GameBoy(std::string rom_name);
+
+ void reset();
+ void run_cycle();
+ void run();
+
+};
+
+#endif
static Logger instance;
public:
- static Logger& getInstance() { return instance; }
- Logger(std::ostream& os): out(os) {}
-
enum log_level{
OFF = -1,
CRITICAL=0,
TRACE=5
};
+ static Logger& getInstance() { return instance; }
+ Logger(std::ostream& os):
+ out(os),
+ current_log_level(TRACE) {}
+
log_level current_log_level;
void info (std::string str) { log(INFO , str); }
void debug (std::string str) { log(DEBUG , str); }
void trace (std::string str) { log(TRACE , str); }
+
+ void set_log_level (log_level level) { current_log_level = level; }
};
#define logger Logger::getInstance()