#!/usr/bin/python
# -*- coding: utf-8 -*-
########################################################################
# teco-pi-demo - Starts the simulator with the OS/8, sends one of the
# famous TECO "calculate pi" program to it, and starts it running at
# a very slow rate of speed to act as a blinkenlights demo.
#
# Copyright © 2017 by Warren Young.
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
# OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Except as contained in this notice, the names of the authors above
# shall not be used in advertising or otherwise to promote the sale,
# use or other dealings in this Software without prior written
# authorization from those authors.
########################################################################
# Bring in just the basics so we can bring in our local modules
import os
import sys
sys.path.insert (0, os.path.dirname (__file__) + '/../lib')
sys.path.insert (0, os.getcwd () + '/lib')
# Other core modules we need
from datetime import datetime
import time
# Our local modules
from pidp8i import *
from simh import *
#### main ##############################################################
def main ():
# Check for command line flags
benchmark = len (sys.argv) > 1 and sys.argv[1] == '-b'
# Create the SIMH child instance and tell it where to send log output
try:
s = simh (dirs.build)
except (RuntimeError) as e:
print "Could not start simulator: " + e.message + '!'
exit (1)
s.set_logfile (os.fdopen (sys.stdout.fileno (), 'w', 0))
# Find and boot the built OS/8 bin disk
rk = os.path.join (dirs.os8mo, 'os8v3d-bin.rk05')
if not os.path.isfile (rk):
print "Could not find " + rk + "; OS/8 media not yet built?"
exit (1)
print "Booting " + rk + "..."
s.send_cmd ("att rk0 " + rk)
s.send_cmd ("boot rk0")
# Start TECO8 in the simulator under OS/8
s.os8_send_cmd ('\\.', "R TECO")
# The macro comes from http://www.iwriteiam.nl/HaPi_TECO_macro.html
#
# The 248 preceding "UN" in the first line of the macro is the number
# of digits of pi to calculate. That value was reached by experiment
# as the largest value that runs without crashing TECO with a
#
# ?MEM STORAGE CAPACITY EXCEEDED
#
# error. You can see that by increasing the value below, commenting
# out the throttle setting below, and running the demo. On a Pi 3, it
# should take a bit over an hour to complete, if it doesn't error out.
#
# With the simulator throttled, generating 248 digits takes 17 years!
#
# That is based on generating 1 digit every ~16 seconds on a Pi 3 when
# running unthrottled, roughly 8 MIPS. When throttled to 59 IPS — or
# 17ms per instruction, as below — you multiply the seconds by the
# factor 8 MIPS / 59 IPS = ~136000, giving about 2.2 million seconds
# per digit. Multiplying that by 248 gives ~17 years.
macro = [
'GZ0J\UNQN"E 248UN \' BUH BUV HK',
'QN< J BUQ QN*10/3UI',
'QI< \+2*10+(QQ*QI)UA B L K QI*2-1UJ QA/QJUQ',
'QA-(QQ*QJ)-2\ 10@I// -1%I >',
'QQ/10UT QH+QT+48UW QW-58"E 48UW %V \' QV"N QV^T \' QWUV QQ-(QT*10)UH >',
'QV^T @^A/',
'/HKEX',
]
# First and last lines are handled specially, so slice them off.
first = macro.pop (0)
last = macro.pop ()
# Send the first line of the macro; implicitly awaits 1st TECO prompt
s.os8_send_cmd ('\*', first)
# Blindly send core lines of the macro; TECO gives no prompts for 'em.
for line in macro:
s.os8_send_line (line)
# Send last line of macro sans CR, followed by two Esc characters to
# start it running.
s.os8_send_str (last) # not os8_send_line!
s.os8_send_ctrl ('[')
s.os8_send_ctrl ('[')
if benchmark:
# Run demo long enough to get a good sense of the simulator's
# execution rate while unthrottled on this host hardware. If
# you don't run it long enough, the IPS value is untrustworthy.
try:
s.spin (10)
except pexpect.TIMEOUT:
# Find out how many IPS was executing
s.os8_send_ctrl ('e')
s.send_cmd ('show clocks')
line = s.read_tail ('Execution Rate:')
curr_ips = int (line.strip().replace(',', '').split(' ')[0])
pf = open ('lib/pidp8i/ips.py', 'a')
pf.write ('current = ' + str (curr_ips) + ' # ' + \
str (datetime.today ()) + '\n')
pf.close ()
s.send_cmd ('quit')
pdp_ratio = float (curr_ips) / ips.pdp8i
rpi_ratio = float (curr_ips) / ips.raspberry_pi_b_plus
print "\nYour system is " + format (rpi_ratio, '.1f') + \
" times faster than a Raspberry Pi Model B+"
print "or " + format (pdp_ratio, '.1f') + \
" times faster than a PDP-8/I.\n"
else:
# Normal mode. Pop out to SIMH and throttle it down to a rate
# suitable for a blinkenlights demo. 1/17 means SIMH runs one
# instruction then waits for 17ms, yielding ~59 IPS.
time.sleep (0.02) # FIXME: simulator chokes on 'cont' without this
s.os8_send_ctrl ('e')
s.send_cmd ('set throttle 1/17')
# You can't hit Ctrl-E while running this script in the foreground
# since pexpect takes over stdio. Therefore, if you want to be able
# to send commands to the simulator while the demo is running,
# uncomment the line below, which will let you send commands to the
# simulator via telnet. From another terminal or SSH session:
#
# $ telnet localhost 3141
#
# or from a remote machine:
#
# $ telnet 192.168.1.2 3141
#
# It's disabled by default because SIMH can't be made to listen only
# on localhost, so doing this may be a security risk. SIMH disables
# obviously-unsafe commands like ! on the remote console, but it is
# possible some mischief may be possible via this path anyway. It
# could be used to exfiltrate a sensitive file via ATTACH, for one
# thing. For another, it's a potential DoS vector.
#s.send_cmd ('set remote telnet=3141')
# Let it run. Never exits.
s.send_cmd ('cont')
s.spin ()
if __name__ == "__main__":
main()