The Morse Code Exec

October 1971

by John Walker


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.


By John Walker