mirror of
https://github.com/rzzzwilson/pymlac.git
synced 2025-06-10 09:32:41 +00:00
Working towards a runnable Imlac
This commit is contained in:
parent
3eb41c0536
commit
440fb57990
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
@ -39,11 +39,11 @@ class _BufferedCanvas(wx.Panel):
|
||||
# The backing buffer
|
||||
buffer = None
|
||||
|
||||
# max coordinates of scaled display
|
||||
ScaleMaxX = 2048
|
||||
ScaleMaxY = 2048
|
||||
# ScaleMaxX = 512
|
||||
# ScaleMaxY = 512
|
||||
# max coordinates of scaled display
|
||||
ScaleMaxX = 2048
|
||||
ScaleMaxY = 2048
|
||||
# ScaleMaxX = 512
|
||||
# ScaleMaxY = 512
|
||||
|
||||
def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
|
||||
size=wx.DefaultSize, style=wx.NO_FULL_REPAINT_ON_RESIZE):
|
||||
@ -133,18 +133,18 @@ class Display(_BufferedCanvas):
|
||||
SYNC_HZ = 40
|
||||
SYNC_40HZ_CYCLE_COUNT = int(CYCLES_PER_SECOND / SYNC_HZ)
|
||||
|
||||
# max coordinates of pymlac display
|
||||
# ScaleMaxX = 2048
|
||||
# ScaleMaxY = 2048
|
||||
ScaleMaxX = 1024
|
||||
ScaleMaxY = 1024
|
||||
# max coordinates of pymlac display
|
||||
# ScaleMaxX = 2048
|
||||
# ScaleMaxY = 2048
|
||||
ScaleMaxX = 1024
|
||||
ScaleMaxY = 1024
|
||||
|
||||
|
||||
def __init__(self, parent, **kwargs):
|
||||
# create and initialise the base panel
|
||||
_BufferedCanvas.__init__(self, parent=parent, **kwargs)
|
||||
self.SetBackgroundColour(self.BackgroundColour)
|
||||
|
||||
# create and initialise the base panel
|
||||
_BufferedCanvas.__init__(self, parent=parent, **kwargs)
|
||||
self.SetBackgroundColour(self.BackgroundColour)
|
||||
|
||||
# set internal state
|
||||
self.running = 0
|
||||
self.cycle_count = 0
|
||||
@ -187,7 +187,7 @@ class Display(_BufferedCanvas):
|
||||
pen.SetStyle(wx.DOT)
|
||||
pen.SetWidth(2)
|
||||
dc.SetPen(pen)
|
||||
|
||||
|
||||
dc.DrawLine(x1, self.ScaleMaxY - y1,
|
||||
x2, self.ScaleMaxY - y2)
|
||||
|
||||
|
||||
289
pymlac/Imlac.py
289
pymlac/Imlac.py
@ -1,24 +1,54 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
The Imlac emulation object.
|
||||
A simulator for an Imlac PDS-1 or PDS-4.
|
||||
|
||||
Usage: pymlac [ <option> ]*
|
||||
|
||||
That is, the user may specify zero or more options interspersed in any manner
|
||||
required. The options are:
|
||||
|
||||
-b (ptr | tty | none) sets the bootstrap ROM code:
|
||||
ptr uses the papertape bootstrap ROM
|
||||
tty uses the teletype bootstrap ROM
|
||||
none uses no bootstrap ROM
|
||||
-c clears core including bootstrap ROM, if write enabled
|
||||
-cf <filename> sets the name of the core file to read and write
|
||||
(default file is 'pymlac.core')
|
||||
-d <value> sets the console data switches to the <value>
|
||||
-h prints this help
|
||||
-ptp <file> loads a file on to the papertape punch device
|
||||
-ptr <file> loads a file on to the papertape reader device
|
||||
-r (<address> | pc) executes from <address> or the current PC contents
|
||||
-s <setfile> sets memory adress values from <setfile>
|
||||
-t (<addr1> [,<addr2>] | off) controls the execution trace:
|
||||
-t 0100 trace from address 0100 (octal)
|
||||
-t 0100,200 trace from 0100 octal to 200 decimal
|
||||
-t off turns trace off
|
||||
-ttyin <file> loads a file on to the teletype reader device
|
||||
-ttyout <file> loads a file on to the teletype writer device
|
||||
-v <viewfile> views contents of memory addresses from file
|
||||
-w (on | off) controls ROM write property:
|
||||
-w on ROM addresses are writable
|
||||
-w off ROM addresses are write protected
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import getopt
|
||||
|
||||
from Globals import *
|
||||
|
||||
import PtrPtp
|
||||
import TtyIn
|
||||
import TtyOut
|
||||
import Memory
|
||||
import MainCPU
|
||||
import DisplayCPU
|
||||
import PtrPtp
|
||||
import TtyIn
|
||||
import TtyOut
|
||||
import Trace
|
||||
|
||||
|
||||
|
||||
class Imlac(object):
|
||||
"""The Imlac object - contains all devices."""
|
||||
|
||||
@ -29,76 +59,267 @@ class Imlac(object):
|
||||
self.traceend = None
|
||||
self.DS = 0100000 # dataswitches
|
||||
|
||||
def init(self, run_address, tracefile, tstart, tend,
|
||||
boot_rom=None, corefile=None):
|
||||
|
||||
Memory.init(boot_rom, corefile)
|
||||
Trace.init(tracefile)
|
||||
self.tracestart = tstart
|
||||
self.traceend = tend
|
||||
self.memory = Memory.Memory()
|
||||
self.ptrptp = PtrPtp.PtrPtp()
|
||||
self.cpu = MainCPU.MainCPU(self.memory, None, None,
|
||||
None, None, None, self.ptrptp)
|
||||
self.dcpu = DisplayCPU.DisplayCPU(self.memory)
|
||||
|
||||
def close(corefile=None):
|
||||
if corefile:
|
||||
Memory.savecore(corefile)
|
||||
self.memory.savecore(corefile)
|
||||
sys.exit()
|
||||
|
||||
def set_ROM(type):
|
||||
Memory.set_ROM(type)
|
||||
self.memory.set_ROM(type)
|
||||
|
||||
def set_boot(romtype):
|
||||
pass
|
||||
|
||||
def __tick_all(cycles):
|
||||
PtrPtp.ptr_tick(cycles)
|
||||
PtrPtp.ptp_tick(cycles)
|
||||
TtyIn.tick(cycles)
|
||||
TtyOut.tick(cycles)
|
||||
self.ptrptp.ptr_tick(cycles)
|
||||
self.ptrptp.ptp_tick(cycles)
|
||||
# self.ttyin.tick(cycles)
|
||||
# self.ttyout.tick(cycles)
|
||||
|
||||
def set_trace(tstart, tend=None):
|
||||
"""Set trace to required range of values."""
|
||||
|
||||
global tracestart, traceend
|
||||
|
||||
if tstart:
|
||||
tracestart = tstart
|
||||
traceend = tend
|
||||
self.tracestart = tstart
|
||||
self.traceend = tend
|
||||
Trace.tracing = True
|
||||
else:
|
||||
Trace.tracing = False
|
||||
|
||||
def execute_once():
|
||||
if traceend is None:
|
||||
if MainCPU.PC == tracestart:
|
||||
if self.traceend is None:
|
||||
if MainCPU.PC == self.tracestart:
|
||||
Trace.settrace(True)
|
||||
else:
|
||||
Trace.settrace(MainCPU.PC >= tracestart and MainCPU.PC <= traceend)
|
||||
Trace.settrace(MainCPU.PC >= self.tracestart
|
||||
and MainCPU.PC <= self.traceend)
|
||||
|
||||
if DisplayCPU.ison():
|
||||
if self.dcpu.ison():
|
||||
Trace.trace('%6.6o' % DisplayCPU.DPC)
|
||||
Trace.trace('\t')
|
||||
|
||||
instruction_cycles = DisplayCPU.execute_one_instruction()
|
||||
instruction_cycles = self.dcpu.execute_one_instruction()
|
||||
|
||||
Trace.trace('%6.6o\t' % MainCPU.PC)
|
||||
|
||||
instruction_cycles += MainCPU.execute_one_instruction()
|
||||
instruction_cycles += self.cpu.execute_one_instruction()
|
||||
|
||||
Trace.itraceend(DisplayCPU.ison())
|
||||
|
||||
__tick_all(instruction_cycles)
|
||||
|
||||
if not DisplayCPU.ison() and not MainCPU.running:
|
||||
if not self.dcpu.ison() and not self.cpu.running:
|
||||
return 0
|
||||
|
||||
return instruction_cycles
|
||||
|
||||
def run():
|
||||
def run(self):
|
||||
"""Start the machine and run until it halts."""
|
||||
|
||||
MainCPU.running = True
|
||||
self.cpu.running = True
|
||||
|
||||
while execute_once() > 0:
|
||||
while self.execute_once() > 0:
|
||||
pass
|
||||
|
||||
MainCPU.running = False
|
||||
self.cpu.running = False
|
||||
|
||||
def set_memory_from_file(self, filename):
|
||||
"""Set Imlac memory from file."""
|
||||
|
||||
raise Exception('set_memory_from_file: unimplemented')
|
||||
|
||||
|
||||
|
||||
def str2int(self, s):
|
||||
"""Convert string to numeric value.
|
||||
|
||||
s numeric string (decimal or octal)
|
||||
|
||||
Returns the numeric value.
|
||||
"""
|
||||
|
||||
base = 10
|
||||
if s[0] == '0':
|
||||
base = 8
|
||||
|
||||
try:
|
||||
value = int(s, base=base)
|
||||
except:
|
||||
return None
|
||||
return value
|
||||
|
||||
|
||||
def usage(msg=None):
|
||||
if msg:
|
||||
print('*'*60)
|
||||
print(msg)
|
||||
print('*'*60)
|
||||
print(__doc__)
|
||||
|
||||
|
||||
def main():
|
||||
"""Main function of the simulator. Mostly interpret CLI args.
|
||||
|
||||
Instantiate and run the Imlac object.
|
||||
"""
|
||||
|
||||
# start wxPython app
|
||||
app = wx.App()
|
||||
TestFrame().Show()
|
||||
app.MainLoop()
|
||||
sys.exit(0)
|
||||
|
||||
# create Imlac object with default settings
|
||||
imlac = Imlac()
|
||||
|
||||
# read CLI args, left to right and process
|
||||
len_sys_argv = len(sys.argv)
|
||||
|
||||
ndx = 1
|
||||
while ndx < len_sys_argv:
|
||||
opt = sys.argv[ndx]
|
||||
ndx += 1
|
||||
print('opt=%s' % opt)
|
||||
if opt[0] != '-':
|
||||
usage("Bad option: '%s'" % str(opt))
|
||||
|
||||
if opt == '-b':
|
||||
if ndx >= len_sys_argv:
|
||||
usage("'-b' option needs a following device name")
|
||||
sys.exit(10)
|
||||
dev = sys.argv[ndx].lower()
|
||||
ndx += 1
|
||||
if dev == 'ptr':
|
||||
imlac.set_ROM('ptr')
|
||||
elif dev == 'tty':
|
||||
imlac.set_ROM('tty')
|
||||
elif dev == 'none':
|
||||
imlac.set_ROM(None)
|
||||
else:
|
||||
usage("-b option must be followed by 'ptr', 'tty' or 'none'")
|
||||
sys.exit(10)
|
||||
elif opt == '-c':
|
||||
imlac.memory.clear_core()
|
||||
elif opt == '-cf':
|
||||
if ndx >= len_sys_argv:
|
||||
usage("'-cf' option needs a following filename")
|
||||
sys.exit(10)
|
||||
filename = sys.argv[ndx]
|
||||
ndx += 1
|
||||
imlac.memory.set_corefile(filename)
|
||||
elif opt == '-d':
|
||||
if ndx >= len_sys_argv:
|
||||
usage("'-d' option needs a following data switch value")
|
||||
sys.exit(10)
|
||||
value = str2int(sys.argv[ndx])
|
||||
ndx += 1
|
||||
if value is None:
|
||||
usage("The '-d' option must be followed by a decimal or octal value")
|
||||
sys.exit(10)
|
||||
imlac.cpu.set_dataswitches(value)
|
||||
elif opt == '-h':
|
||||
usage()
|
||||
sys.exit(0)
|
||||
elif opt == '-ptp':
|
||||
if ndx >= len_sys_argv:
|
||||
usage("'-ptp' option needs a following PTP filename")
|
||||
sys.exit(10)
|
||||
filename = sys.argv[ndx]
|
||||
ndx += 1
|
||||
imlac.ptrptp.ptp_mount(filename)
|
||||
elif opt == '-ptr':
|
||||
if ndx >= len_sys_argv:
|
||||
usage("'-ptr' option needs a following PTR filename")
|
||||
sys.exit(10)
|
||||
filename = sys.argv[ndx]
|
||||
ndx += 1
|
||||
imlac.ptrptp.ptr_mount(filename)
|
||||
elif opt == '-r':
|
||||
if ndx >= len_sys_argv:
|
||||
usage("'-r' option needs a following address or 'pc'")
|
||||
sys.exit(10)
|
||||
address = sys.argv[ndx].lower()
|
||||
ndx += 1
|
||||
if address != 'pc':
|
||||
addr_value = str2int(address)
|
||||
if addr_value is None:
|
||||
usage("'-r' option needs a following address or 'pc'")
|
||||
sys.exit(10)
|
||||
self.cpu.PC = addr_value
|
||||
imlac.run()
|
||||
elif opt == '-s':
|
||||
if ndx >= len_sys_argv:
|
||||
usage("'-s' option needs a following data filename")
|
||||
sys.exit(10)
|
||||
filename = sys.argv[ndx]
|
||||
ndx += 1
|
||||
imlac.set_memory_from_file(filename)
|
||||
elif opt == '-t':
|
||||
if ndx >= len_sys_argv:
|
||||
usage("'-t' option needs a following address range or 'off'")
|
||||
sys.exit(10)
|
||||
r = sys.argv[ndx]
|
||||
ndx += 1
|
||||
if r == 'off':
|
||||
set_trace_off()
|
||||
else:
|
||||
if ',' in r:
|
||||
rr = r.split(',')
|
||||
if len(rr) != 2:
|
||||
usage("'-r' option may be followed by only two addresses")
|
||||
sys.exit(10)
|
||||
(start, stop) = rr
|
||||
start_addr = str2int(start)
|
||||
stop_addr = str2int(stop)
|
||||
else:
|
||||
start_addr = str2int(r)
|
||||
stop_addr = None
|
||||
imlac.set_trace(star_addr, stop_addr)
|
||||
elif opt == '-ttyin':
|
||||
raise Exception('-ttyin: Unimplemented')
|
||||
# if ndx >= len_sys_argv:
|
||||
# usage("'-ttyin' option needs a following data filename")
|
||||
# sys.exit(10)
|
||||
# filename = sys.argv[ndx]
|
||||
# ndx += 1
|
||||
# set_tty_in(filename)
|
||||
elif opt == '-ttyout':
|
||||
raise Exception('-ttyout: Unimplemented')
|
||||
# if ndx >= len_sys_argv:
|
||||
# usage("'-ttyout' option needs a following data filename")
|
||||
# sys.exit(10)
|
||||
# filename = sys.argv[ndx]
|
||||
# ndx += 1
|
||||
# set_tty_out(filename)
|
||||
elif opt == '-v':
|
||||
raise Exception('-ttyout: Unimplemented')
|
||||
# if ndx >= len_sys_argv:
|
||||
# usage("'-v' option needs a following address filename")
|
||||
# sys.exit(10)
|
||||
# filename = sys.argv[ndx]
|
||||
# ndx += 1
|
||||
# view_mem(filename)
|
||||
elif opt == '-w':
|
||||
if ndx >= len_sys_argv:
|
||||
usage("'-v' option needs a following 'on' or 'off'")
|
||||
sys.exit(10)
|
||||
state = sys.argv[ndx]
|
||||
ndx += 1
|
||||
if state == 'on':
|
||||
state = False
|
||||
elif state == 'off':
|
||||
state = True
|
||||
else:
|
||||
usage("'-v' option needs a following 'on' or 'off'")
|
||||
sys.exit(10)
|
||||
self.memory.set_ROM_writable(state)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@ -158,6 +158,11 @@ class MainCPU(object):
|
||||
|
||||
self.running = False
|
||||
|
||||
def set_dataswitches(self, value):
|
||||
"""Set given value into the data switches."""
|
||||
|
||||
self.DS = value
|
||||
|
||||
def BLOCKADDR(self, address):
|
||||
"""Get address WITHIN THE BLOCK."""
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
Emulate the Imlac Memory.
|
||||
@ -111,10 +111,15 @@ class Memory(object):
|
||||
self.clear_core()
|
||||
|
||||
self.set_ROM(self.boot_rom)
|
||||
|
||||
|
||||
def set_corefile(self, corefile):
|
||||
"""Set name of the core save/restore file."""
|
||||
|
||||
self.corefile = corefile
|
||||
|
||||
def clear_core(self):
|
||||
"""Clears memory to all zeros.
|
||||
|
||||
|
||||
If using ROM, that is unchanged.
|
||||
"""
|
||||
|
||||
@ -186,6 +191,11 @@ class Memory(object):
|
||||
else:
|
||||
self.rom_protected = save_flag
|
||||
|
||||
def set_ROM_writable(self, writable):
|
||||
"""Set ROM write protection state."""
|
||||
|
||||
self.rom_protected = writable
|
||||
|
||||
def fetch(self, address, indirect):
|
||||
"""Get a value from a memory address.
|
||||
|
||||
@ -222,19 +232,19 @@ class Memory(object):
|
||||
|
||||
return address
|
||||
|
||||
def str_trace(self, msg=None):
|
||||
"""Get a traceback string."""
|
||||
def str_trace(self, msg=None):
|
||||
"""Get a traceback string."""
|
||||
|
||||
import traceback
|
||||
|
||||
result = []
|
||||
|
||||
if msg:
|
||||
result.append(msg+'\n')
|
||||
result = []
|
||||
|
||||
result.extend(traceback.format_stack())
|
||||
if msg:
|
||||
result.append(msg+'\n')
|
||||
|
||||
return ''.join(result)
|
||||
result.extend(traceback.format_stack())
|
||||
|
||||
return ''.join(result)
|
||||
|
||||
def put(self, value, address, indirect):
|
||||
"""Put a value into a memory address.
|
||||
@ -260,13 +270,13 @@ class Memory(object):
|
||||
def dump(self, low, high):
|
||||
"""Dump memory in range [low, high] inclusive to a file."""
|
||||
|
||||
with open('x.dump', 'wb') as fd:
|
||||
with open('x.dump', 'wb') as fd:
|
||||
for i in range(high - low + 1):
|
||||
val = self.fetch(low + i, False)
|
||||
high = val >> 8
|
||||
low = val & 0xff
|
||||
data = struct.pack('B', high)
|
||||
fd.write(data)
|
||||
data = struct.pack('B', low)
|
||||
fd.write(data)
|
||||
high = val >> 8
|
||||
low = val & 0xff
|
||||
data = struct.pack('B', high)
|
||||
fd.write(data)
|
||||
data = struct.pack('B', low)
|
||||
fd.write(data)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user