Breakpoints as class objects now
This commit is contained in:
parent
6924a81e2b
commit
eec2d1da54
1 changed files with 36 additions and 76 deletions
112
machine.py
112
machine.py
|
@ -32,6 +32,7 @@ from kl11 import KL11
|
|||
from rp import RPRM
|
||||
# from rpa import RPRM_AIO as RPRM
|
||||
from kw11 import KW11
|
||||
from breakpoints import StepsBreakpoint, PCBreakpoint
|
||||
|
||||
from op4 import op4_dispatch_table
|
||||
|
||||
|
@ -493,7 +494,24 @@ class PDP11:
|
|||
|
||||
return (val, extendedb6) if rmw else val
|
||||
|
||||
def run(self, *, steps=None, pc=None, stopat=None, loglevel=None):
|
||||
# convenience, creates a breakpoint function to stop after N steps
|
||||
def run_steps(self, steps, *args, **kwargs):
|
||||
bpt = StepsBreakpoint(steps=steps)
|
||||
return self.run(*args, breakpoint=bpt, **kwargs)
|
||||
|
||||
# convenience, creates a breakpoint to stop at the given pc,
|
||||
# optionally limited to a specific mode (KERNEL, USER, SUPERVISOR)
|
||||
def run_until(self, *args, stoppc, stopmode=None, **kwargs):
|
||||
"""Run processor with breakpoint at 'stopat'.
|
||||
|
||||
If stopat is an integer, stop at that PC value in any mode.
|
||||
If stopat is a tuple, stop at (pc, mode) where mode
|
||||
is self.KERNEL, self.SUPERVISOR or self.USER
|
||||
"""
|
||||
bpt = PCBreakpoint(stoppc=stoppc, stopmode=stopmode)
|
||||
return self.run(*args, breakpoint=bpt, **kwargs)
|
||||
|
||||
def run(self, *, pc=None, loglevel=None, breakpoint=None):
|
||||
"""Run the machine for a number of steps (instructions).
|
||||
|
||||
If steps is None (default), the machine runs until a HALT instruction
|
||||
|
@ -512,20 +530,6 @@ class PDP11:
|
|||
if pc is not None:
|
||||
self.r[self.PC] = pc
|
||||
|
||||
# Breakpoints (and step limits) are in the critical path.
|
||||
# To keep overhead to a minimum, breakpointfunc creates a
|
||||
# custom function to evaluate breakpoint criteria. When there
|
||||
# are no breakpoints or step limits at all, stop_here will be None.
|
||||
# Hence the test construction:
|
||||
#
|
||||
# if stop_here and stop_here()
|
||||
#
|
||||
# which is as fast as it can be when there are no execution limits.
|
||||
# When there ARE breakpoints etc, stop_here is a callable that
|
||||
# evaluates all stop criteria and returns True if the inner loop
|
||||
# should break.
|
||||
stop_here = self.breakpointfunc(stopat, steps)
|
||||
|
||||
# some shorthands for convenience
|
||||
interrupt_mgr = self.ub.intmgr
|
||||
mmu = self.mmu
|
||||
|
@ -559,7 +563,7 @@ class PDP11:
|
|||
elif interrupt_mgr.pri_pending > self.psw_pri:
|
||||
self.go_trap(interrupt_mgr.get_pending(self.psw_pri))
|
||||
|
||||
if stop_here and stop_here():
|
||||
if breakpoint and breakpoint(self):
|
||||
break
|
||||
|
||||
# fall through to here if self.halted or a stop_here condition
|
||||
|
@ -567,55 +571,6 @@ class PDP11:
|
|||
if self.halted:
|
||||
self.logger.debug(f".run HALTED: {self.machinestate()}")
|
||||
|
||||
def breakpointfunc(self, stopat, steps):
|
||||
# create a custom function that returns True if execution
|
||||
# meets the stop criteria. The returned function MUST be
|
||||
# called EXACTLY ONCE per instruction execution.
|
||||
#
|
||||
# If steps is not None, then at most that many invocations can
|
||||
# occur before execution will be halted (i.e., True returned).
|
||||
#
|
||||
# stopat can be a tuple: (pc, mode) or just a naked pc value.
|
||||
# Execution will halt when the processor reaches that pc
|
||||
# (in the given mode, or in any mode if not given).
|
||||
#
|
||||
# If both stopat and steps are None, then this returns None,
|
||||
# which allows the run() loop to optimize out the check.
|
||||
|
||||
if stopat is None and steps is None:
|
||||
return None
|
||||
|
||||
if steps is None:
|
||||
stepsgen = itertools.count()
|
||||
else:
|
||||
stepsgen = range(steps-1)
|
||||
|
||||
try:
|
||||
stoppc, stopmode = stopat
|
||||
except TypeError:
|
||||
stoppc = stopat
|
||||
stopmode = None
|
||||
|
||||
def _evalstop():
|
||||
icount = 0 # needed if steps == 1
|
||||
for icount in stepsgen:
|
||||
|
||||
# this is sneaky ... it's can be handy in debugging to
|
||||
# know the instruction count; stuff it into the cpu object
|
||||
self.xxx_instcount = icount
|
||||
|
||||
if self.r[self.PC] == stoppc:
|
||||
if stopmode is None or self.psw_curmode == stopmode:
|
||||
self.logger.info(f".run: breakpt at {oct(stoppc)}")
|
||||
break
|
||||
yield False
|
||||
else:
|
||||
self.logger.info(f".run: ran {icount+1} steps")
|
||||
yield True
|
||||
|
||||
g = _evalstop()
|
||||
return lambda: next(g)
|
||||
|
||||
def redyellowcheck(self):
|
||||
"""stack limits: possibly sets YELLOW straps, or go RED."""
|
||||
|
||||
|
@ -1031,18 +986,23 @@ class PDP1170(PDP11):
|
|||
return s
|
||||
|
||||
# logging/debugging convenience
|
||||
def machinestate(self, brief=False):
|
||||
s = self.spsw() + '; '
|
||||
stacknames = ("KSP", "SSP", "!X!", "USP")
|
||||
regnames = (* (f"R{i}" for i in range(6)),
|
||||
stacknames[self.psw_curmode], "PC")
|
||||
def machinestate(self):
|
||||
# machine state is arbitrarily collected into a dictionary:
|
||||
d = {}
|
||||
regnames = (* (f"R{i}" for i in range(6)), "SP", "PC")
|
||||
for i in range(8):
|
||||
s += f"{regnames[i]}: {oct(self.r[i])} "
|
||||
d[regnames[i]] = self.r[i]
|
||||
|
||||
d['PSW'] = self.psw
|
||||
|
||||
# these are redundant but convenient to have broken out
|
||||
d['CURMODE'] = self.psw_curmode
|
||||
d['PRI'] = self.psw_pri
|
||||
|
||||
for m in (0, 1, 3):
|
||||
name = stacknames[m]
|
||||
if m == self.psw_curmode:
|
||||
name = name[0] + "xx"
|
||||
s += f"{name}: {oct(self.stackpointers[m])} "
|
||||
d[("KSP", "SSP", "!X!", "USP")[m]] = self.stackpointers[m]
|
||||
|
||||
return s
|
||||
for mmr in ('MMR0', 'MMR1', 'MMR2', 'MMR3'):
|
||||
d[mmr] = getattr(self.mmu, mmr)
|
||||
|
||||
return d
|
||||
|
|
Loading…
Add table
Reference in a new issue