mirror of
https://github.com/mnaberez/py65.git
synced 2025-01-01 11:29:32 +00:00
Merge pull request #26 from agatti/breakpoint
Basic breakpoint support.
This commit is contained in:
commit
8ec13250a6
@ -103,9 +103,70 @@ Offsets are interpreted like any other numbers. In the example above,
|
||||
``start+4`` implies that the offset (``4``) uses the default radix. This
|
||||
could also be written as ``start+$04`` for explicit hexadecimal.
|
||||
|
||||
Breakpoints
|
||||
-----------
|
||||
|
||||
It is possible to set breakpoints to stop execution when reaching a
|
||||
given address or label. Breakpoints are added using the
|
||||
``add_breakpoint`` command::
|
||||
|
||||
.disassemble start:start+4
|
||||
$ff80 d8 CLD
|
||||
$ff81 a2 ff LDX #$ff
|
||||
$ff83 9a TXS
|
||||
$ff84 a0 1c LDY #$1c
|
||||
.add_breakpoint $ff84
|
||||
Breakpoint 0 added at $FF84
|
||||
.goto $ff80
|
||||
Breakpoint 0 reached.
|
||||
PC AC XR YR SP NV-BDIZC
|
||||
6502: ff84 00 ff 00 ff 10110000
|
||||
|
||||
Note that a number is assigned to each breakpoint, similar to how
|
||||
VICE operates. Deleting a breakpoint can be done via the
|
||||
``delete_breakpoint`` command using the breakpoint identifier given
|
||||
by ``add_breakpoint``::
|
||||
|
||||
.add_breakpoint $ff84
|
||||
Breakpoint 0 added at $FF84
|
||||
.delete_breakpoint 0
|
||||
Breakpoint 0 removed
|
||||
|
||||
Breakpoint can be listed using the ``list_breakpoint`` command::
|
||||
|
||||
.add_breakpoint $1234
|
||||
Breakpoint 0 added at $1234
|
||||
.add_breakpoint $5678
|
||||
Breakpoint 1 added at $5678
|
||||
.add_breakpoint $9ABC
|
||||
Breakpoint 2 added at $9ABC
|
||||
.list_breakpoints
|
||||
Breakpoint 0 : $1234
|
||||
Breakpoint 1 : $5678
|
||||
Breakpoint 2 : $9ABC
|
||||
|
||||
Keep in mind that breakpoint identifiers are not recycled throughout
|
||||
a session, this means that if you add three breakpoints (#0, #1, #2)
|
||||
and then delete breakpoint #1, the next breakpoint you add will be
|
||||
breakpoint #3, not #1. Also, invoking ``reset`` clears breakpoints
|
||||
too, not just labels.
|
||||
|
||||
Command Reference
|
||||
=================
|
||||
|
||||
.. describe:: add_breakpoint <address|label>
|
||||
|
||||
Sets a breakpoint on execution at the given address or at the
|
||||
address represented by the given label::
|
||||
|
||||
.add_breakpoint $1234
|
||||
.add_label f000 start
|
||||
.add_breakpoint start
|
||||
|
||||
Breakpoints get a numeric identifier to be used with
|
||||
``delete_breakpoint``, the list of identifiers can be retrieved
|
||||
with ``list_breakpoints``.
|
||||
|
||||
.. describe:: add_label <address> <label>
|
||||
|
||||
Assign a label to an address::
|
||||
@ -155,6 +216,21 @@ Command Reference
|
||||
.cycles
|
||||
12
|
||||
|
||||
.. describe:: delete_breakpoint <breakpoint_id>
|
||||
|
||||
Removes the breakpoint associated with the given identifier::
|
||||
|
||||
.add_breakpoint $1234
|
||||
Breakpoint 0 added at $1234
|
||||
.add_label f000 start
|
||||
.add_breakpoint start
|
||||
Breakpoint 1 added at $F000
|
||||
.delete_breakpoint 0
|
||||
Breakpoint 0 removed
|
||||
|
||||
The list of identifiers added with ``add_breakpoint`` can be
|
||||
retrieved with ``list_breakpoints``.
|
||||
|
||||
.. describe:: delete_label <label>
|
||||
|
||||
Delete a label that was previously defined with ``add_label``::
|
||||
@ -213,6 +289,21 @@ Command Reference
|
||||
disassemble <address_range>
|
||||
Disassemble instructions in the address range.
|
||||
|
||||
.. describe:: list_breakpoints
|
||||
|
||||
Lists all the breakpoints that have been set so far::
|
||||
|
||||
.add_breakpoint $1234
|
||||
Breakpoint 0 added at $1234
|
||||
.add_breakpoint $5678
|
||||
Breakpoint 1 added at $5678
|
||||
.add_breakpoint $9ABC
|
||||
Breakpoint 2 added at $9ABC
|
||||
.list_breakpoints
|
||||
Breakpoint 0 : $1234
|
||||
Breakpoint 1 : $5678
|
||||
Breakpoint 2 : $9ABC
|
||||
|
||||
.. describe:: load <filename> <address>
|
||||
|
||||
Load a binary file into memory starting at the address specified::
|
||||
|
@ -135,8 +135,10 @@ class Monitor(cmd.Cmd):
|
||||
self._shortcuts = {'EOF': 'quit',
|
||||
'~': 'tilde',
|
||||
'a': 'assemble',
|
||||
'ab': 'add_breakpoint',
|
||||
'al': 'add_label',
|
||||
'd': 'disassemble',
|
||||
'db': 'delete_breakpoint',
|
||||
'dl': 'delete_label',
|
||||
'exit': 'quit',
|
||||
'f': 'fill',
|
||||
@ -145,6 +147,7 @@ class Monitor(cmd.Cmd):
|
||||
'h': 'help',
|
||||
'?': 'help',
|
||||
'l': 'load',
|
||||
'lb': 'list_breakpoints',
|
||||
'm': 'mem',
|
||||
'q': 'quit',
|
||||
'r': 'registers',
|
||||
@ -434,9 +437,20 @@ class Monitor(cmd.Cmd):
|
||||
|
||||
def _run(self, stopcodes=[]):
|
||||
last_instruct = None
|
||||
while last_instruct not in stopcodes:
|
||||
self._mpu.step()
|
||||
last_instruct = self._mpu.memory[self._mpu.pc]
|
||||
if not self._address_parser.breakpoints:
|
||||
while last_instruct not in stopcodes:
|
||||
self._mpu.step()
|
||||
last_instruct = self._mpu.memory[self._mpu.pc]
|
||||
else:
|
||||
# Slows things down quite a bit, unfortunately.
|
||||
breakpoint = False
|
||||
while last_instruct not in stopcodes and not breakpoint:
|
||||
self._mpu.step()
|
||||
last_instruct = self._mpu.memory[self._mpu.pc]
|
||||
if self._mpu.pc in self._address_parser.breakpoints:
|
||||
self._output("Breakpoint %d reached." % self._address_parser.breakpoints.index(self._mpu.pc))
|
||||
breakpoint = True
|
||||
|
||||
|
||||
def help_radix(self):
|
||||
self._output("radix [H|D|O|B]")
|
||||
@ -739,6 +753,63 @@ class Monitor(cmd.Cmd):
|
||||
self._output("Set the width used by some commands to wrap output.")
|
||||
self._output("With no argument, the current width is printed.")
|
||||
|
||||
def do_add_breakpoint(self, args):
|
||||
split = shlex.split(args)
|
||||
if len(split) != 1:
|
||||
self._output("Syntax error: %s" % args)
|
||||
return self.help_add_breakpoint()
|
||||
|
||||
address = self._address_parser.address_for(split[0])
|
||||
if not address:
|
||||
address = self._address_parser.number(split[0])
|
||||
|
||||
if address not in self._address_parser.breakpoints:
|
||||
self._output("Breakpoint %d added at $%04X" % (len(self._address_parser.breakpoints), address))
|
||||
self._address_parser.breakpoints.append(address)
|
||||
else:
|
||||
self._output("Breakpoint already present at $%04X" % address)
|
||||
|
||||
def help_add_breakpoint(self):
|
||||
self._output("add_breakpoint <address|label>")
|
||||
self._output("Add a breakpoint on execution at the given address or label")
|
||||
|
||||
def do_delete_breakpoint(self, args):
|
||||
split = shlex.split(args)
|
||||
if len(split) != 1:
|
||||
self._output("Syntax error: %s" % args)
|
||||
return self.help_delete_breakpoint()
|
||||
|
||||
number = None
|
||||
try:
|
||||
number = int(split[0])
|
||||
if number < 0 or number > len(self._address_parser.breakpoints):
|
||||
self._output("Invalid breakpoint number %d", number)
|
||||
return
|
||||
except ValueError:
|
||||
self._output("Illegal number: %s" % args)
|
||||
return
|
||||
|
||||
if self._address_parser.breakpoints[number] is not None:
|
||||
self._address_parser.breakpoints[number] = None
|
||||
self._output("Breakpoint %d removed" % number)
|
||||
else:
|
||||
self._output("Breakpoint %d already removed" % number)
|
||||
|
||||
def help_delete_breakpoint(self):
|
||||
self._output("delete_breakpoint <number>")
|
||||
self._output("Delete the breakpoint on execution marked by the given number")
|
||||
|
||||
def do_list_breakpoints(self, args):
|
||||
for (index, bp) in enumerate(self._address_parser.breakpoints):
|
||||
if bp is None:
|
||||
continue
|
||||
label = self._address_parser.label_for(bp, '')
|
||||
output_string = "Breakpoint %d : $%04X %s" % (index, bp, label)
|
||||
self._output(output_string.strip())
|
||||
|
||||
def help_list_breakpoints(self):
|
||||
self._output("list_breakpoints")
|
||||
self._output("Lists the currently assigned breakpoints")
|
||||
|
||||
def main(args=None):
|
||||
c = Monitor()
|
||||
|
@ -5,7 +5,7 @@ class AddressParser(object):
|
||||
"""Parse user input into addresses or ranges of addresses.
|
||||
"""
|
||||
|
||||
def __init__(self, maxwidth=16, radix=16, labels={}):
|
||||
def __init__(self, maxwidth=16, radix=16, labels={}, breakpoints=[]):
|
||||
"""Maxwidth is the maximum width of an address in bits.
|
||||
Radix is the default radix to use when one is not specified
|
||||
as a prefix of any input. Labels are a dictionary of label
|
||||
@ -18,6 +18,12 @@ class AddressParser(object):
|
||||
for k, v in labels.items():
|
||||
self.labels[k] = self._constrain(v)
|
||||
|
||||
self.breakpoints = []
|
||||
for v in breakpoints:
|
||||
address = self._constrain(v)
|
||||
if address not in self.breakpoints:
|
||||
self.breakpoints.append(address)
|
||||
|
||||
def _get_maxwidth(self):
|
||||
return self._maxwidth
|
||||
|
||||
@ -35,6 +41,10 @@ class AddressParser(object):
|
||||
return label
|
||||
return default
|
||||
|
||||
def address_for(self, label):
|
||||
"""Given a label, return the corresponding address or None."""
|
||||
return self.labels.get(label, None)
|
||||
|
||||
def number(self, num):
|
||||
"""Parse a string containing a label or number into an address.
|
||||
"""
|
||||
|
Loading…
Reference in New Issue
Block a user