Interrupt hell. Breakpoints added
authorslack <slack@codemaniacs.com>
Tue, 1 Jul 2008 01:56:26 +0000 (03:56 +0200)
committerslack <slack@codemaniacs.com>
Tue, 1 Jul 2008 01:56:26 +0000 (03:56 +0200)
GBVideo.cc
disasm.h
gbcore.cc
gbcore.h
tests/test_core.cc

index 5e935665f24c2ef9031932d5f6c1acc8919aacd9..75120ec6d2c5a69225e7bc17fd11936242622880 100644 (file)
@@ -1,5 +1,6 @@
 #include "GBVideo.h"
 #include "gbcore.h"
+#include "Logger.h"
 #include "util.h"
 #include <iostream>
 
@@ -10,7 +11,7 @@ GBVideo::GBVideo(GameBoy *core):
        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);
@@ -80,21 +81,29 @@ void GBVideo::update()
        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);
@@ -107,11 +116,15 @@ void GBVideo::update()
        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)
@@ -144,7 +157,6 @@ void GBVideo::update()
        // 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;
@@ -155,7 +167,7 @@ void GBVideo::update()
                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
@@ -178,13 +190,21 @@ void GBVideo::update()
                                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];
+                       }
                }
 
        }
index 51b213266da00f1dd40b550f8d16cd3dc7aae798..3bf5b1caacfc354bc1fcb762c9bc65c9222163ad 100644 (file)
--- a/disasm.h
+++ b/disasm.h
                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; \
+       }
+
 
 ////////////////////////////////////////////////////////////
 
index 1dce04d59ef5c0911ed855f5f6be0e1a0e1a0e8a..07396f828de8bd8e9b8999d59bbf95889fe7cf67 100644 (file)
--- a/gbcore.cc
+++ b/gbcore.cc
@@ -16,7 +16,9 @@ GameBoy::GameBoy(std::string rom_name, GameBoyType type):
        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);
@@ -74,52 +76,31 @@ void GameBoy::reset()
        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++);
        
@@ -942,8 +923,66 @@ GameBoy::run_status GameBoy::run_cycle()
        } // 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;
 }
 
@@ -956,12 +995,21 @@ GameBoy::run_status GameBoy::run()
        {
                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();
@@ -1263,11 +1311,11 @@ void GameBoy::disassemble_opcode(u16 addr, std::string &instruction, int &length
                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")
index 48543aedf326180e0cc30187b082220028d2e682..42bef041295c1c6143feb8a217e7cb39f8b6dc25 100644 (file)
--- a/gbcore.h
+++ b/gbcore.h
@@ -5,6 +5,7 @@
 #include "GBMemory.h"
 #include "GBVideo.h"
 #include <string>
+#include <map>
 
 union GBRom;
 
@@ -13,11 +14,11 @@ class GameBoy
        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
@@ -85,10 +86,11 @@ class GameBoy
        enum run_status 
        {
                NORMAL = 0,
-               BREAKPOINT = 1,
-               WATCHPOINT = 2,
-               TRACEPOINT = 3,
-               PAUSED = 4,
+               BREAKPOINT,
+               WATCHPOINT,
+               TRACEPOINT,
+               PAUSED,
+               QUIT,
        };
 
        // Constructors
@@ -102,6 +104,12 @@ class GameBoy
 
        // 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;
 
@@ -109,6 +117,21 @@ class GameBoy
        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
index b6ef4c3d79062fb39719549bfc5bf5320b2f9874..2c38105e1734b2bbd2ddaf158a14a13e4b012835 100644 (file)
@@ -13,6 +13,18 @@ using std::endl;
 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) {
@@ -44,7 +56,7 @@ int main(int argc, char **argv)
                        }
                }
                
-               if (command == "step") 
+               if (command == "step" || command == "s"
                {
                        gb.run_cycle();
                        cout << gb.status_string() << endl;
@@ -52,13 +64,25 @@ int main(int argc, char **argv)
                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())
@@ -68,13 +92,13 @@ int main(int argc, char **argv)
                                        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;
                        }
                        
@@ -95,6 +119,36 @@ int main(int argc, char **argv)
                        }
 
                }
+               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;