#include <sstream>
#include <iomanip>
-u8 GBIO::read(int addr) const
-{
- return ports[addr-IO_BASE];
-}
-
-void GBIO::write(int addr, u8 value)
-{
- ports[addr-IO_BASE] = value;
-}
-
void GBMemory::write(int addr, u8 value)
{
if (addr < 0x8000) mbc->write(addr, value);
else if (addr < 0xE000) WRAM[addr - WRAM_BASE] = value;
else if (addr < 0xFE00) write(addr-0x2000, value);
else if (addr < 0xFEA0) core->video.write_OAM (addr, value);
- else if (addr >= 0xFF00 && addr <= 0xFF7F) IO.write(addr, value);
- else if (addr >= 0xFF80 && addr <= 0xFFFE) HRAM[addr - HRAM_BASE]=value;
- else if (addr == 0xFFFF) IE=value;
+ else if (addr >= 0xFF00) high[addr-0xFF00] = value;
else {
std::ostringstream errmsg;
errmsg << "Invalid write address 0x" <<
else if (addr < 0xE000) return WRAM[addr - WRAM_BASE];
else if (addr < 0xFDFF) return read(addr-0x2000);
else if (addr < 0xFEA0) return core->video.read_OAM (addr);
- else if (addr >= 0xFF00 && addr <= 0xFF7F) return IO.read(addr);
- else if (addr >= 0xFF80 && addr <= 0xFFFE) return HRAM[addr - HRAM_BASE];
- else if (addr == 0xFFFF) return IE;
+ else if (addr >= 0xFF00) return high[addr-0xFF00];
else {
std::ostringstream errmsg;
errmsg << "Invalid read address 0x" <<
}
}
-
+u16 GBMemory::read16(int addr) const
+{
+ if (addr < 0x8000) return mbc->read16(addr);
+ else if (addr < 0xA000) return core->video.read16_VRAM(addr);
+ else if (addr < 0xC000) return mbc->read16(addr);
+ else if (addr < 0xE000) return WRAM[addr - WRAM_BASE] + (WRAM[addr - WRAM_BASE + 1] << 8);
+ else if (addr < 0xFDFF) return read16(addr-0x2000);
+ else if (addr < 0xFEA0) return core->video.read16_OAM (addr);
+ else if (addr >= 0xFF00) return high[addr-0xFF00] + (high[addr-0xFF00+1] << 8);
+ else {
+ std::ostringstream errmsg;
+ errmsg << "Invalid read address 0x" <<
+ std::hex << std::setw(4) << std::setfill('0') << addr;
+ logger.error(errmsg.str());
+ return *(static_cast<u8*>(0));
+ }
+}
class GameBoy;
class MBC;
-class GBIO
+class GBMemory
{
friend class GameBoy;
friend class GBVideo;
- u8 ports[128];
+ GameBoy *core;
+ MBC *mbc;
+ // 0000-3FFF: ROM Bank 0 (in cart)
+ // 4000-7FFF: Switchable ROM Bank (in cart)
+ // 8000-9FFF: Video RAM
+ // A000-BFFF: External RAM (in cart, switchable)
+ u8 WRAM[8192]; // C000-DFFF: Work RAM
+ // E000-FDFF: ECHO: Same as C000-DDFF
+ // FE00-FE9F: Sprite Attribute Table (OAM)
+ // FEA0-FEFF: Not usable
+ u8 high[256]; // FF80-FFFE: High RAM
+
+ public:
+
+ static const u16 VRAM_BASE = 0x8000;
+ static const u16 EXTERNAL_RAM_BASE = 0xA000;
+ static const u16 WRAM_BASE = 0xC000;
+ static const u16 OAM_BASE = 0xFE00;
+ static const u16 IO_BASE = 0xFF00;
+ static const u16 HRAM_BASE = 0xFF80;
+
+ GBMemory(GameBoy *core): core(core), mbc(0), high() {}
+ void init(MBC *mbc) { this->mbc = mbc; }
+
+
+ u8 read(int addr) const;
+ u16 read16(int addr) const;
+ void write(int addr, u8 value);
public:
- static const u16 IO_BASE = 0xFF00;
static const u16 LCDC = 0xFF40; // LCD Control (R/W)
static const u16 STAT = 0xFF41; // LCD Status (R/W)
static const u16 SCY = 0xFF42; // Scroll Y (R/W)
static const u16 OBP1 = 0xFF49; // Object Pallete 1 data(R/W)
static const u16 DMA = 0xFF46; // DMA Transfer & Start addr (W)
static const u16 IF = 0xFF0F; // Interrupt flag (R/W)
+ static const u16 IE = 0xFF0F; // Interrupt enable (R/W)
private:
static const u16 I_LCDC = 0xFF40 - IO_BASE; // LCD Control (R/W)
static const u16 I_OBP1 = 0xFF49 - IO_BASE; // Object Pallete 1 data(R/W)
static const u16 I_DMA = 0xFF46 - IO_BASE; // DMA Transfer & Start addr (W)
static const u16 I_IF = 0xFF0F - IO_BASE; // Interrupt flag (R/W)
-
- public:
- u8 read(int addr) const;
- void write(int addr, u8 value);
-
-};
-
-class GBMemory
-{
- friend class GameBoy;
- friend class GBVideo;
-
- GameBoy *core;
- MBC *mbc;
- // 0000-3FFF: ROM Bank 0 (in cart)
- // 4000-7FFF: Switchable ROM Bank (in cart)
- // 8000-9FFF: Video RAM
- // A000-BFFF: External RAM (in cart, switchable)
- u8 WRAM[8192]; // C000-DFFF: Work RAM
- // E000-FDFF: ECHO: Same as C000-DDFF
- // FE00-FE9F: Sprite Attribute Table (OAM)
- // FEA0-FEFF: Not usable
- GBIO IO; // FF00-FF7F: IO ports
-
- u8 HRAM[126]; // FF80-FFFE: High RAM
- u8 IE; // FFFF : Interrupt Enable
-
- public:
-
- static const u16 VRAM_BASE = 0x8000;
- static const u16 EXTERNAL_RAM_BASE = 0xA000;
- static const u16 WRAM_BASE = 0xC000;
- static const u16 OAM_BASE = 0xFE00;
- static const u16 IO_BASE = 0xFF00;
- static const u16 HRAM_BASE = 0xFF80;
-
- GBMemory(GameBoy *core): core(core), mbc(0), IO(), IE(0) {}
- void init(MBC *mbc) { this->mbc = mbc; }
-
-
- u8 read(int addr) const;
- void write(int addr, u8 value);
-
+ static const u16 I_IE = 0xFF0F - IO_BASE; // Interrupt enable (R/W)
};
SDL_Quit();
}
+#if 0
u8 GBVideo::read_VRAM (int addr) const
{
- int STAT = core->memory.IO.ports[GBIO::I_STAT];
- if ((STAT & 3) == 3)
- return 0xFF; // VRAM access disabled
- else
+ //int STAT = core->memory.high[GBMemory::I_STAT];
+ //if ((STAT & 3) == 3)
+ // return 0xFF; // VRAM access disabled
+ //else
return VRAM[addr-VRAM_BASE];
}
u8 GBVideo::read_OAM (int addr) const
{
- int STAT = core->memory.IO.ports[GBIO::I_STAT];
- if ((STAT & 3) >= 2)
- return 0xFF; // OAM access disabled
- else
+ //int STAT = core->memory.high[GBMemory::I_STAT];
+ //if ((STAT & 3) >= 2)
+ // return 0xFF; // OAM access disabled
+ //else
return OAM[addr-OAM_BASE];
}
+u16 GBVideo::read16_VRAM (int addr) const
+{
+ //int STAT = core->memory.high[GBMemory::I_STAT];
+ //if ((STAT & 3) == 3)
+ // return 0xFF; // VRAM access disabled
+ //else
+ return VRAM[addr-VRAM_BASE]+(VRAM[addr-VRAM_BASE+1] << 8);
+}
+
+u16 GBVideo::read16_OAM (int addr) const
+{
+ //int STAT = core->memory.high[GBMemory::I_STAT];
+ //if ((STAT & 3) >= 2)
+ // return 0xFF; // OAM access disabled
+ //else
+ return OAM[addr-OAM_BASE]+(OAM[addr-OAM_BASE+1] << 8);
+}
+
void GBVideo::write_VRAM(int addr, u8 value)
{
- int STAT = core->memory.IO.ports[GBIO::I_STAT];
- if ((STAT & 3) == 3)
- return; // VRAM access disabled
- else
+ //int STAT = core->memory.high[GBMemory::I_STAT];
+ //if ((STAT & 3) == 3)
+ // return; // VRAM access disabled
+ //else
VRAM[addr-VRAM_BASE] = value;
}
void GBVideo::write_OAM (int addr, u8 value)
{
- int STAT = core->memory.IO.ports[GBIO::I_STAT];
- if ((STAT & 3) >= 2)
- return; // OAM access disabled
- else
+ //int STAT = core->memory.high[GBMemory::I_STAT];
+ //if ((STAT & 3) >= 2)
+ // return; // OAM access disabled
+ //else
OAM[addr-OAM_BASE] = value;
}
+#endif
+
void GBVideo::update()
{
//Mode 0 is present between 201-207 clks, 2 about 77-83 clks, and 3
if (cycles_until_next_update == 0)
{
- int STAT = core->memory.IO.ports[GBIO::I_STAT];
- int LYC = core->memory.IO.ports[GBIO::I_LYC];
- int LY = core->memory.IO.ports[GBIO::I_LY];
+ int STAT = core->memory.high[GBMemory::I_STAT];
+ int LYC = core->memory.high[GBMemory::I_LYC];
+ int LY = core->memory.high[GBMemory::I_LY];
switch (mode)
{
}
SDL_Flip(display);
frames_rendered++;
- char buf[50];
- sprintf(buf, "%d", frames_rendered);
- SDL_WM_SetCaption(buf, 0);
+ if (frames_rendered % 10 == 0)
+ {
+ char buf[50];
+ sprintf(buf, "%d", frames_rendered);
+ SDL_WM_SetCaption(buf, 0);
+ }
// preserve bits 3-6, set mode to 1 (VBlank) and coincidence to 0
STAT = (STAT&0xF8) | 1;
{
LY = (LY+1)%154;
logger.trace(LY);
- core->memory.IO.ports[GBIO::I_LY] = LY;
+ core->memory.high[GBMemory::I_LY] = LY;
}
- core->memory.IO.ports[GBIO::I_STAT] = STAT;
+ core->memory.high[GBMemory::I_STAT] = STAT;
}
--cycles_until_next_update;
u32 *pixels = static_cast<u32*>(display->pixels);
u32 pixels_per_line = display->pitch/display->format->BytesPerPixel;
- int LCDC = core->memory.IO.ports[GBIO::I_LCDC];
- int LY = core->memory.IO.ports[GBIO::I_LY];
+ int LCDC = core->memory.high[GBMemory::I_LCDC];
+ int LY = core->memory.high[GBMemory::I_LY];
if (LY < 144 && display_mode == NORMAL)
{
// Draw the background
// Draw at hline_t == 80, when the app cannot write to neither VRAM nor OAM
- int BGP = core->memory.IO.ports[GBIO::I_BGP];
+ int BGP = core->memory.high[GBMemory::I_BGP];
int pallette[4];
pallette[0] = BGP & 3;
pallette[1] = (BGP>>2) & 3;
// (vx , vy ) -> position of the pixel in the 256x256 bg
// (map_x , map_y ) -> map coordinates of the current tile
// (tile_x, tile_y) -> position of the pixel in the tile
- int SCX = core->memory.IO.ports[GBIO::I_SCX];
- int SCY = core->memory.IO.ports[GBIO::I_SCY];
+ int SCX = core->memory.high[GBMemory::I_SCX];
+ int SCY = core->memory.high[GBMemory::I_SCY];
int vy = (LY + SCY) % 256;
int map_y = vy / 8;
int tile_y = vy % 8;
}
else if (display_mode == BG_MAP)
{
- int BGP = core->memory.IO.ports[GBIO::I_BGP];
+ int BGP = core->memory.high[GBMemory::I_BGP];
int pallette[4];
pallette[0] = BGP & 3;
pallette[1] = (BGP>>2) & 3;
~GBVideo();
// VRAM/OAM access
- u8 read_VRAM (int addr) const;
- u8 read_OAM (int addr) const;
- void write_VRAM(int addr, u8 value);
- void write_OAM (int addr, u8 value);
+ inline u8 read_VRAM (int addr) const { return VRAM[addr-VRAM_BASE]; }
+ inline u8 read_OAM (int addr) const { return OAM[addr-OAM_BASE]; }
+ inline u16 read16_VRAM (int addr) const { return VRAM[addr-VRAM_BASE]+(VRAM[addr-VRAM_BASE+1] << 8); }
+ inline u16 read16_OAM (int addr) const { return OAM[addr-OAM_BASE]+(OAM[addr-OAM_BASE+1] << 8); }
+ inline void write_VRAM(int addr, u8 value) { VRAM[addr-VRAM_BASE] = value; }
+ inline void write_OAM (int addr, u8 value) { OAM[addr-OAM_BASE] = value; }
// drawing control
void draw();
{
if (addr <= 0x7FFF)
return ROM[addr];
- else if ((addr&0xE000) == 0xA000) //(addr >= 0xA000 && addr <= 0xBFFF)
+ else //if ((addr&0xE000) == 0xA000) //(addr >= 0xA000 && addr <= 0xBFFF)
return RAM[addr-0xA000];
- else
- logger.error("NoMBC: Incorrect read");
+ //else
+ // logger.error("NoMBC: Incorrect read");
+ return 0;
+}
+
+u16 NoMBC::read16(int addr) const
+{
+ if (addr <= 0x7FFF)
+ return ROM[addr]+(ROM[addr+1] << 8);
+ else //if ((addr&0xE000) == 0xA000) //(addr >= 0xA000 && addr <= 0xBFFF)
+ return RAM[addr-0xA000] + (RAM[addr-0xA000+1] << 8);
+ //else
+ // logger.error("NoMBC: Incorrect read");
return 0;
}
{
public:
virtual u8 read(int addr) const=0;
+ virtual u16 read16(int addr) const=0;
virtual void write(int addr, u8 value)=0;
virtual ~MBC() {};
};
u8 read (int addr) const;
void write(int addr, u8 value);
+ u16 read16(int addr) const;
};
-CXXFLAGS=-O3 -g -Wall -Weffc++ -Wstrict-null-sentinel -Wold-style-cast \
+CXXFLAGS=-pg -O3 -g -Wall -Weffc++ -Wstrict-null-sentinel -Wold-style-cast \
-Woverloaded-virtual $(shell sdl-config --cflags)
-LDFLAGS=-g $(shell sdl-config --libs)
+LDFLAGS=-pg -g $(shell sdl-config --libs)
all: tests
regs.A = memory.read(regs.DE);
break;
case 0xFA: // LD A, (nn)
- regs.A = memory.read(memory.read(regs.PC) + (memory.read(regs.PC+1)<<8));
+ regs.A = memory.read(memory.read16(regs.PC));
regs.PC+=2;
break;
memory.write(regs.DE, regs.A);
break;
case 0xEA: // LD (nn), A
- memory.write(memory.read(regs.PC) + (memory.read(regs.PC+1)<<8), regs.A);
+ memory.write(memory.read16(regs.PC), regs.A);
regs.PC+=2;
break;
// LDH (n), A
case 0xE0: {
- memory.write(0xFF00 + memory.read(regs.PC++), regs.A);
+ memory.high[memory.read(regs.PC++)] = regs.A;
break;
}
// LDH A, (n)
case 0xF0:
- regs.A = memory.read(0xFF00 + memory.read(regs.PC++));
+ regs.A = memory.high[memory.read(regs.PC++)];
break;
// LD n, nn
case 0x01: // LD BC, nn
- regs.BC = memory.read(regs.PC)+(memory.read(regs.PC+1) << 8);
+ regs.BC = memory.read16(regs.PC);
regs.PC +=2;
break;
case 0x11: // LD DE, nn
- regs.DE = memory.read(regs.PC)+(memory.read(regs.PC+1) << 8);
+ regs.DE = memory.read16(regs.PC);
regs.PC +=2;
break;
case 0x21: // LD HL, nn
- regs.HL = memory.read(regs.PC)+(memory.read(regs.PC+1) << 8);
+ regs.HL = memory.read16(regs.PC);
regs.PC +=2;
break;
case 0x31: // LD SP, nn
- regs.SP = memory.read(regs.PC)+(memory.read(regs.PC+1) << 8);
+ regs.SP = memory.read16(regs.PC);
regs.PC +=2;
break;
// LD (nn), SP
case 0x08: {
- int addr = memory.read(regs.PC) + (memory.read(regs.PC+1) << 8);
+ int addr = memory.read16(regs.PC);
regs.PC += 2;
memory.write(addr, regs.SP);
break;
// Jumps
// JP nn
case 0xC3:
- regs.PC = memory.read(regs.PC) | (memory.read(regs.PC+1)<<8);
+ regs.PC = memory.read16(regs.PC);
break;
// JP cc, nn
case 0xC2: { // JP NZ, nn
- u16 dst = memory.read(regs.PC) | (memory.read(regs.PC+1)<<8);
+ u16 dst = memory.read16(regs.PC);
if (!check_flag(ZERO_FLAG))
regs.PC = dst;
else
}
case 0xCA: { // JP Z, nn
- u16 dst = memory.read(regs.PC) | (memory.read(regs.PC+1)<<8);
+ u16 dst = memory.read16(regs.PC);
if (check_flag(ZERO_FLAG))
regs.PC = dst;
else
}
case 0xD2: { // JP NC, nn
- u16 dst = memory.read(regs.PC) | (memory.read(regs.PC+1)<<8);
+ u16 dst = memory.read16(regs.PC);
if (!check_flag(CARRY_FLAG))
regs.PC = dst;
else
}
case 0xDA: { // JP C, nn
- u16 dst = memory.read(regs.PC) | (memory.read(regs.PC+1)<<8);
+ u16 dst = memory.read16(regs.PC);
if (check_flag(CARRY_FLAG))
regs.PC = dst;
else
// Calls
// CALL nn
case 0xCD: {
- u16 addr = memory.read(regs.PC) | (memory.read(regs.PC+1)<<8);
+ u16 addr = memory.read16(regs.PC);
regs.PC += 2;
do_call(addr);
break;
// CALL cc, nn
case 0xC4: { // CALL NZ, nn
if (!check_flag(ZERO_FLAG)) {
- u16 addr = memory.read(regs.PC) | (memory.read(regs.PC+1)<<8);
+ u16 addr = memory.read16(regs.PC);
regs.PC += 2;
do_call(addr);
} else {
case 0xCC: { // CALL Z, nn
if (check_flag(ZERO_FLAG)) {
- u16 addr = memory.read(regs.PC) | (memory.read(regs.PC+1)<<8);
+ u16 addr = memory.read16(regs.PC);
regs.PC += 2;
do_call(addr);
} else {
case 0xD4: { // CALL NC, nn
if (!check_flag(CARRY_FLAG)) {
- u16 addr = memory.read(regs.PC) | (memory.read(regs.PC+1)<<8);
+ u16 addr = memory.read16(regs.PC);
regs.PC += 2;
do_call(addr);
} else {
case 0xDC: { // CALL C, nn
if (check_flag(CARRY_FLAG)) {
- u16 addr = memory.read(regs.PC) | (memory.read(regs.PC+1)<<8);
+ u16 addr = memory.read16(regs.PC);
regs.PC += 2;
do_call(addr);
} else {
// Returns
// RET
case 0xC9: {
- u16 retaddr = (memory.read(regs.SP+1)<<8) | memory.read(regs.SP);
+ u16 retaddr = memory.read16(regs.SP);
regs.SP += 2;
regs.PC = retaddr;
break;
// RET cc
case 0xC0: // RET NZ
if (!check_flag(ZERO_FLAG)) {
- u16 retaddr = (memory.read(regs.SP+1)<<8) | memory.read(regs.SP);
+ u16 retaddr = memory.read16(regs.SP);
regs.SP += 2;
regs.PC = retaddr;
}
case 0xC8: // RET Z
if (check_flag(ZERO_FLAG)) {
- u16 retaddr = (memory.read(regs.SP+1)<<8) | memory.read(regs.SP);
+ u16 retaddr = memory.read16(regs.SP);
regs.SP += 2;
regs.PC = retaddr;
}
case 0xD0: // RET NC
if (!check_flag(CARRY_FLAG)) {
- u16 retaddr = (memory.read(regs.SP+1)<<8) | memory.read(regs.SP);
+ u16 retaddr = memory.read16(regs.SP);
regs.SP += 2;
regs.PC = retaddr;
}
case 0xD8: // RET C
if (check_flag(CARRY_FLAG)) {
- u16 retaddr = (memory.read(regs.SP+1)<<8) | memory.read(regs.SP);
+ u16 retaddr = memory.read16(regs.SP);
regs.SP += 2;
regs.PC = retaddr;
}
// RETI
case 0xD9: {
// RET && EI
- u16 retaddr = (memory.read(regs.SP+1)<<8) | memory.read(regs.SP);
+ u16 retaddr = memory.read16(regs.SP);
regs.SP += 2;
regs.PC = retaddr;
IME=1;
video.update();
// Check for interrupts before opcode fetching
- u8 IE=memory.IE;
+ u8 IE=memory.high[GBMemory::I_IE];
//logger.trace("IME=", int(IME), " IE=", int(IE));
if (IME && IE)
{
- u8 IF = memory.IO.ports[GBIO::I_IF];
+ u8 IF = memory.high[GBMemory::I_IF];
//logger.trace("Dispatching interrupts: IE=", int(IE), " IF=", int(IF));
if (IF)
{
logger.trace("JOYPAD IRQ");
}
}
- memory.IO.ports[GBIO::I_IF] = IF;
+ memory.high[GBMemory::I_IF] = IF;
}
-
+ /*
for(BreakpointMap::iterator i=breakpoints.begin();
i != breakpoints.end();
i++)
if (i->second.addr == regs.PC && i->second.enabled)
return BREAKPOINT;
}
+ */
return NORMAL;
}