From cee4e0ad16f6161c587dc576b57e736f88a385e3 Mon Sep 17 00:00:00 2001 From: Ross Wilson Date: Sat, 31 Oct 2015 16:42:37 +0700 Subject: [PATCH] Combined Ptr.py and Ptp.py --- pymlac/PtrPtp.py | 235 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 pymlac/PtrPtp.py diff --git a/pymlac/PtrPtp.py b/pymlac/PtrPtp.py new file mode 100644 index 0000000..749f6ec --- /dev/null +++ b/pymlac/PtrPtp.py @@ -0,0 +1,235 @@ +#!/usr/bin/python + +""" +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 * + + +class PtrPtp(object): + + # number of chars per second we want + PtrCharsPerSecond = 300 + PtpCharsPerSecond = 30 + + # 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 = 0377 + + # 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.""" + + # general device + self.device_use = None + self.device_motor_on = False + self.device_open_file = None + self.device_filename = None + 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") + + self.device_use = self.InUsePTR + self.device_motor_on = False + self.device_filename = fname + self.device_open_file = open(self.device_filename, 'rb') + 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") + + self.device_use = None + self.device_motor_on = False + self.device_ready = False + if self.device_filename: + self.device_open_file.close() + self.device_open_file = None + self.device_filename = None + self.ptr_at_eof = True + self.ptr_value = self.PtrEOF + + 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.ptp_filename = fname + self.device_open_file = open(self.ptp_filename, 'wb') + 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") + + self.device_motor_on = False + self.device_ready = False + if self.ptp_filename: + self.device_open_file.close() + self.device_open_file = None + self.ptp_filename = None + self.ptp_at_eof = True + self.ptp_value = self.PtrEOF + + 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(struct.pack('1B', 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.PtrNotReadyCycles +