#include "GBVideo.h"
#include "gbcore.h"
+#include "Logger.h"
#include "util.h"
#include <iostream>
frames_rendered(0)
{
SDL_Init(SDL_INIT_VIDEO);
- display=SDL_SetVideoMode(160,144,32,SDL_SWSURFACE);
+ display=SDL_SetVideoMode(320,288,32,SDL_HWSURFACE | SDL_DOUBLEBUF);
colors[0] = SDL_MapRGB(display->format, 0xFF, 0xFF, 0xFF);
colors[1] = SDL_MapRGB(display->format, 0xAA, 0xAA, 0xAA);
u32 *pixels = static_cast<u32*>(display->pixels);
u32 pixels_per_line = display->pitch/display->format->BytesPerPixel;
+ int LCDC = core->memory.read(GBIO::LCDC);
int STAT = core->memory.read(GBIO::STAT);
int LYC = core->memory.read(GBIO::LYC);
int t = core->cycle_count % 70224;
int hline_t=-1;
int LY = t/456;
- //std::cout << t << std::endl;
+
+ logger.trace("EI=", int(core->memory.read(0xFFFF)), " LY=", LY, " LYC=", LYC);
if (t >= 65664)
{
if (t == 65664)
{
+ logger.trace("Requesting IRQ_VBLANK");
+ core->irq(GameBoy::IRQ_VBLANK);
+ //STAT = set_bit(STAT,4);
+
if (check_bit(STAT,4))
- core->irq(GameBoy::IRQ_VBLANK);
- SDL_UpdateRect(display, 0, 0, 0, 0);
+ {
+ core->irq(GameBoy::IRQ_LCD_STAT);
+ }
+ SDL_Flip(display);
frames_rendered++;
char buf[50];
sprintf(buf, "%d", frames_rendered);
else
{
hline_t = t%456;
+ logger.trace("hline_t=", hline_t);
if (LY == LYC)
{
STAT = set_bit(STAT, 2); // set coincidence flag
if (hline_t == 0 && check_bit(STAT, 6))
+ {
+ logger.trace("Requesting IRQ_LCD_STAT");
core->irq(GameBoy::IRQ_LCD_STAT);
+ }
}
if (hline_t < 80)
// Draw at hline_t == 80, when the app cannot write to neither VRAM nor OAM
if (hline_t == 80)
{
- int LCDC = core->memory.read(GBIO::LCDC);
int BGP = core->memory.read(GBIO::BGP);
int pallette[4];
pallette[0] = BGP & 3;
if (check_bit(LCDC, 0)) // is BG display active?
{
u16 tile_map_addr = check_bit(LCDC,3) ? 0x1C00 : 0x1800;
- u16 tile_data_addr = check_bit(LCDC,4) ? 0x0800 : 0x0000;
+ u16 tile_data_addr = check_bit(LCDC,4) ? 0x0000 : 0x0800;
int tile_data_base = (tile_data_addr == 0x0800) ? -128 : 127;
// (vx , vy ) -> position of the pixel in the 256x256 bg
u8 color = ((current_row_high >> tile_x)&1) << 1 |
((current_row_low >> tile_x)&1);
- pixels[LY*pixels_per_line+x] = colors[pallette[color]];
+ pixels[2*(LY*pixels_per_line+x)] = colors[pallette[color]];
+ pixels[2*(LY*pixels_per_line+x)+1] = colors[pallette[color]];
+ pixels[2*(LY*pixels_per_line+x)+320] = colors[pallette[color]];
+ pixels[2*(LY*pixels_per_line+x)+321] = colors[pallette[color]];
}
}
else
{
for (int x=0; x<160; x++)
- pixels[LY*pixels_per_line+x] = colors[0];
+ {
+ pixels[2*(LY*pixels_per_line+x)] = colors[0];
+ pixels[2*(LY*pixels_per_line+x)+1] = colors[0];
+ pixels[2*(LY*pixels_per_line+x)+320] = colors[0];
+ pixels[2*(LY*pixels_per_line+x)+321] = colors[0];
+ }
}
}
break;
+// Special routine for JR
+#define dis_JR(opcode, name) \
+ case opcode: { \
+ s8 offset = memory.read(PC++); \
+ result << name << " " << std::dec << int(offset) << "\t[0x" \
+ << std::hex << std::setw(2) << int(PC+offset) << "]"; \
+ break; \
+ }
+
////////////////////////////////////////////////////////////
regs(),
IME(1),
HALT(0),
- cycle_count(0)
+ cycle_count(0),
+ breakpoints(),
+ last_breakpoint_id(0)
{
logger.info("GameBoy init");
rom = read_gbrom(rom_name);
memory.write(0xFFFF, 0x00); // IE
}
-#include "opcodes.h"
+int GameBoy::set_breakpoint(u16 addr)
+{
+ breakpoints[++last_breakpoint_id] = Breakpoint(addr, true);
+ return last_breakpoint_id;
+}
-GameBoy::run_status GameBoy::run_cycle()
+void GameBoy::delete_breakpoint(int id)
{
- video.update();
+ breakpoints.erase(id);
+}
- // Check for interrupts before opcode fetching
- u8 IE;
- if (IME && (IE=memory.read(0xFFFF)))
- {
- u8 IF = memory.read(0xFF0F);
- if (IF)
- {
- if ((IF & IRQ_VBLANK) && (IE & IRQ_VBLANK))
- {
- IME = 0;
- IF &= (~IRQ_VBLANK);
- do_call(0x40);
- }
- else if ((IF & IRQ_LCD_STAT) && (IE & IRQ_LCD_STAT))
- {
- IME = 0;
- IF &= (~IRQ_LCD_STAT);
- do_call(0x48);
- }
- else if ((IF & IRQ_TIMER) && (IE & IRQ_TIMER))
- {
- IME = 0;
- IF &= (~IRQ_TIMER);
- do_call(0x50);
- }
- else if ((IF & IRQ_SERIAL) && (IE & IRQ_SERIAL))
- {
- IME = 0;
- IF &= (~IRQ_SERIAL);
- do_call(0x58);
- }
- else if ((IF & IRQ_JOYPAD) && (IE & IRQ_JOYPAD))
- {
- IME = 0;
- IF &= (~IRQ_JOYPAD);
- do_call(0x60);
- }
- }
- }
+void GameBoy::enable_breakpoint(int id)
+{
+ breakpoints[id].enabled = true;
+}
+
+void GameBoy::disable_breakpoint(int id)
+{
+ breakpoints[id].enabled = false;
+}
+
+#include "opcodes.h"
+GameBoy::run_status GameBoy::run_cycle()
+{
int opcode;
opcode = memory.read(regs.PC++);
} // end switch
++cycle_count;
- //std::cout << "cycle_count " << cycle_count << std::endl;
+
+ video.update();
+ // Check for interrupts before opcode fetching
+ u8 IE=memory.read(0xFFFF);
+ logger.trace("IME=", int(IME), " IE=", int(IE));
+ if (IME && IE)
+ {
+ u8 IF = memory.read(0xFF0F);
+ logger.trace("Dispatching interrupts: IE=", int(IE), " IF=", int(IF));
+ if (IF)
+ {
+ if ((IF & IRQ_VBLANK) && (IE & IRQ_VBLANK))
+ {
+ IME = 0;
+ IF &= (~IRQ_VBLANK);
+ do_call(0x40);
+ logger.trace("VBLANK IRQ");
+ }
+ else if ((IF & IRQ_LCD_STAT) && (IE & IRQ_LCD_STAT))
+ {
+ IME = 0;
+ IF &= (~IRQ_LCD_STAT);
+ do_call(0x48);
+ logger.trace("LCD STAT IRQ");
+ }
+ else if ((IF & IRQ_TIMER) && (IE & IRQ_TIMER))
+ {
+ IME = 0;
+ IF &= (~IRQ_TIMER);
+ do_call(0x50);
+ logger.trace("TIMER IRQ");
+ }
+ else if ((IF & IRQ_SERIAL) && (IE & IRQ_SERIAL))
+ {
+ IME = 0;
+ IF &= (~IRQ_SERIAL);
+ do_call(0x58);
+ logger.trace("SERIAL IRQ");
+ }
+ else if ((IF & IRQ_JOYPAD) && (IE & IRQ_JOYPAD))
+ {
+ IME = 0;
+ IF &= (~IRQ_JOYPAD);
+ do_call(0x60);
+ logger.trace("JOYPAD IRQ");
+ }
+ }
+ memory.write(0xFF0F, IF);
+ }
+
+
+ for(BreakpointMap::iterator i=breakpoints.begin();
+ i != breakpoints.end();
+ i++)
+ {
+ if (i->second.addr == regs.PC && i->second.enabled)
+ return BREAKPOINT;
+ }
+
return NORMAL;
}
{
while (video.poll_event(&ev))
{
- if (ev.type == SDL_KEYDOWN)
+ switch(ev.type)
{
- if (ev.key.keysym.sym == SDLK_ESCAPE)
- {
- return PAUSED;
- }
+ case SDL_KEYDOWN:
+ switch(ev.key.keysym.sym)
+ {
+ case SDLK_ESCAPE:
+ return PAUSED;
+ case SDLK_q:
+ return QUIT;
+ default:
+ break;
+ }
+ break;
+ case SDL_QUIT:
+ return QUIT;
}
}
status = run_cycle();
dis_inm16(0xDA, "JP C")
dis(0xE9, "JP (HL)")
- dis_inm8(0x18, "JR")
- dis_inm8(0x20, "JR NZ")
- dis_inm8(0x28, "JR Z")
- dis_inm8(0x30, "JR NC")
- dis_inm8(0x38, "JR C")
+ dis_JR(0x18, "JR")
+ dis_JR(0x20, "JR NZ")
+ dis_JR(0x28, "JR Z")
+ dis_JR(0x30, "JR NC")
+ dis_JR(0x38, "JR C")
// Calls
dis_inm16(0xCD, "CALL")
#include "GBMemory.h"
#include "GBVideo.h"
#include <string>
+#include <map>
union GBRom;
public:
enum GameBoyType { GAMEBOY, GAMEBOYCOLOR, SUPERGAMEBOY } gameboy_type;
enum InterruptRequest {
- IRQ_VBLANK = 0x00,
- IRQ_LCD_STAT = 0x01,
- IRQ_TIMER = 0x02,
- IRQ_SERIAL = 0x04,
- IRQ_JOYPAD = 0x08
+ IRQ_VBLANK = 0x01,
+ IRQ_LCD_STAT = 0x02,
+ IRQ_TIMER = 0x04,
+ IRQ_SERIAL = 0x08,
+ IRQ_JOYPAD = 0x10,
};
enum Flag
enum run_status
{
NORMAL = 0,
- BREAKPOINT = 1,
- WATCHPOINT = 2,
- TRACEPOINT = 3,
- PAUSED = 4,
+ BREAKPOINT,
+ WATCHPOINT,
+ TRACEPOINT,
+ PAUSED,
+ QUIT,
};
// Constructors
// debug methods
void disassemble_opcode(u16 addr, std::string &instruction, int &length) const;
+
+ int set_breakpoint (u16 addr);
+ void delete_breakpoint (int id);
+ void enable_breakpoint (int id);
+ void disable_breakpoint(int id);
+
std::string status_string() const;
std::string get_port_name(int port) const;
private:
GameBoy(const GameBoy&);
GameBoy operator=(const GameBoy&);
+
+ // debug things
+ struct Breakpoint {
+ int addr;
+ bool enabled;
+
+ Breakpoint(int a, bool e): addr(a), enabled(e) {}
+ Breakpoint(): addr(-1), enabled(false) {}
+ };
+
+ typedef std::map<int, Breakpoint> BreakpointMap;
+
+ BreakpointMap breakpoints;
+ int last_breakpoint_id;
+
};
#endif
using std::string;
using std::vector;
+int str2int(string s)
+{
+ if (s[0] == '0') {
+ if (s[1] == 'x')
+ return strtol(s.c_str()+2, NULL, 16);
+ else if (s[1] >= '0' && s[1] <= '9')
+ return strtol(s.c_str()+1, NULL, 8);
+ else return 0;
+ }
+ return strtol(s.c_str(), NULL, 10);
+}
+
int main(int argc, char **argv)
{
if (argc != 2) {
}
}
- if (command == "step")
+ if (command == "step" || command == "s")
{
gb.run_cycle();
cout << gb.status_string() << endl;
else if (command == "run" || command == "r")
{
int status = gb.run();
- cout << "run returned with status " << status << endl;
+ if (status == GameBoy::QUIT)
+ {
+ break;
+ }
+ else if (status == GameBoy::BREAKPOINT)
+ {
+ cout << "Breakpoint hit at " << gb.regs.PC << endl;
+ cout << gb.status_string() << endl;
+ }
+ else
+ {
+ cout << "run returned with status " << status << endl;
+ }
}
else if (command == "quit" || command == "q")
{
break;
}
- else if (command == "disasm")
+ else if (command == "disasm" || command == "d")
{
int start, end, pos;
switch(arguments.size())
end = start + 30;
break;
case 1:
- start = atoi(arguments[0].c_str());
+ start = str2int(arguments[0]);
end = start + 30;
break;
case 2:
default:
- start = atoi(arguments[0].c_str());
- end = atoi(arguments[1].c_str());
+ start = str2int(arguments[0]);
+ end = str2int(arguments[1]);
break;
}
}
}
+ else if (command == "x")
+ {
+ int addr = str2int(arguments[0]);
+ cout << "[" << std::hex << std::setw(4) << std::setfill('0') <<
+ addr << "]\t";
+ if (arguments.size() > 1)
+ {
+ if (arguments[1] == "d")
+ cout << std::dec << int(gb.memory.read(addr)) << endl;
+ }
+ cout<< int(gb.memory.read(addr)) << endl;
+ }
+ else if (command == "break" || command == "b")
+ {
+ int addr = str2int(arguments[0]);
+ cout << "breakpoint #" << gb.set_breakpoint(addr) <<
+ " set at 0x" << std::hex << std::setw(4) << std::setfill('0') << addr << endl;
+ }
+ else if (command == "delete")
+ {
+ gb.delete_breakpoint(str2int(arguments[0]));
+ }
+ else if (command == "enable")
+ {
+ gb.enable_breakpoint(str2int(arguments[0]));
+ }
+ else if (command == "disable")
+ {
+ gb.disable_breakpoint(str2int(arguments[0]));
+ }
else
{
cout << "Unknown command '" << command << "'" << endl;