As might be gleaned by examining other documents available on this site, I have somewhat of an affinity for the absurd. In terms of outrageous applications for computers, I peaked early in my career, with the program you find below. This program, written in the fall of 1971, was a full-fledged operating system which transformed a UNIVAC 1108 mainframe computer worth millions of dollars into…a code practice oscillator! When idle, it played synthesised music generated by an algorithm invented by Harry Pyle and used in a little digital gizmo he built.
This program was written more than 20 years before audio hardware became a commonplace component of computers. Nonetheless, the UNIVAC 1108 maintenance panel included an “Audio” button. This was an obscure feature intended to be used in conjunction with the program alarm for debugging operating systems. It caused the program alarm, sounded by the ALRM instruction, to be gated by the guard mode bit in the processor state register. This resulted in an audible signal on the console when the operating system was in control, for example, because the system had gone idle.
A little experimentation (no button must go unpushed, after all) revealed that even if the program alarm was not sounding, an artifact of the circuit caused the program alarm speaker on the operator's console to emit a little “tick” every time the processor went into or out of guard mode. And that, of course, was a glitch big enough to permit generation of arbitrary tones, given a suitable program to flip guard mode on and off at the proper rate.
Since user programs always ran with guard mode on, that meant the tone generation trick had to be done by code in the operating system, or by a stand-alone operating system booted from tape. I chose the latter, in large part to compound the absurdity of the whole thing; very few UNIVAC programmers ever wrote stand-alone operating systems—they wrote user programs that ran under the operating system or modified components of the operating system itself, but few ever looked at the boot code in the system or contemplated writing their own. Shortly before I wrote this program, I had been involved in the development of the boot code for CHI/OS, an operating system being developed from scratch at Chi Corporation in Cleveland, so I was sufficiently familiar with low-level boot code to pull this off.
Despite its ridiculous intent, this was a fully general 1108 operating system which could be booted by any processor of a multiprocessor system, and into any memory module. On a multiprocessor system with a console on each processor, one could even boot multiple copies into different memory banks and enjoy stereo or three-channel sound. To my knowledge, nobody ever tried this. The 1108 console had a set of “selective jump” switches on the operator's console which could be tested by programs, so I used them to allow the user to set the speed at which the Morse code was transmitted. The code even adapted automatically when run on an 1106, which it detected by timing instruction speed against the hardware real-time clock. Unhappily, the successors of the 1108 and 1106 lacked the “Audio” feature, so the Morse code exec passed into history when the machines for which it was written retired from service.
The program was written so that, when executed in user mode, it wrote its own boot tape. Gosh it was cool, late at night, to type on the console and know that several million dollars of hardware were exclusively dedicated to making Morse code come out of the speaker. Folks familiar with user-mode 1100 programming will notice some oddities resulting from the fact that this code runs in the executive set of registers, which have different memory-mapped addresses when referenced in instructions. If you're hazy on the 1100 instruction set after all these years, check out the instruction set reference.
.
. M O R S E / S Y N T H E S I Z E R E X E C
.
. OCTOBER 1971 BY JOHN WALKER
.
. YOU BOOT THE TAPE THIS THING WRITES INTO ANY MODULE THROUGH
. ANY CHANNEL OR IOC PATH OF ANY PROCESSOR. BEFORE PUSHING START, PUSH
. THE 'AUDIO' BUTTON ON THE MAINTENANCE PANEL (IT'S RIGHT BENEATH THE
. PHASE LIGHTS, AND TO THE LEFT OF 'INITIAL LOAD'.) AFTER STARTING UP
. THE PROCESSOR, BEHOLD THE MARVELOUS SOUND THAT ISSUES FROM THE ALARM
. SPEAKER ON THE OPERATOR'S CONSOLE.
.
. AFTER YOU TIRE OF THAT, TRY TYPING ON THE CONSOLE. YOUR
. INPUT WILL BE SENT BACK TO YOU IN MORSE CODE. THE SPEED OF THE MORSE
. CODE IS SET BY THE BINARY VALUE IN THE JUMP KEYS 1-10 (1 IS THE LOW
. ORDER BIT). THE JUMP KEYS ARE READ WHENEVER THE RETURN KEY IS HIT.
. YOU MAY GET AS FAR AHEAD OF THE OUTPUT AS YOU LIKE. SETTING JUMP KEY
. 15 WILL CAUSE INPUT TO BE QUEUED, AND TO BE SENT AFTER JUMP KEY 15 IS
. TURNED OFF. IF YOU TYPE NOTHING FOR 10 SECONDS AFTER OUTPUT CEASES,
. THE SYNTHESIZER WILL START AGAIN. TYPING A HAPPY-PUNCH (¤) WILL
. IMMEDIATELY ABORT THE MORSE CODE AND START THE SYNTHESIZER.
/.
AXR$
R0 EQU 0100
.
. 'HOW IT SOUNDS' CONSTANTS
.
RATE EQU ; HOW FAST TO SHIFT SYNTHESIZER REGISTER
100000
BSLENG EQU ; BASIC TIME INTERVAL FOR MORSE CODE
3
SYNPTC EQU ; SYNTHESIZER BASIC HIGH NOTE PITCH
250
MPITCH EQU ; COUNT FOR MORSE PITCH
1000
.
. REGISTER DEFINITIONS
.
XR EQU 0140 EXEC REGISTERS BIAS
.
. . X2 *INTERRUPT REGISTER*
. . X3 *INTERRUPT REGISTER*
.
. . A6 *INTERRUPT REGISTER*
SIX EQU A7 1 IF RUNNING ON AN 1106
LENGTH EQU A8 LENGTH OF BASIC MORSE INTERVAL
. . A9 TIMEOUT COUNTER FOR MORSE MODE
BITS EQU A10 FEEDBACK SHIFT REGISTER BITS REGISTER
PITCH EQU A11 PITCH COUNT FOR TONE SUBROUTINE
. . A12 CONSOLE ACCESS WORD (ABSOLUTE)
. . A13 ONE FOR MORSE MODE, ZERO FOR SYNTHESIZER
. . A14 GUARD MODE OFF PSR
. . A15 GUARD MODE ON PSR
.
CIP EQU R5 CONSOLE LOAD POINTER
CDP EQU R6 CONSOLE DEPOSIT POINTER
.
DOT EQU '.'--' '
DASH EQU '-'--' '
.
P PROC 0,1
UNGUARD* NAME 0
GUARD* NAME 1
DO P(0,0)=0 , ER 0
DO P(0,0)=1 , LMJ X2,TON
END
.
P PROC 1
ORG* NAME 0
DO +(P(1,1)-$)<0 ,A0 EQU 08+(GOO )+3*RETID+(LA 100)
RES P(1,1)-$
END
.
P PROC 0
TAG* NAME 0
* EQU $-O1000
END
.
P PROC 1,1
SCN* NAME 0
I$ 072,014,,,,P(1,1)
END
.
S* FUNC .
END $-O1000+S(1)
.
JKL* PROC 1,2
JK JKL(1,1),S(2) REMOVE THIS BIT ?
ANA,U A6,1*/(JKL(1,1)-1) YES
END
.
M* PROC *2
M$$1 NAME 0
DO +(M(2,1)**0770000000000)<0 , GO M$$2
M(2,1) EQU +(M(2,1)*/6)+' '
GO M$$1
M$$2* NAME 0
CH$*(M(1,1)) EQU M(2,1)
END
.
P PROC 0,1
GEN* NAME 0
DO CH$(I-1)=0 ,CH$(I-1) EQU ' '
+ CH$(I-1)--' '
END
.
$(1) LIT
O1000 .
/.
BOOT TAG
SCN A0+XR SAVE BOOT CHANNEL
LA A0,040,A0 GET ACCESS WORD FROM BOOT
SSL A0,15 SHIFT ALL BUT MODULE NUMBER OFF
LSSL A0,15 GET BACK BOOT MODULE ADDRESS
LX,U X1,,A0 MOVE IBANK BIAS TO X1
LSSL A0,9 SHIFT IBANK BIAS TO BI
LXM,U A0,0177000 SET 65K BS
LPS A0+XR LOAD UP NEW PSR
NOP 5 WAIT FOR IT
LA A14,A0+XR GET GUARD MODE OFF PSR
AU A14,K1S29 ADD GUARD MODE BIT
LA,U A13 CLEAR MORSE MODE
LA A0,LMJINS GET TRAP INSTRUCTION
SA A0,ISILOC PUT IN ISI INTERRUPT LOCATION
LA A12,K1CDW LOAD CONSOLE ACCESS WORD
AA A12,X1+XR ADD BASING
LR CDP,K1CDBUF LOAD CONSOLE DATA POINTER
SR CDP,CIP INITIALIZE LOAD POINTER
LA A0,K3BIG GET COUNT FOR SPEED DETERMINATION
LA,U SIX CLEAR 1106 FLAG
LR,U R0,2500 GET RTC COUNT FOR TIMING
JGD A0,S(0) WAIT FOR A WHILE
LA A0,R0 GET RTC COUNT
LSSL A0,18 GET SIGN BIT AS FLAG
JP A0,S(2) 1108 CAN FINISH IN ALLOTTED TIME
LA,U SIX,1 1106 CANNOT.
LICM 15,A12 BRING UP CONSOLE INPUT
LA,U BITS,1 INITIALIZE BITS NON-ZERO
LA LENGTH,ISNRAT GET INITIAL SENDING RATE
TZ SIX IS IT AN 1106 ?
SSL LENGTH,1 YES. FUDGE SENDING RATE
AAIJ IDLE ENTER IDLE LOOP
.
NSI TAG
LPS X0 RELOAD PSR AT INTERRUPT TIME
AAIJ 0,X2 RETURN TO CODE
.
TOFF TAG
LPS A14 RELOAD PSR WITH GUARD MODE OFF
AAIJ 0,X2 RETURN
.
TON TAG
LPS A15 LOAD GUARD MODE ON PSR
AAIJ 0,X2 RETURN
.
TONE TAG
GUARD . TURN ON GUARD MODE
LR R4,PITCH GET PITCH CODE
JGD R4,S(0) WAIT WITH IT ON
UNGUARD . TURN OFF GUARD MODE
LR R4,PITCH RELOAD PITCH CODE
JGD R4,S(0) WAIT WITH IT OFF
JGD A0,TONE DO FOR TONAL DURATION
J 0,X11 RETURN TO CALL
.
ISIIM TAG
SCN A6+XR GET INTERRUPTING CHANNEL NUMBER
TE,U A6,15 IS IT THE CONSOLE ?
PAIJ NSI NO. IGNORE THE INTERRUPT
LPS A14 LOAD GUARD MODE OFF PSR
NOP 8 ALLOW IT TO COME INTO EFFECT
LA A6,CDW GET CONSOLE POINTER
LX X3,CDP GET CONSOLE POINTER
TZ A13 ARE WE IN MORSE MODE ?
J QCI YES. QUEUE INPUT
LA,U A13,1 NO. SET MORSE MODE
SA A6,,*X3 PUT CHARACTER IN BUFFER
SX X3,CDP UPDATE POINTER
LICM 15,A12 BRING CONSOLE BACK UP
AAIJ MORSE ENTER MORSE ROUTINE
.
QCI TAG
TNE,U A6,'¤' IS IT ABORT CHARACTER ?
PAIJ ABORT YES. TERMINATE MORSE OUTPUT
TE,U A6,'#' IS IT NEW LINE ?
TNE,U A6,'^' IS IT A CARRIAGE RETURN ?
PAIJ COMPUTE YES. COMPUTE OUTPUT SPEED
SA A6,,*X3 PUT CHARACTER IN BUFFER
SX X3,CDP UPDATE POINTER
RTN TAG
LICM 15,A12 BRING INPUT BACK UP
LX X2,X2+XR GET ADDRESS OF INTERRUPT
PAIJ NSI RETURN TO INTERRUPTED INSTRUCTION
.
ABORT TAG
LA A0,K1CDBUF RELOAD BUFFER POINTER
SA A0,CDP PUT BACK IN BUFFER
SA A0,CIP RESET LOAD POINTER ALSO
LA,U A13 CLEAR MORSE MODE
LICM 15,A12 RESET INPUT ACCESS WORD
AAIJ IDLE ENTER IDLE SYNTHESIZER
.
. RECOMPUTE SPEED
.
COMPUTE TAG
LA,U A6,1*/10-1 GET ONE BITS
I DO 10 , JKL I
MSI,U A6,BSLENG MULTIPLY BY BASE LENGTH
TZ SIX IS IT AN 1106 ?
SSL A6,1 YES. FUDGE LENGTH
SA A6,LENGTH SAVE LENGTH
PAIJ RTN BRING BACK INPUT, RETURN TO PROCESSING
.
. FEEDBACK SHIFT REGISTER MUSIC SYNTHESIZER
.
IDLE TAG
LA A0,K1SEC ONE SECOND DELAY
JGD A0,S(0) WAIT A WHILE
IDLOOP TAG
LA A0,BITS GET CURRENT BITS
AND,U A0,1*/4 AND OFF FIFTH BIT
LA A2,A1 SAVE ANDED VALUE
JNB A0,S(2) SHIFTING OUT A BIT ?
XOR,U A1,1*/4 YES. FLIP FIFTH BIT
SSL A0,1 SHIFT RIGHT ONE PLACE
JZ A2,S(2) ONE BIT TO SHIFT IN ?
AA,U A0,1*/8 YES. SHIFT IN THE BIT
SA A0,BITS UPDATE BITS
DSL A0,36+5 ISOLATE HIGH BITS
LA,U A0,SYNPTC LOAD PITCH BASE FOR SYNTHESISER
AA,U A1,1 INSURE BITS NOT ZERO
MSI A0,A1 COMPUTE FINAL PITCH
TZ SIX IS IT AN 1106 ?
SSL A0,1 YES. USE HALF THE COUNT
SA A0,PITCH SAVE PITCH
LA,U A1,RATE GET BASE RATE COUNT
LA,U A0 CLEAR A0
DI A0,PITCH COMPUTE NUMBER OF TIMES THRU LOOP
TZ SIX ARE WE ON AN 1106 ?
SSL A0,1 YES. HALVE THE COUNT
LMJ X11,TONE GENERATE TONE
J IDLOOP COMPUTE NEXT TONE
.
. INTERRUPT LOCATIONS
.
ORG 0217 DAY CLOCK INTERRUPT
LMJ X2,NSI IGNORE IT
ORG 0220 ISI INPUT MONITOR
ISILOC TAG
NOP 4 SET TO IGNORE BOOT INTERRUPT
ORG 0231 REAL TIME INTERRUPT
LMJ X2,NSI IGNORE IT
ORG 0242 ER INTERRUPT
LMJ X2,TOFF TURN OFF GUARD MODE
.
ORG 0300
.
. MORSE CODE GENERATOR
.
MORSE TAG
LA A0,K1SEC ONE SECOND DELAY
JGD A0,S(0) MAKE TRANSITION GENTLE
LA,U A9 CLEAR TIMEOUT COUNTER
LA,U A2,MPITCH GET PITCH CODE FOR MORSE
TZ SIX IS IT AN 1106 ?
SSL A2,1 YES. CUT LOOP COUNT IN HALF
SA A2,PITCH SET UP PITCH
MLOOP TAG
JK 14,S(0) JK 14: QUEUE INPUT
PAIJ S(1) PREVENT INTERRUPTS
LA A0,CIP GET INPUT POINTER
TNE A0,CDP IS IT EQUAL TO THE DEPOSIT POINTER ?
AAIJ EMPTY YES. NOTHING TO OUTPUT
AAIJ S(1) ALLOW INTERRUPTS
LA A1,,*A0 GET THE NEXT CHARACTER TO OUTPUT
LA,U A9 CLEAR 'CHARACTER GET FAILED'
SA A0,CIP RESTORE INPUT POINTER
TNE,U A1,' ' IS IT A SPACE ?
J EWORD YES. GENERATE END-OF-WORD PAUSE
LA A1,MTABL,A1 CONVERT FIELDATA TO MORSE CODE
MOLOOP TAG
LA,U A0 CLEAR 'DOT OR DASH'
LDSL A0,6 SHIFT NEXT CODE INTO A0
JZ A0,ELETTR GO TO END OF LETTER PROCESSING
TE,U A0,DOT IS IT A DOT ?
J DASHP NO. MUST BE A DASH
LA A0,LENGTH GET MORSE TONE LENGTH
LMJ X11,TONE SOUND OFF
EBIT TAG
LA A0,LENGTH GET LENGTH CODE
MSI,U A0,MPITCH*2 COMPUTE EQUIVALENT LOOP COUNT FOR
JGD A0,S(0) ALLOW ONE TIME INTERVAL TO PASS
J MOLOOP PROCESS NEXT DOT OR DASH
.
DASHP TAG
LA A0,LENGTH GET DOT LENGTH
MSI,U A0,3 DASH IS THREE TIMES LONGER
LMJ X11,TONE SEND THAT TONE
J EBIT JOIN PROCESSING FOR END-OF-BIT
.
ELETTR TAG
LA A0,LENGTH GET BASIC LENGTH
MSI,U A0,3*MPITCH*2 GET DASH'S WORTH OF SILENCE
JGD A0,S(0) ALLOW THAT MUCH TIME TO PASS
J MLOOP GET NEXT CHARACTER
.
EWORD TAG
LA A0,LENGTH GET BASIC LENGTH
MSI,U A0,6*MPITCH*2 GET TWO DASHES OF LENGTH
JGD A0,S(0) TWO DASHES BETWEEN WORDS
J MLOOP PROCESS NEXT CHARACTER
.
EMPTY TAG
AA,U A9,1 BUMP NUMBER OF FAILURES
TLE A9,K5BIG IDLE TOO LONG ?
J MLOOP NO. TRY AGAIN
JK 14,S(2) IGNORE TIMEOUT ?
PAIJ ABORT NO. GO BACK TO SYNTHESIZER
LA,U A9 CLEAR TIMEOUT
J MLOOP ENTER MORSE MODE.
.
. MORSE TABLE INITIALIZATION
.
. REF. ARRL HANDBOOK, 1950 EDITION
.
M 'A' '.-'
M 'B' '-...'
M 'C' '-.-.'
M 'D' '-..'
M 'E' '.'
M 'F' '..-.'
M 'G' '--.'
M 'H' '....'
M 'I' '..'
M 'J' '.---'
M 'K' '-.-'
M 'L' '.-..'
M 'M' '--'
M 'N' '-.'
M 'O' '---'
M 'P' '.--.'
M 'Q' '--.-'
M 'R' '.-.'
M 'S' '...'
M 'T' '-'
M 'U' '..-'
M 'V' '...-'
M 'W' '.--'
M 'X' '-..-'
M 'Y' '-.--'
M 'Z' '--..'
M '1' '.----'
M '2' '..---'
M '3' '...--'
M '4' '....-'
M '5' '.....'
M '6' '-....'
M '7' '--...'
M '8' '---..'
M '9' '----.'
M '0' '-----'
M '.' '.-.-.-'
M ',' '--..--'
M '?' '..--..'
M '/' '-..-.'
M ';' '-.-.-.'
M 072 '.----.'
M ':' '---...'
.
. FIELDATA TO MORSE CODE CONVERSION TABLE
.
MTABL TAG
I DO 64 , GEN
.
CDBUF EQU 02000 START OF CONSOLE DATA
CDW TAG
* 0 DATA WORD FOR CONSOLE INPUT
K1S29 TAG
* 1*/29
LMJINS TAG
LMJ X2,ISIIM
K1CDW TAG
* 1,CDW
K1CDBUF TAG
* 1,CDBUF
K3BIG TAG
* 300000
ISNRAT TAG
* BSLENG*(1*/3)
K1SEC TAG
* 1250000
K5BIG TAG
* 500000
ORG 01777 INSURE ENOUGH CORE ALLOCATED
$(2).
WRITE LA,U A0,WRPKT LOAD PACKET ADDRESS
ER IOW$
ER EXIT$
WRPKT 'BOOTFILE'
* 0
F FORM 6,6,24
F 0,W$,0
* 02000,O1000
END WRITE
UNIVAC has been, over the years, a registered trademark of Eckert-Mauchly Computer Corporation, Remington Rand Corporation, Sperry Rand Corporation, Sperry Corporation, and Unisys Corporation.