Skip to content

Commit

Permalink
Use breakpoint map instead of breakpoint list
Browse files Browse the repository at this point in the history
  • Loading branch information
thatguy11325 authored and Baekalfen committed Apr 10, 2024
1 parent 3077bed commit 7cdcd5a
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 62 deletions.
8 changes: 4 additions & 4 deletions pyboy/core/mb.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,13 @@ cdef class Motherboard:
cdef bint double_speed
cdef readonly bint cgb, cartridge_cgb

cdef list breakpoints_list
cdef dict breakpoints
cdef bint breakpoint_singlestep
cdef bint breakpoint_singlestep_latch
cdef int64_t breakpoint_waiting
cdef int breakpoint_add(self, int64_t, int64_t) except -1 with gil
cdef void breakpoint_remove(self, int64_t) noexcept with gil
cdef inline int breakpoint_reached(self) noexcept with gil
cdef int64_t breakpoint_add(self, int64_t, int64_t) except -1 with gil
cdef void breakpoint_remove(self, int64_t, int64_t) noexcept with gil
cdef inline tuple[int64_t, int64_t, int64_t] breakpoint_reached(self) noexcept with gil
cdef inline void breakpoint_reinject(self) noexcept nogil

cdef inline bint processing_frame(self) noexcept nogil
Expand Down
102 changes: 52 additions & 50 deletions pyboy/core/mb.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def __init__(
self.serialbuffer = [0] * 1024
self.serialbuffer_count = 0

self.breakpoints_list = [] #[(0, 0x150), (0, 0x0040), (0, 0x0048), (0, 0x0050)]
self.breakpoints = {} #{(0, 0x150): (0x100) (0, 0x0040): 0x200, (0, 0x0048): 0x300, (0, 0x0050): 0x44}
self.breakpoint_singlestep = False
self.breakpoint_singlestep_latch = False
self.breakpoint_waiting = -1
Expand Down Expand Up @@ -122,62 +122,64 @@ def breakpoint_add(self, bank, addr):
else:
raise Exception("Unsupported breakpoint address. If this a mistake, reach out to the developers")

self.breakpoints_list.append((bank, addr, opcode))
return len(self.breakpoints_list) - 1
self.breakpoints[(bank, addr)] = opcode

def breakpoint_find(self, bank, addr):
for i, (_bank, _addr, _) in enumerate(self.breakpoints_list):
if _bank == bank and _addr == addr:
return i
return -1

def breakpoint_remove(self, index):
logger.debug(f"Breakpoint remove: {index}")
if not 0 <= index < len(self.breakpoints_list):
logger.error("Cannot remove breakpoint: Index out of bounds %d/%d", index, len(self.breakpoints_list))
# return (None, None, None)
bank, addr, opcode = self.breakpoints_list.pop(index)
logger.debug(f"Breakpoint remove: {bank:02x}:{addr:04x} {opcode:02x}")

# Restore opcode
if addr < 0x100 and bank == -1:
self.bootrom.bootrom[addr] = opcode
elif addr < 0x4000:
self.cartridge.rombanks[bank, addr] = opcode
elif 0x4000 <= addr < 0x8000:
self.cartridge.rombanks[bank, addr - 0x4000] = opcode
elif 0x8000 <= addr < 0xA000:
if bank == 0:
self.lcd.VRAM0[addr - 0x8000] = opcode
opcode = self.breakpoints.get((bank, addr))
if opcode is not None:
return (bank, addr, opcode)
return tuple()

def breakpoint_remove(self, bank, addr):
logger.debug(f"Breakpoint remove: ({bank}, {addr})")
opcode = self.breakpoints.pop((bank, addr), None)
if opcode is not None:
logger.debug(f"Breakpoint remove: {bank:02x}:{addr:04x} {opcode:02x}")

# Restore opcode
if addr < 0x100 and bank == -1:
self.bootrom.bootrom[addr] = opcode
elif addr < 0x4000:
self.cartridge.rombanks[bank, addr] = opcode
elif 0x4000 <= addr < 0x8000:
self.cartridge.rombanks[bank, addr - 0x4000] = opcode
elif 0x8000 <= addr < 0xA000:
if bank == 0:
self.lcd.VRAM0[addr - 0x8000] = opcode
else:
self.lcd.VRAM1[addr - 0x8000] = opcode
elif 0xA000 <= addr < 0xC000:
self.cartridge.rambanks[bank, addr - 0xA000] = opcode
elif 0xC000 <= addr <= 0xE000:
self.ram.internal_ram0[addr - 0xC000] = opcode
else:
self.lcd.VRAM1[addr - 0x8000] = opcode
elif 0xA000 <= addr < 0xC000:
self.cartridge.rambanks[bank, addr - 0xA000] = opcode
elif 0xC000 <= addr <= 0xE000:
self.ram.internal_ram0[addr - 0xC000] = opcode
logger.error("Unsupported breakpoint address. If this a mistake, reach out to the developers")
else:
logger.error("Unsupported breakpoint address. If this a mistake, reach out to the developers")
# return (None, None, None)
# return (bank, addr, opcode)
logger.error("Breakpoint not found. If this a mistake, reach out to the developers")

def breakpoint_reached(self):
for i, (bank, pc, _) in enumerate(self.breakpoints_list):
if self.cpu.PC == pc and (
(pc < 0x4000 and bank == 0 and not self.bootrom_enabled) or \
(0x4000 <= pc < 0x8000 and self.cartridge.rombank_selected == bank) or \
(0xA000 <= pc < 0xC000 and self.cartridge.rambank_selected == bank) or \
(0xC000 <= pc <= 0xFFFF and bank == -1) or \
(pc < 0x100 and bank == -1 and self.bootrom_enabled)
):
# Breakpoint hit
# bank, addr, opcode = self.breakpoint_remove(i)
bank, addr, opcode = self.breakpoints_list[i]
logger.debug(f"Breakpoint reached: {bank:02x}:{addr:04x} {opcode:02x}")
self.breakpoint_waiting = (bank & 0xFF) << 24 | (addr & 0xFFFF) << 8 | (opcode & 0xFF)
logger.debug(f"Breakpoint waiting: {self.breakpoint_waiting:08x}")
return i
pc = self.cpu.PC
bank = None
if pc < 0x100 and self.bootrom_enabled:
bank = -1
elif pc < 0x4000:
bank = 0
elif 0x4000 <= pc < 0x8000:
bank = self.cartridge.rombank_selected
elif 0xA000 <= pc < 0xC000:
bank = self.cartridge.rambank_selected
elif 0xC000 <= pc <= 0xFFFF:
bank = 0
opcode = self.breakpoints.get((bank, pc))
if opcode is not None:
# Breakpoint hit
addr = pc
logger.debug("Breakpoint reached: %02x:%04x %02x", bank, addr, opcode)
self.breakpoint_waiting = (bank & 0xFF) << 24 | (addr & 0xFFFF) << 8 | (opcode & 0xFF)
logger.debug("Breakpoint waiting: %08x", self.breakpoint_waiting)
return (bank, addr, opcode)
logger.debug("Invalid breakpoint reached: %04x", self.cpu.PC)
return -1
return (-1, -1, -1)

def breakpoint_reinject(self):
if self.breakpoint_waiting < 0:
Expand Down
2 changes: 1 addition & 1 deletion pyboy/plugins/debug_prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def handle_breakpoint(self):
for addr, label in addresses.items():
print(f"{bank:02X}:{addr:04X} {label}")
elif cmd == "bl":
for bank, addr in self.mb.breakpoints_list:
for bank, addr in self.mb.breakpoints.keys():
print(f"{bank:02X}:{addr:04X} {self.rom_symbols.get(bank, {}).get(addr, '')}")
elif cmd == "b" or cmd.startswith("b "):
if cmd.startswith("b "):
Expand Down
16 changes: 9 additions & 7 deletions pyboy/pyboy.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,9 +348,10 @@ def _tick(self, render):
self.mb.breakpoint_reinject()

# NOTE: PC has not been incremented when hitting breakpoint!
breakpoint_index = self.mb.breakpoint_reached()
if breakpoint_index != -1:
self.mb.breakpoint_remove(breakpoint_index)
breakpoint_meta = self.mb.breakpoint_reached()
if breakpoint_meta != (-1, -1, -1):
bank, addr, _ = breakpoint_meta
self.mb.breakpoint_remove(bank, addr)
self.mb.breakpoint_singlestep_latch = 0

if not self._handle_hooks():
Expand Down Expand Up @@ -1093,6 +1094,7 @@ def hook_register(self, bank, addr, callback, context):
raise ValueError("Hook already registered for this bank and address.")
self.mb.breakpoint_add(bank, addr)
bank_addr_opcode = (bank & 0xFF) << 24 | (addr & 0xFFFF) << 8 | (opcode & 0xFF)
logger.debug("Adding hook for opcode %08x", bank_addr_opcode)
self._hooks[bank_addr_opcode] = (callback, context)

def hook_deregister(self, bank, addr):
Expand Down Expand Up @@ -1125,12 +1127,12 @@ def hook_deregister(self, bank, addr):
if bank is None and isinstance(addr, str):
bank, addr = self._lookup_symbol(addr)

index = self.mb.breakpoint_find(bank, addr)
if index == -1:
breakpoint_meta = self.mb.breakpoint_find(bank, addr)
if not breakpoint_meta:
raise ValueError("Breakpoint not found for bank and addr")
_, _, opcode = breakpoint_meta

_, _, opcode = self.mb.breakpoints_list[index]
self.mb.breakpoint_remove(index)
self.mb.breakpoint_remove(bank, addr)
bank_addr_opcode = (bank & 0xFF) << 24 | (addr & 0xFFFF) << 8 | (opcode & 0xFF)
self._hooks.pop(bank_addr_opcode)

Expand Down

0 comments on commit 7cdcd5a

Please sign in to comment.