From f1ef935431c33c4e2bbf01637dfe8d7500e1625e Mon Sep 17 00:00:00 2001 From: slack Date: Tue, 15 Jul 2008 02:47:48 +0200 Subject: [PATCH] Watchpoint support. Fixed bug in AND/OR/XOR zero flag setting. --- GBMemory.cc | 55 ++++++++++++++++++++++++++++++++++++++---- GBMemory.h | 52 ++++++++++++++++++++++++++++++++++++---- GBVideo.h | 12 +++++----- disasm.h | 18 +++++++------- gbcore.cc | 35 ++++++++++++++++----------- gbcore.h | 4 ++-- opcodes.h | 6 ++--- tests/test_core.cc | 60 ++++++++++++++++++++++++++++++++++++++-------- 8 files changed, 189 insertions(+), 53 deletions(-) diff --git a/GBMemory.cc b/GBMemory.cc index dc2cba3..bb31106 100644 --- a/GBMemory.cc +++ b/GBMemory.cc @@ -5,9 +5,50 @@ #include #include #include + + +int GBMemory::set_watchpoint(u16 addr) +{ + watchpoints[++last_watchpoint_id] = Watchpoint(addr, true); + return last_watchpoint_id; +} + +void GBMemory::delete_watchpoint(int id) +{ + watchpoints.erase(id); +} + +void GBMemory::enable_watchpoint(int id) +{ + watchpoints[id].enabled = true; +} + +void GBMemory::disable_watchpoint(int id) +{ + watchpoints[id].enabled = false; +} + +void GBMemory::check_watchpoints(u16 addr, u16 value) +{ + for (WatchpointMap::iterator i = watchpoints.begin(); + i != watchpoints.end(); i++) + { + if (i->second.enabled && addr == i->second.addr) + { + watchpoint_reached = true; + watchpoint_addr = addr; + watchpoint_oldvalue = read(addr, DONT_WATCH); + watchpoint_newvalue = value; + break; + } + } +} -void GBMemory::write(u16 addr, u8 value) +void GBMemory::write(u16 addr, u8 value, WatchpointControl watch) { + if (watch == WATCH) + check_watchpoints(addr, value); + if (addr < 0x8000) mbc->write(addr, value); else if (addr < 0xA000) core->video.write_VRAM(addr, value); else if (addr < 0xC000) mbc->write(addr, value); @@ -20,14 +61,12 @@ void GBMemory::write(u16 addr, u8 value) { high[I_DIV] = 0; } - /* else if (addr == DMA) { u16 dma_src = value << 8; logger.warning("OAM DMA transfer from 0x", std::hex, std::setfill('0'), dma_src, " requested"); core->video.DMA_OAM(dma_src); } - */ } else { std::ostringstream errmsg; @@ -39,8 +78,11 @@ void GBMemory::write(u16 addr, u8 value) } -u8 GBMemory::read(u16 addr) const +u8 GBMemory::read(u16 addr, WatchpointControl watch) { + if (watch == WATCH) + check_watchpoints(addr, 0xFFFF); + if (addr < 0x8000) return mbc->read(addr); else if (addr < 0xA000) return core->video.read_VRAM(addr); else if (addr < 0xC000) return mbc->read(addr); @@ -58,8 +100,11 @@ u8 GBMemory::read(u16 addr) const } } -u16 GBMemory::read16(u16 addr) const +u16 GBMemory::read16(u16 addr, WatchpointControl watch) { + if (watch == WATCH) + check_watchpoints(addr, 0xFFFF); + 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); diff --git a/GBMemory.h b/GBMemory.h index 6e27bd4..ba34be8 100644 --- a/GBMemory.h +++ b/GBMemory.h @@ -2,6 +2,7 @@ #define GBMEMORY_H #include "sized_types.h" +#include class GameBoy; class MBC; @@ -23,7 +24,37 @@ class GBMemory // FEA0-FEFF: Not usable u8 high[256]; // FF80-FFFE: High RAM + void check_watchpoints(u16 addr, u16 value); + + // prevent copying + GBMemory (const GBMemory &other); + GBMemory operator= (const GBMemory &other); + + // debug things + struct Watchpoint { + int addr; + bool enabled; + + Watchpoint(int a, bool e): addr(a), enabled(e) {} + Watchpoint(): addr(-1), enabled(false) {} + }; + + typedef std::map WatchpointMap; + + WatchpointMap watchpoints; + int last_watchpoint_id; + + public: + int set_watchpoint (u16 addr); + void delete_watchpoint (int id); + void enable_watchpoint (int id); + void disable_watchpoint(int id); + + bool watchpoint_reached; + u16 watchpoint_addr; + u8 watchpoint_oldvalue; + u16 watchpoint_newvalue; static const u16 VRAM_BASE = 0x8000; static const u16 EXTERNAL_RAM_BASE = 0xA000; @@ -32,13 +63,26 @@ class GBMemory static const u16 IO_BASE = 0xFF00; static const u16 HRAM_BASE = 0xFF80; - GBMemory(GameBoy *core): core(core), mbc(0), high() {} + GBMemory(GameBoy *core): core(core), mbc(0), high(), + watchpoints(), + last_watchpoint_id(0), + watchpoint_reached(false), + watchpoint_addr(0), + watchpoint_oldvalue(0), + watchpoint_newvalue(0) + {} void init(MBC *mbc) { this->mbc = mbc; } - u8 read (u16 addr) const; - u16 read16(u16 addr) const; - void write (u16 addr, u8 value); + enum WatchpointControl + { + WATCH=0, + DONT_WATCH=1, + }; + + u8 read (u16 addr, WatchpointControl watch = WATCH); + u16 read16(u16 addr, WatchpointControl watch = WATCH); + void write (u16 addr, u8 value, WatchpointControl watch = WATCH); public: static const u16 DIV = 0xFF04; // Divider register (R/W) diff --git a/GBVideo.h b/GBVideo.h index 02117c4..b1aa618 100644 --- a/GBVideo.h +++ b/GBVideo.h @@ -71,12 +71,12 @@ class GBVideo void reset(); // VRAM/OAM access - inline u8 read_VRAM (int addr) const; - inline u8 read_OAM (int addr) const; - inline u16 read16_VRAM (int addr) const; - inline u16 read16_OAM (int addr) const; - inline void write_VRAM(int addr, u8 value); - inline void write_OAM (int addr, u8 value); + u8 read_VRAM (int addr) const; + u8 read_OAM (int addr) const; + u16 read16_VRAM (int addr) const; + u16 read16_OAM (int addr) const; + void write_VRAM(int addr, u8 value); + void write_OAM (int addr, u8 value); // Write the whole OAM area via DMA void DMA_OAM (const u16 src); diff --git a/disasm.h b/disasm.h index 3bf5b1c..d20de97 100644 --- a/disasm.h +++ b/disasm.h @@ -41,28 +41,28 @@ #define dis_inm8(opcode, name) \ case opcode: \ result << name << " 0x" << std::setw(2) << \ - int(memory.read(PC++)); \ + int(memory.read(PC++, GBMemory::DONT_WATCH)); \ break; // OP inm16 #define dis_inm16(opcode, name) \ case opcode: \ result << name << " 0x" << std::setw(4) << \ - int(memory.read(PC)+(memory.read(PC+1) << 8)); \ + int(memory.read16(PC, GBMemory::DONT_WATCH)); \ PC += 2; \ break; #define dis_reg_inm(opcode, name, reg) \ case opcode: \ result << name << " " << #reg << ", 0x" << std::setw(2) << \ - int(memory.read(PC++)); \ + int(memory.read(PC++, GBMemory::DONT_WATCH)); \ break; #define dis_reg16_inm(opcode, name, reg16) \ case opcode: \ result << name << " " << #reg16 << ", 0x" << \ std::setw(4) << \ - int(memory.read(PC)+(memory.read(PC+1) << 8)); \ + int(memory.read16(PC, GBMemory::DONT_WATCH)); \ PC += 2; \ break; @@ -70,7 +70,7 @@ case opcode: \ result << name << " " << #reg16 << ", 0x" << \ std::setw(2) << \ - int(memory.read(PC++)); \ + int(memory.read(PC++, GBMemory::DONT_WATCH)); \ break; #define dis_reg_reg(opcode, name, reg1, reg2) \ @@ -107,7 +107,7 @@ case opcode: \ result << name << " " << #reg << ", (0x" << \ std::setw(4) << \ - int(memory.read(PC) + (memory.read(PC+1)<<8)) << \ + int(memory.read16(PC, GBMemory::DONT_WATCH)) << \ ")"; \ PC += 2; \ break; @@ -126,7 +126,7 @@ case opcode: \ result << name << " (0x" << \ std::setw(4) << \ - int(memory.read(PC) + (memory.read(PC+1)<<8)) << \ + int(memory.read16(PC, GBMemory::DONT_WATCH)) << \ "), " << #reg; \ PC += 2; \ break; @@ -136,7 +136,7 @@ case opcode: \ result << name << " (0x" << \ std::setw(4) << \ - int(memory.read(PC) + (memory.read(PC+1)<<8)) << \ + int(memory.read16(PC, GBMemory::DONT_WATCH)) << \ "), " << #reg16; \ PC += 2; \ break; @@ -146,7 +146,7 @@ case opcode: \ result << name << " (" << #reg16 << "), 0x" << \ std::setw(2) << \ - int(memory.read(PC++)); \ + int(memory.read(PC++, GBMemory::DONT_WATCH)); \ break; diff --git a/gbcore.cc b/gbcore.cc index 9f7a217..017507d 100644 --- a/gbcore.cc +++ b/gbcore.cc @@ -710,7 +710,7 @@ GameBoy::run_status GameBoy::run_cycle() case 0xA6: //AND (HL) regs.A &= memory.read(regs.HL); - if (regs.A == 0) set_flag(ZERO_FLAG); + set_flag_if(regs.A == 0, ZERO_FLAG); reset_flag(ADD_SUB_FLAG); set_flag(HALF_CARRY_FLAG); reset_flag(CARRY_FLAG); @@ -719,7 +719,7 @@ GameBoy::run_status GameBoy::run_cycle() case 0xE6: //AND inm regs.A &= memory.read(regs.PC++); - if (regs.A == 0) set_flag(ZERO_FLAG); + set_flag_if(regs.A == 0, ZERO_FLAG); reset_flag(ADD_SUB_FLAG); set_flag(HALF_CARRY_FLAG); reset_flag(CARRY_FLAG); @@ -731,7 +731,7 @@ GameBoy::run_status GameBoy::run_cycle() case 0xB6: //OR (HL) regs.A |= memory.read(regs.HL); - if (regs.A == 0) set_flag(ZERO_FLAG); + set_flag_if(regs.A == 0, ZERO_FLAG); reset_flag(ADD_SUB_FLAG); reset_flag(HALF_CARRY_FLAG); reset_flag(CARRY_FLAG); @@ -740,7 +740,7 @@ GameBoy::run_status GameBoy::run_cycle() case 0xF6: //OR inm regs.A |= memory.read(regs.PC++); - if (regs.A == 0) set_flag(ZERO_FLAG); + set_flag_if(regs.A == 0, ZERO_FLAG); reset_flag(ADD_SUB_FLAG); reset_flag(HALF_CARRY_FLAG); reset_flag(CARRY_FLAG); @@ -752,7 +752,7 @@ GameBoy::run_status GameBoy::run_cycle() case 0xAE: //XOR (HL) regs.A ^= memory.read(regs.HL); - if (regs.A == 0) set_flag(ZERO_FLAG); + set_flag_if(regs.A == 0, ZERO_FLAG); reset_flag(ADD_SUB_FLAG); reset_flag(HALF_CARRY_FLAG); reset_flag(CARRY_FLAG); @@ -761,7 +761,7 @@ GameBoy::run_status GameBoy::run_cycle() case 0xEE: //XOR inm regs.A ^= memory.read(regs.PC++); - if (regs.A == 0) set_flag(ZERO_FLAG); + set_flag_if(regs.A == 0, ZERO_FLAG); reset_flag(ADD_SUB_FLAG); reset_flag(HALF_CARRY_FLAG); reset_flag(CARRY_FLAG); @@ -1359,6 +1359,12 @@ GameBoy::run_status GameBoy::run_cycle() if (cycles_until_next_instruction > 0) return WAIT; else { + if (memory.watchpoint_reached) + { + memory.watchpoint_reached = false; + return WATCHPOINT; + } + for(BreakpointMap::iterator i=breakpoints.begin(); i != breakpoints.end(); i++) @@ -1463,7 +1469,7 @@ std::string GameBoy::get_port_name(int port) const return port_name; } -std::string GameBoy::status_string() const +std::string GameBoy::status_string() { std::string disassembled_instruction; int length; @@ -1486,16 +1492,17 @@ std::string GameBoy::status_string() const " DE = " << std::hex << std::setw(4) << std::setfill('0') << int(regs.DE) << " HL = " << std::hex << std::setw(4) << std::setfill('0') << int(regs.HL) << "\tSP = " << std::hex << std::setw(4) << std::setfill('0') << int(regs.SP) << std::endl << - "IME = " << int(IME) << " IE = " << int(memory.read(0xFFFF)) << " IF = " << int(memory.read(0xFF0F)); + "IME = " << int(IME) << " IE = " << int(memory.read(0xFFFF, GBMemory::DONT_WATCH)) << + " IF = " << int(memory.read(0xFF0F, GBMemory::DONT_WATCH)); return result.str(); } #include "disasm.h" -void GameBoy::disassemble_opcode(u16 addr, std::string &instruction, int &length) const +void GameBoy::disassemble_opcode(u16 addr, std::string &instruction, int &length) { int opcode; u16 PC = addr; - opcode = memory.read(PC++); + opcode = memory.read(PC++, GBMemory::DONT_WATCH); std::ostringstream result; result << std::hex << std::uppercase << std::setfill('0'); @@ -1546,7 +1553,7 @@ void GameBoy::disassemble_opcode(u16 addr, std::string &instruction, int &length // LDH (n), A case 0xE0: { - int port = int(memory.read(PC++)); + int port = int(memory.read(PC++, GBMemory::DONT_WATCH)); result << "LD (0xFF" << std::setw(2) << port << "), A" << "\t[" << get_port_name(port) << "]"; @@ -1554,7 +1561,7 @@ void GameBoy::disassemble_opcode(u16 addr, std::string &instruction, int &length } // LDH A, (n) case 0xF0: { - int port = int(memory.read(PC++)); + int port = int(memory.read(PC++, GBMemory::DONT_WATCH)); result << "LD A, (0xFF" << std::setw(2) << port << ")" << "\t[" << get_port_name(port) << "]"; break; @@ -1571,7 +1578,7 @@ void GameBoy::disassemble_opcode(u16 addr, std::string &instruction, int &length // LD HL, SP+n // LDHL SP, n case 0xF8: - result << "LD HL, SP + 0x"<< std::setw(2) << int(memory.read(PC++)); + result << "LD HL, SP + 0x"<< std::setw(2) << int(memory.read(PC++, GBMemory::DONT_WATCH)); break; // LD (nn), SP @@ -1662,7 +1669,7 @@ void GameBoy::disassemble_opcode(u16 addr, std::string &instruction, int &length // Miscellaneous instructions case 0xCB: { - int sub_opcode = memory.read(PC++); + int sub_opcode = memory.read(PC++, GBMemory::DONT_WATCH); switch(sub_opcode) { // SWAP n diff --git a/gbcore.h b/gbcore.h index 699e68d..5201b66 100644 --- a/gbcore.h +++ b/gbcore.h @@ -109,14 +109,14 @@ class GameBoy run_status run(); // debug methods - void disassemble_opcode(u16 addr, std::string &instruction, int &length) const; + void disassemble_opcode(u16 addr, std::string &instruction, int &length); 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 status_string(); std::string get_port_name(int port) const; // prevent object copying diff --git a/opcodes.h b/opcodes.h index c1d82d2..bb1ffb4 100644 --- a/opcodes.h +++ b/opcodes.h @@ -130,7 +130,7 @@ #define AND_reg(opcode, reg) \ case opcode: { \ regs.A &= regs.reg; \ - if (regs.A == 0) set_flag(ZERO_FLAG); \ + set_flag_if(regs.A == 0, ZERO_FLAG); \ reset_flag(ADD_SUB_FLAG); \ set_flag(HALF_CARRY_FLAG); \ reset_flag(CARRY_FLAG); \ @@ -141,7 +141,7 @@ #define OR_reg(opcode, reg) \ case opcode: { \ regs.A |= regs.reg; \ - if (regs.A == 0) set_flag(ZERO_FLAG); \ + set_flag_if(regs.A == 0, ZERO_FLAG); \ reset_flag(ADD_SUB_FLAG); \ reset_flag(HALF_CARRY_FLAG); \ reset_flag(CARRY_FLAG); \ @@ -152,7 +152,7 @@ #define XOR_reg(opcode, reg) \ case opcode: { \ regs.A ^= regs.reg; \ - if (regs.A == 0) set_flag(ZERO_FLAG); \ + set_flag_if(regs.A == 0, ZERO_FLAG); \ reset_flag(ADD_SUB_FLAG); \ reset_flag(HALF_CARRY_FLAG); \ reset_flag(CARRY_FLAG); \ diff --git a/tests/test_core.cc b/tests/test_core.cc index 808f527..9ba7593 100644 --- a/tests/test_core.cc +++ b/tests/test_core.cc @@ -26,6 +26,34 @@ int str2int(string s) return strtol(s.c_str(), NULL, 10); } +void print_run_result(GameBoy &gb, int status) +{ + if (status == GameBoy::BREAKPOINT) + { + cout << "Breakpoint hit at " << gb.regs.PC << endl; + cout << gb.status_string() << endl; + } + else if (status == GameBoy::WATCHPOINT) + { + cout << "Watchpoint 0x" << std::hex << std::setw(4) << std::setfill('0') << + int(gb.memory.watchpoint_addr) << " hit at 0x" << gb.regs.PC; + if (gb.memory.watchpoint_newvalue == 0xFFFF) + { + cout << " (READ)" << endl << + "value = " << int(gb.memory.watchpoint_oldvalue) << endl; + } + else + { + cout << " (WRITE)" << endl << + "old = " << int(gb.memory.watchpoint_oldvalue) << + " new = " << int(gb.memory.watchpoint_newvalue) << endl; + } + } + //else + //{ + // cout << "run returned with status " << status << endl; + //} +} int main(int argc, char **argv) { @@ -75,8 +103,10 @@ int main(int argc, char **argv) if (command == "step" || command == "s") { - while(gb.run_cycle() == GameBoy::WAIT) {} // do nothing + int status; + while((status = gb.run_cycle()) == GameBoy::WAIT) {} // do nothing + print_run_result(gb, status); cout << gb.status_string() << endl; } else if (command == "run" || command == "r" || command == "cont") @@ -92,15 +122,7 @@ int main(int argc, char **argv) { 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; - } + print_run_result(gb, status); } else if (arguments.size() == 1) { @@ -190,6 +212,24 @@ int main(int argc, char **argv) { gb.disable_breakpoint(str2int(arguments[0])); } + else if (command == "watch" || command == "b") + { + int addr = str2int(arguments[0]); + cout << "watchpoint #" << gb.memory.set_watchpoint(addr) << + " set at 0x" << std::hex << std::setw(4) << std::setfill('0') << addr << endl; + } + else if (command == "wdelete") + { + gb.memory.delete_watchpoint(str2int(arguments[0])); + } + else if (command == "wenable") + { + gb.memory.enable_watchpoint(str2int(arguments[0])); + } + else if (command == "wdisable") + { + gb.memory.disable_watchpoint(str2int(arguments[0])); + } else if (command == "display") { if (arguments[0] == "bgmap") -- 2.34.1