mirror of
https://github.com/rzzzwilson/pymlac.git
synced 2025-06-10 09:32:41 +00:00
227 lines
6.5 KiB
Python
Executable File
227 lines
6.5 KiB
Python
Executable File
"""
|
|
Emulate the imlac Paper Tape Reader/Punch (PTR/PTP).
|
|
|
|
We take some pains to closely emulate the *real* device, even
|
|
including misuse, such as no tape mounted. This means we must tell
|
|
the PTR/PTP object how many CPU cycles have passed (tick()).
|
|
"""
|
|
|
|
import struct
|
|
|
|
from Globals import *
|
|
import log
|
|
log = log.Log('test.log', log.Log.DEBUG)
|
|
|
|
|
|
class PtrPtp(object):
|
|
|
|
# number of chars per second we want
|
|
PtrCharsPerSecond = 300
|
|
PtpCharsPerSecond = 300
|
|
|
|
# duty cycle for PTR is 30% ready and 70% not ready
|
|
PtrReadyCycles = int((CYCLES_PER_SECOND / PtrCharsPerSecond) / 0.7) / 25
|
|
PtrNotReadyCycles = int((CYCLES_PER_SECOND / PtrCharsPerSecond) / 0.3) / 25
|
|
PtpNotReadyCycles = int(CYCLES_PER_SECOND / PtpCharsPerSecond)
|
|
|
|
# no tape in reader, return 0377 (all holes see light)
|
|
PtrEOF = 0xff
|
|
|
|
# module-level state variables
|
|
device_use = None
|
|
device_motor_on = False
|
|
device_open_file = None
|
|
device_filename = None
|
|
device_cycle_count = 0
|
|
device_ready = False
|
|
|
|
ptr_at_eof = True
|
|
ptr_value = PtrEOF
|
|
|
|
# status values
|
|
InUsePTR = 'PTR'
|
|
InUsePTP = 'PTP'
|
|
|
|
|
|
def __init__(self):
|
|
"""Initialize the papertape device."""
|
|
|
|
self.reset()
|
|
|
|
def reset(self):
|
|
"""Reset device to a known state."""
|
|
|
|
self.device_use = None
|
|
self.device_motor_on = False
|
|
self.device_open_file = None
|
|
self.device_filename = None
|
|
self.device_ready = False
|
|
|
|
# reader-specific
|
|
self.ptr_at_eof = True
|
|
self.ptr_value = self.PtrEOF
|
|
|
|
def ready(self):
|
|
"""Test if device is ready."""
|
|
|
|
return self.device_ready
|
|
|
|
###############
|
|
# Interface routines for reader
|
|
###############
|
|
|
|
def ptr_mount(self, fname):
|
|
"""Mount papertape file on the reader device."""
|
|
|
|
if self.device_use == self.InUsePTP:
|
|
raise RuntimeError("ptr_mount: Can't mount PTR file, being used as PTP")
|
|
|
|
if self.device_open_file:
|
|
self.device_open_file.close()
|
|
|
|
self.device_use = self.InUsePTR
|
|
self.device_motor_on = False
|
|
self.device_filename = fname
|
|
self.device_open_file = open(self.device_filename, 'rb')
|
|
log('Opening PTR file: %s' % fname)
|
|
self.device_ready = False
|
|
self.device_cycle_count = self.PtrNotReadyCycles
|
|
self.ptr_at_eof = False
|
|
self.ptr_value = None
|
|
|
|
def ptr_dismount(self):
|
|
"""Dismount a papertape file."""
|
|
|
|
if self.device_use == self.InUsePTP:
|
|
raise RuntimeError("ptr_dismount: Can't dismount PTR file, being used as PTP")
|
|
|
|
if self.device_open_file:
|
|
self.device_open_file.close()
|
|
|
|
self.reset()
|
|
|
|
def start(self):
|
|
"""Turn papertape reader motor on."""
|
|
|
|
if self.device_use == self.InUsePTP:
|
|
raise RuntimeError("start: Can't start PTR motor, being used as PTP")
|
|
|
|
self.device_use = self.InUsePTR
|
|
self.device_motor_on = True
|
|
self.device_ready = False
|
|
self.device_cycle_count = self.PtrReadyCycles
|
|
|
|
def stop(self):
|
|
"""Stop reader motor."""
|
|
|
|
if self.device_use == self.InUsePTP:
|
|
raise RuntimeError("stop: Can't stop PTR motor, being used as PTP")
|
|
|
|
self.device_motor_on = False
|
|
self.device_ready = False
|
|
self.device_cycle_count = self.PtrReadyCycles
|
|
|
|
def read(self):
|
|
"""Read papertape value."""
|
|
|
|
if self.device_use == self.InUsePTP:
|
|
raise RuntimeError("ptr_read: Can't read PTR, being used as PTP")
|
|
|
|
return self.ptr_value
|
|
|
|
def ptr_eof(self):
|
|
"""Return reader EOF status."""
|
|
|
|
if self.device_use == self.InUsePTP:
|
|
raise RuntimeError("ptr_eof: Can't read PTR status, being used as PTP")
|
|
|
|
return self.ptr_at_eof
|
|
|
|
def ptr_tick(self, cycles):
|
|
"""Called to push PTR state along.
|
|
|
|
cycles number of cycles passed since last tick
|
|
"""
|
|
|
|
if self.device_use != self.InUsePTR:
|
|
return
|
|
|
|
# if end of tape or motor off, do nothing, state remains unchanged
|
|
if self.ptr_at_eof or not self.device_motor_on:
|
|
return
|
|
|
|
self.device_cycle_count -= cycles
|
|
|
|
if self.device_cycle_count <= 0:
|
|
if self.device_ready:
|
|
self.device_ready = False
|
|
self.device_cycle_count += self.PtrNotReadyCycles
|
|
else:
|
|
self.device_ready = True
|
|
self.device_cycle_count += self.PtrReadyCycles
|
|
self.ptr_value = self.device_open_file.read(1)
|
|
if len(self.ptr_value) < 1:
|
|
# EOF on input file, pretend end of tape
|
|
self.ptr_at_eof = True
|
|
self.ptr_value = self.PtrEOF
|
|
else:
|
|
self.ptr_value = ord(self.ptr_value)
|
|
|
|
###############
|
|
# Interface routines for punch
|
|
###############
|
|
|
|
def ptp_mount(self, fname):
|
|
"""Mount papertape file on the punch device."""
|
|
|
|
# check usage of the device
|
|
if self.device_use == self.InUsePTR:
|
|
raise RuntimeError("ptp_mount: Can't mount PTP file, being used as PTR")
|
|
|
|
self.device_use = self.InUsePTP
|
|
self.device_motor_on = False
|
|
self.device_ready = False
|
|
self.device_cycle_count = self.PtrNotReadyCycles
|
|
self.device_filename = fname
|
|
self.device_open_file = open(self.device_filename, 'w')
|
|
self.ptp_at_eof = False
|
|
self.ptp_value = None
|
|
|
|
def ptp_dismount(self):
|
|
"""Dismount a papertape punch file."""
|
|
|
|
if self.device_use == self.InUsePTR:
|
|
raise RuntimeError("ptp_dismount: Can't dismount PTP file, being used as PTR")
|
|
|
|
if self.device_filename:
|
|
self.device_open_file.close()
|
|
|
|
self.reset()
|
|
|
|
def punch(self, value):
|
|
"""Write byte to papertape file.
|
|
|
|
value 8 bit value to punch
|
|
"""
|
|
|
|
if self.device_use == self.InUsePTR:
|
|
raise RuntimeError("punch: Can't punch PTP file, being used as PTR")
|
|
|
|
self.device_ready = False
|
|
self.device_cycle_count = self.PtpNotReadyCycles
|
|
self.device_open_file.write(chr(value))
|
|
|
|
def ptp_tick(self, cycles):
|
|
"""Called to push PTP state along.
|
|
|
|
cycles number of cycles passed since last tick
|
|
"""
|
|
|
|
if self.device_use != self.InUsePTP:
|
|
return
|
|
|
|
self.device_cycle_count -= cycles
|
|
if self.device_cycle_count <= 0:
|
|
self.device_ready = True
|
|
self.device_cycle_count += self.PtpNotReadyCycles
|