1
0
mirror of https://github.com/mnaberez/py65.git synced 2024-06-09 10:29:35 +00:00

Basic breakpoint support.

This commit is contained in:
Alessandro Gatti 2014-10-01 13:31:31 +02:00
parent 3502d32189
commit 3e002e4115
3 changed files with 176 additions and 4 deletions

View File

@ -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::

View File

@ -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()

View File

@ -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.
"""