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 rp import RPRM
|
||||||
# from rpa import RPRM_AIO as RPRM
|
# from rpa import RPRM_AIO as RPRM
|
||||||
from kw11 import KW11
|
from kw11 import KW11
|
||||||
|
from breakpoints import StepsBreakpoint, PCBreakpoint
|
||||||
|
|
||||||
from op4 import op4_dispatch_table
|
from op4 import op4_dispatch_table
|
||||||
|
|
||||||
|
@ -493,7 +494,24 @@ class PDP11:
|
||||||
|
|
||||||
return (val, extendedb6) if rmw else val
|
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).
|
"""Run the machine for a number of steps (instructions).
|
||||||
|
|
||||||
If steps is None (default), the machine runs until a HALT instruction
|
If steps is None (default), the machine runs until a HALT instruction
|
||||||
|
@ -512,20 +530,6 @@ class PDP11:
|
||||||
if pc is not None:
|
if pc is not None:
|
||||||
self.r[self.PC] = pc
|
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
|
# some shorthands for convenience
|
||||||
interrupt_mgr = self.ub.intmgr
|
interrupt_mgr = self.ub.intmgr
|
||||||
mmu = self.mmu
|
mmu = self.mmu
|
||||||
|
@ -559,7 +563,7 @@ class PDP11:
|
||||||
elif interrupt_mgr.pri_pending > self.psw_pri:
|
elif interrupt_mgr.pri_pending > self.psw_pri:
|
||||||
self.go_trap(interrupt_mgr.get_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
|
break
|
||||||
|
|
||||||
# fall through to here if self.halted or a stop_here condition
|
# fall through to here if self.halted or a stop_here condition
|
||||||
|
@ -567,55 +571,6 @@ class PDP11:
|
||||||
if self.halted:
|
if self.halted:
|
||||||
self.logger.debug(f".run HALTED: {self.machinestate()}")
|
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):
|
def redyellowcheck(self):
|
||||||
"""stack limits: possibly sets YELLOW straps, or go RED."""
|
"""stack limits: possibly sets YELLOW straps, or go RED."""
|
||||||
|
|
||||||
|
@ -1031,18 +986,23 @@ class PDP1170(PDP11):
|
||||||
return s
|
return s
|
||||||
|
|
||||||
# logging/debugging convenience
|
# logging/debugging convenience
|
||||||
def machinestate(self, brief=False):
|
def machinestate(self):
|
||||||
s = self.spsw() + '; '
|
# machine state is arbitrarily collected into a dictionary:
|
||||||
stacknames = ("KSP", "SSP", "!X!", "USP")
|
d = {}
|
||||||
regnames = (* (f"R{i}" for i in range(6)),
|
regnames = (* (f"R{i}" for i in range(6)), "SP", "PC")
|
||||||
stacknames[self.psw_curmode], "PC")
|
|
||||||
for i in range(8):
|
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):
|
for m in (0, 1, 3):
|
||||||
name = stacknames[m]
|
d[("KSP", "SSP", "!X!", "USP")[m]] = self.stackpointers[m]
|
||||||
if m == self.psw_curmode:
|
|
||||||
name = name[0] + "xx"
|
|
||||||
s += f"{name}: {oct(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