diff --git a/boot.py b/boot.py index 67b33db..0455949 100644 --- a/boot.py +++ b/boot.py @@ -292,10 +292,19 @@ if __name__ == "__main__": pdpoptions = {'drivenames': args.drives} runoptions = {} + bkpts = [] if args.debug: pdpoptions['loglevel'] = 'DEBUG' + bkpts.append(breakpoints.MemChecker(1000000)) if args.instlog: - runoptions['breakpoint'] = breakpoints.Logger() + bkpts.append(breakpoints.Logger()) + + if bkpts: + # if there is just one, use it directly, else combine 'em + if len(bkpts) == 1: + runoptions['breakpoint'] = bkpts[0] + else: + runoptions['breakpoint'] = breakpoints.MultiBreakpoint(*bkpts) p = make_unix_machine(**pdpoptions) diff --git a/breakpoints.py b/breakpoints.py index beb847f..eb1a8ae 100644 --- a/breakpoints.py +++ b/breakpoints.py @@ -154,3 +154,33 @@ class Logger(Breakpoint): inst_s = "None" if inst is None else oct(inst) self.__logger.debug(f"{pc_s}/{m_s} :: {inst_s}") return self.__bp(pdp, xinfo) + +# Useful for ensuring the emulation implementation isn't broken in +# some way - making sure all values in physical memory are 16 bits (only). +# This will run an expensive check every nth instructions. +# Obviously, if bogus values are found it will be nearly impossible to +# figure out HOW they got in there; put some checks back into readRW/etc +# functions as necessary to debug that (they are removed for performance). + + +class MemChecker(Breakpoint): + def __init__(self, nth): + self.check_every = nth + self.togo = nth + + def __call__(self, pdp, xinfo): + self.togo -= 1 + if self.togo == 0: + self.check16(pdp) + self.togo = self.check_every + return False + + def check16(self, pdp): + """Verifies no illegal values ended up in physical memory or regs.""" + pdp.logger.debug("Check16 checking physical memory") + for a, w in enumerate(pdp.physmem): + if w < 0 or w > 65535: + raise ValueError(f"Illegal physmem value {w} @ {oct(a<<2)}") + for r, w in enumerate(pdp.r): + if w < 0 or w > 65535: + raise ValueError(f"Illegal reg value {w} @ {r}")