mirror of
https://github.com/open-simh/simh.git
synced 2026-01-13 15:27:46 +00:00
Make generate.py resuable outside of open-simh, as suggested and
motivated by Richard Cornwell's simulator repository.
- Make the "experimental" rule optional. Do not generate a Python
"KeyError" if the rule is missing.
- Add documentation on how to use the CMake infrastructure outside
of open-simh: Customize the packaging.py script, season to taste.
- Update the KA10 simulator customization, moving it to its own
Python script, simgen/pdp10_simulator.py. Preparatory move that
anticipates additional frontpanel and display options.
- generate.py option "--skip-orphans": Skip the orphaned simulator
check (i.e., don't cross-reference the simulators in packaging.py
with what was scraped from the makefile.)
- Add "TEST_ARGS" argument to CMake's add_simulator function so that the
IBM 1130 simulator can pass to "-g" on the command line to disable the
GUI when running RegisterSanityCheck, i.e.:
ibm1130 RegisterSanityCheck -g
This fixes an edge case Heisenbug encountered during Github CI/CD
tests where ibm1130 appears to hang indefinitely on the Windows
runners.
The cause is the GUI's Pump() thread function being prematurely
terminated before all GUI resources are acquired. The net result is an
infinite loop in the MS C runtime trying to exit the process with
unstable internal state. (Separate patch: synchronization across main
and Pump() threads to ensure resource acquisition completes.)
This issue never shows up on non-Windows platforms or the SIMH makefile.
- cmake/generator.py, cmake/simgen: Add a "test_args" keyword argument
to the BasicSimulator constructor that holds the tests argument
parameter emitted as the "TEST_ARGS" argument to a simulator's
add_simulator(). Ensure that the IBM 1130 emits 'TEST_ARG "-g"' in its
add_simulator().
- scp.c: reset_all_p() adds 'P' to the existing switches, versus saving
sim_switches and ONLY setting the 'P' power-up reset switch. Net effect
is that the IBM 1130 simulator actually sees the 'G' flag that inhibits
the GUI during the console device reset.
161 lines
6.2 KiB
Python
161 lines
6.2 KiB
Python
import pprint
|
|
|
|
import simgen.parse_makefile as SPM
|
|
import simgen.basic_simulator as SBS
|
|
import simgen.ibm1130_simulator as IBM1130
|
|
import simgen.pdp10_simulator as PDP10
|
|
import simgen.vax_simulators as VAXen
|
|
import simgen.utils as SU
|
|
|
|
## Special variables that should __not__ expand into their definitions:
|
|
_special_vars = frozenset(['DISPLAYL',
|
|
'DISPLAYVT',
|
|
'DISPLAY340',
|
|
'DISPLAYNG',
|
|
'DISPLAYIII'])
|
|
|
|
## Map simulator name to its class, for special cases
|
|
_special_simulators = {
|
|
"besm6": SBS.BESM6Simulator,
|
|
"i650": SBS.IBM650Simulator,
|
|
"ibm1130": IBM1130.IBM1130Simulator,
|
|
"pdp10-ka": PDP10.KA10Simulator,
|
|
"vax": VAXen.VAXSimulator,
|
|
"vax730": VAXen.BasicVAXSimulator
|
|
}
|
|
|
|
ignored_display_macros = {
|
|
'DISPLAYVT': ['${DISPLAYD}/vt11.c'],
|
|
'DISPLAY340': ['${DISPLAYD}/type340.c'],
|
|
'DISPLAYNG': ['${DISPLAYD}/ng.c'],
|
|
'DISPLAYIII': ['${DISPLAYD}/iii.c']
|
|
}
|
|
|
|
def get_simulator_ctor(name):
|
|
"""Return the class object for special case simulators, otherwise
|
|
return the base 'SIMHBasicSimulator'
|
|
"""
|
|
return _special_simulators.get(name) or SBS.SIMHBasicSimulator
|
|
|
|
|
|
class SimCollection:
|
|
"""A collection of simulators.
|
|
"""
|
|
def __init__(self, dir_macro):
|
|
self.source_macros = {}
|
|
self.macro_uses = {}
|
|
self.simulators = {}
|
|
|
|
def get_simulator(self, name, dir_macro, _dir_path, test_name, buildrom):
|
|
sim = self.simulators.get(name)
|
|
if sim is None:
|
|
sim = (get_simulator_ctor(name))(name, dir_macro, test_name, buildrom)
|
|
self.simulators[name] = sim
|
|
return sim
|
|
|
|
def add_source_macro(self, macro, macro_def, sim):
|
|
if macro not in self.source_macros:
|
|
self.source_macros[macro] = macro_def
|
|
|
|
used = self.macro_uses.get(macro)
|
|
if used is None:
|
|
self.macro_uses[macro] = []
|
|
used = self.macro_uses[macro]
|
|
used.append(sim)
|
|
|
|
def get_simulator_vars(self, debug=0):
|
|
simvars = set()
|
|
ignored = set(self.source_macros.keys())
|
|
for macval in self.source_macros.values():
|
|
## This could be replaced by a functools.reduce()
|
|
for val in macval:
|
|
simvars = simvars.union(set(SPM.extract_variables(val)))
|
|
|
|
for sim in self.simulators.values():
|
|
simvars = simvars.union(sim.get_source_vars().union(sim.get_include_vars()))
|
|
|
|
simvars = simvars.difference(ignored).difference(_special_vars)
|
|
SU.emit_debug(debug, 2, 'simvars {0}'.format(simvars))
|
|
return simvars
|
|
|
|
def write_simulators(self, stream, debug=0, test_label='default'):
|
|
## Emit source macros
|
|
dontexpand = set([smac for smac, uses in self.macro_uses.items() if smac not in ignored_display_macros and len(uses) > 1])
|
|
SU.emit_debug(debug, 2, "{0}: dontexpand {1}".format(self.__class__.__name__, dontexpand))
|
|
|
|
if len(dontexpand) > 0:
|
|
smac_sorted = list(dontexpand)
|
|
smac_sorted.sort()
|
|
for smac in smac_sorted:
|
|
stream.write('\n\n')
|
|
stream.write('set({0}\n'.format(smac))
|
|
stream.write('\n'.join([' ' * 4 + f for f in self.source_macros[smac]]))
|
|
stream.write(')')
|
|
stream.write('\n\n')
|
|
|
|
## Emit the simulators
|
|
simnames = list(self.simulators.keys())
|
|
simnames.sort()
|
|
SU.emit_debug(debug, 2, "{0}: Writing {1}".format(self.__class__.__name__, simnames))
|
|
for simname in simnames:
|
|
sim = self.simulators[simname]
|
|
|
|
## Patch up the simulator source lists, expanding macros that aren't
|
|
## in the macro sources:
|
|
sim.sources = self.expand_sources(sim.sources, dontexpand, debug)
|
|
|
|
stream.write('\n')
|
|
sim.write_simulator(stream, 0, test_label)
|
|
|
|
def write_unit_tests(self, stream, debug=0, test_label='default'):
|
|
dontexpand = set([smac for smac, uses in self.macro_uses.items() if len(uses) > 1])
|
|
|
|
simnames = list(self.simulators.keys())
|
|
simnames.sort()
|
|
SU.emit_debug(debug, 2, "{0}: Writing {1}".format(self.__class__.__name__, simnames))
|
|
for simname in simnames:
|
|
sim = self.simulators[simname]
|
|
|
|
## Patch up the simulator source lists, expanding macros that aren't
|
|
## in the macro sources:
|
|
sim.sources = self.expand_sources(sim.sources, dontexpand, debug)
|
|
sim.write_unit_test(stream, 0, test_label)
|
|
|
|
def expand_sources(self, srcs, dontexpand, debug=0):
|
|
updated_srcs = []
|
|
for src in srcs:
|
|
SU.emit_debug(debug, 2, "{0}: Source {1}".format(self.__class__.__name__, src))
|
|
m = SPM._var_rx.match(src)
|
|
if m and m[1] not in dontexpand.union(_special_vars):
|
|
SU.emit_debug(debug, 2, "{0}: Expanding {1}".format(self.__class__.__name__, m[1]))
|
|
varexp = self.source_macros.get(m[1])
|
|
if varexp is not None:
|
|
updated_srcs.extend(self.source_macros[m[1]])
|
|
else:
|
|
print('!! Could not expand {0}'.format(m[1]))
|
|
else:
|
|
updated_srcs.append(src)
|
|
|
|
if updated_srcs == srcs:
|
|
return srcs
|
|
else:
|
|
return self.expand_sources(updated_srcs, dontexpand, debug)
|
|
|
|
def __len__(self):
|
|
return len(self.simulators)
|
|
|
|
if '_dispatch' in pprint.PrettyPrinter.__dict__:
|
|
def simcoll_pprinter(pprinter, simcoll, stream, indent, allowance, context, level):
|
|
cls = simcoll.__class__
|
|
stream.write(cls.__name__ + '(')
|
|
indent += len(cls.__name__) + 1
|
|
pprinter._format(simcoll.source_macros, stream, indent, allowance + 2, context, level)
|
|
stream.write(',\n' + ' ' * indent)
|
|
uses_dict = dict([(sim, len(uses)) for (sim, uses) in simcoll.macro_uses.items()])
|
|
pprinter._format(uses_dict, stream, indent, allowance + 2, context, level)
|
|
stream.write(',\n' + ' ' * indent)
|
|
pprinter._format(simcoll.simulators, stream, indent, allowance + 2, context, level)
|
|
stream.write(')')
|
|
|
|
pprint.PrettyPrinter._dispatch[SimCollection.__repr__] = simcoll_pprinter
|