CTSS: The QED editor

The QED editor has a long history. It was originally created for the Berkeley Timesharing System running on the SDD 930/940 machine in 1965-6. Ken Thompson, who was part of the Bell Labs contingent working on Multics, re-implemented it for CTSS in around 3500 lines of FAP assembly code some time in 1969; he also produced a version for Multics using BCPL. While working on Unix, the ideas from QED inspired tools such as ed and sed.

Other authors produced implementations of QED for other time-sharing systems, and you can even find versions for Unix you can run on current systems.

/images/ctss/qed-session.png

A session using QED to move text to a new file.. Source: Rupert Lane. License: CC0.

Setup

Note that QED only supports 6- and 12-bit line-marked files and ASCII files, not card image files. If your file is in card image format, convert it using SQUASH first.

An editing session

QED has the concept of multiple buffers rather than working on a single file at a time. In the below I start QED and read in TPK MAD to the default buffer using the r command with the s option to mean 6-bit line-marked.

qed
W 1755.3
QED     
rs tpk mad

x will list the buffers in use: here we have the default buffer 0 with 23 lines:

x
"0" 0023

The p command prints lines from the buffer. Like many commands, it takes an optional range as its first argument. Without a range it defaults to . which is the current line, but I can specify line numbers or ranges of lines:

p                        
           END OF PROGRAM
10 p                     
           DIMENSION NUM(11)
3,5 p                       
           INTERNAL FUNCTION(X)
           ENTRY TO F.         
           FUNCTION RETURN SQRT.(.ABS.X) + 5 * X.P.3

1,$ p would print all lines of the file.

Range arguments can also be regular expressions enclosed in ~/~s.

/th.* input/,/read/ p            
           THROUGH INPUT,FOR J=0,1,J.GE.N
INPUT      READ FORMAT FMT,NUM(J)

Had we used the d command instead of p, it would delete those three lines.

The global command

As well as using regular expressions in ranges, we can use them as an argument to the global command g. As an example, to find all lines that match the string 11

gp/11/
           N = 11
           DIMENSION NUM(11)
           PRINT COMMENT $PLEASE ENTER 11 NUMBERS$

Here, the range is not specified before the command, so it is implicitly the whole file. The g command takes two parameters: the command to execute (p) and the regexp to max (/11/).

Search and replace

The s command takes two regexps as parameters for the text to find and the text to replace. So if I wanted to change all 11s to 15s I could do:

1,$ s/11/15/

The range, 1,$ needs to be supplied as otherwise this command will only operate on the current line.

Inserting text

Position yourself on the line after you want the new text to go and then use the i command. Terminate this with the escape character followed by f, ie \f

/other/                  
           OTHERWISE
i                   
           PRINT COMMENT $TRY AGAIN$
\f

a will append text directly to the end of the buffer.

Moving text

Let's say we want to move the internal function to an separate file, and make it an external function. We can use the m command with a range to determine what lines to move. Here this will move the text to buffer 1:

/internal func/,/end of func/ m1

We can use b to switch to the other buffer and look at it. We also change INTERNAL to EXTERNAL.

b1                       
1,$p
           INTERNAL FUNCTION(X)
           ENTRY TO F.         
           FUNCTION RETURN SQRT.(.ABS.X) + 5 * X.P.3
           END OF FUNCTION                          
/internal/
           INTERNAL FUNCTION(X)
s/int/ext/                     
p
           EXTERNAL FUNCTION(X)

Let's save it as func mad.

ws func mad

We then return to buffer 0 and confirm that it has gone by seeing the ?1 error message when searching for sqrt.

b0                        
/sqrt/
?1

Save the main file.

ws tpk mad

Execute system commands

QED can execute commands stored in a buffer and then return to the editor. Let's set up some compile/load commands in buffer 2:

b2                       
i
mad tpk
\f

Return to buffer 0 and execute it.

b0
e2
LENGTH 00152.  TV SIZE 00006.  ENTRY 00040

Other QED commands

  • f will replace the contents of the current buffer with information such as date, time, login details etc.
  • k will sort a range
  • l will list a file to the console
  • u - the audit command - will create a buffer showing the effect of all edits done since the last read.

Further information

QED is described more fully in the CTSS Programmer's Guide section AH.3.09 p353.

Source code for QED on a number of different systems is collected on Github at arnoldrobbins/qed-archive.

Questions, corrections, comments

I welcome any questions or comments, and also especially any corrections if I have got something wrong. Please email me at rupert@timereshared.com and I will add it here and update the main text if needed.


CTSS: Document production with TYPSET and RUNOFF

The concept of using computers to produce documents was first demonstrated before CTSS in 1960 with the Colossal Typewriter project on the PDP-1. Rather than typing directly to paper, you could store text and edit it in memory before printing to a typewriter or line printer. However, in single user mode, it was not a cost-efficient use of a computer, as alluded to by a similar PDP-1 project Expensive Typewriter.

On an interactive multi-user system it did make more sense, as the processing requirements for this is quite small. CTSS had an early system called DITTO/ROFF, and this was replaced by TYPSET/RUNOFF by Jerry Saltzer in around November 1964, which we'll look at today.

/images/ctss/typset-runoff-demo-lc.png

A version of the above produced with TYPSET/MEMO". Source: Rupert Lane. License: CC0.

TYPSET

TYPSET is an editor where you can add and change text, including control codes to specify output formatting. This uses the same editor commands as EDC and EDL (in fact, as described in an earlier article, the design for these editors came from TYPSET; see that article for how to use the editor).

TYPSET expects the name2 of a file to be (MEMO), so the command TYPSET abc will create or edit a file ABC (MEMO). It uses 12-bit line-marked files, so with a suitable typewriter you could enter lower case and an extended range of symbols. If you are using a recent version of s709/ctss-kit or my quickstart or eliza-ctss released after 22 May 2025 this will support it as well.

Control words

Control words always start with a . (dot) in column 1. There are full and abbreviated forms, eg .center, which will center the next line, can be abbreviated as .ce.

A full list of control words:

Abbr Command Meaning
.ll .line length n Set line length to n characters
.in .indent n Set indent of each following line to be n characters
.un .undent n Reduce indentation by n characters
.pl .paper length n Set lines per page, default 66
.ss .single space This is the default line spacing
.ds .double space
.bp .begin page Start a new page
.ad .adjust Justify text by adding spaces (the default)
.nj .nojust Turns off the above
.fl .fill Justify JJ text by moving words to fill (the default)
.nf .nofill Turns off the above
.pa .page (n) Turns on page number printing, optionally starting at n
.sp .space (n) Inserts n blank lines. Default for n is 1.
.he .header xxxx Sets page header to be xxxx
.br .break Prevents filling of text before and after this word
.ce .center Centers the following line
.li .literal Indicates the following line is not a control word
.hm .heading mode P P can be CENTER, MARGIN, FACING, OPPOSED
.op .odd page Next page number will skip ahead to next odd number
.pm .paging mode P Page mumbler mode, eg MARGIN, CENTER
.ap .append A Include text from file A (MEMO) here

Text styles

As the output for this system was a typewriter, there was obviously no concept of bold, italics, different fonts, text size etc. One feature the typewriter did have that we don't have today was backspacing. For example, to get something like Ç you could enter C, then backspace, then ,. The system will move the head back one character on backspace so the two symbols print in the same space.

RUNOFF

The RUNOFF command takes the input memo file, interprets the control words and prints the formatted output to the typewriter console. It prompts the user LOAD PAPER, HIT RETURN so the paper can be changed if needed.

As an example, the screenshot above was produced by this file:

.ll 80
.ds
.header TIMERESHARED.COM
.hm center
.bp
.center
CTSS: Document production with TYPSET and RUNOFF

The concept of using computers to produce documents was first
demonstrated before CTSS in 1960 with the Colossal Typewriter project
on the PDP-1. Rather than typing directly to paper, you could store
text and edit it in memory before printing to a typewriter or line
printer. However, in single user mode, it was not a cost-efficient use
of a computer, as alluded to by a similar PDP-1 project Expensive
Typewriter.

Further information

TYPSET/RUNOFF are described more fully in the CTSS Programmer's Guide section AH.9.01 p504; there's an OCR'd version at multicians.org.

These commands was the inspiration for similar text production systems on Multics and Unix. On a Unix system today you are likely to find troff which uses very similar control words.

Questions, corrections, comments

May 2025: Updated to point to the newer versions of the s709 emulator/ctss-kit for full support of lower case letters. Thanks to Dave Pitts for his continued work on this system.

I welcome any questions or comments, and also especially any corrections if I have got something wrong. Please email me at rupert@timereshared.com and I will add it here and update the main text.


CTSS: The "." shell, chat and email

Many people think that command line shells, electronic mail and chat as something fairly modern. But CTSS had them all 60 years ago. In this post we'll look at "." (or "dot"), the interactive shell including a chat facility, along with the MAIL command.

/images/ctss/ctss-chat.png

A chat session on CTSS using "dot". Source: Rupert Lane. License: CC0.

"dot"

The CTSS supervisor contains basic functionality for users to enter commands. During the design of Multics, this concept was developed further to create the idea of a shell, a program intended for humans to enter commands and monitor what is going on. From the Multics design memo:

[…] unlike a calling program, a user is not assigned once and for all a predetermined set of instructions. One does not know what he plans to do next, and he will eventually overlook some yet highly recommendable checking, if the command does not warn him against any possible misunderstanding as to what has been performed.

This idea was brought back to CTSS by Tom Van Vleck and Noel Morris in 1965 with the creation of "dot".

Installation: "dot" is included on simh. On s709, you will need a recent version of the CTSS kit to run this - either version 1.0.10 or later from Dave Pitts' site or a version of my quickstart or eliza-ctss released after 1 May 2025. Reinstall your system, then start CTSS and login as user sysdev password system. Run the runcom mkdot command. This will compile and install "dot" as a system component.

To start it, just type . You will notice the prompt changes to an abbreviated form of the R and W notifications. "dot" will reload itself after you run a command, but in some cases, for example after an error, it will be suspended, so type RSTAT to continue

.

W 1508.2
R       

hello

W
MIT8C0: 2 USERS AT 05/03/25 1509.3, MAX = 30
R                                           
 
xxx

W
 'XXX' NOT FOUND.
 TYPE RSTART TO IGNORE.
R .100+.150            
           
rstart

W 1509.7
R

Multiple commands

With "dot" running, you can type more than one command per line, separating them by commas with whitespace. For example to run hello and listf:

hello , listf * mad

W
MIT8C0: 2 USERS AT 05/03/25 1451.4, MAX = 30
                                            
     3 FILES     5 RECORDS
 NAME1  NAME2 MOD NOREC   USED
 HELLO    MAD 000     1         
BOTTLE    MAD 000     1 04/14/25
  CQA1    MAD 000     3         

R

You can run several commands on a given parameter, so rather than typing MAD HELLO , LOADGO HELLO you can do:

( mad loadgo ) hello

W
LENGTH 00020.  TV SIZE 00003.  ENTRY 00011
EXECUTION.                                
 HELLO WORLD
  EXIT CALLED. PM MAY BE TAKEN.
R .166+.050

You can also run one command on multiple parameters: rather than typing MAD HELLO , MAD BOTTLE you can do:

mad ( hello bottle )

W
LENGTH 00020.  TV SIZE 00003.  ENTRY 00011
LENGTH 00155.  TV SIZE 00003.  ENTRY 00070
R

Abbreviations

Commonly used commands can be abbreviated using DC. Existing abbreviations can be listed with ABBREV COM. So for forgetful Unix users who keep typing ls and cp you could do:

dc ls listf cp move
W
R
 
abbrev com
W
 
    LS   LISTF
    CP    MOVE
              
R
 
ls * mad
W
 
     3 FILES     5 RECORDS
 NAME1  NAME2 MOD NOREC   USED
BOTTLE    MAD 000     1 05/03/25
 HELLO    MAD 000     1         
  CQA1    MAD 000     3 04/14/25
                                

R

Command line parameter can be defined with DP. Abbreviations are stored across sessions in the file USER PROFIL.

There is also a handy built in abbreviation .x for (CFLx) so to list files in common file directory #4 you now just need to type ls .4 instead of LISTF (CFL4).

Several people are typing

"dot" also includes a typewriter-to-typewriter chat facility. Both ends of the chat need to be running "dot". Say you are logged in as guest and want to chat to sysdev who is user M!416 5. Use the write m1416 5 command. On the other end, you will see the messages and can respond immediately. See the image at the top of this post for an example.

Either end can type Control-C to exit the chat session. The ALLOW and FORBID commands control who can chat to whom.

Electronic mail

A separate facility from "dot" was the electronic mail command MAIL. The design and history of this is covered by Tom Van Vleck (one of MAIL's original authors) at multicians.org which is well worth a read.

A quick demo of how it is used. Say I am logged on as SYSDEV and I want to send an email to GUEST. I put my message in a file, here HELLO TXT, and send it using MAIL.

p hello txt    
W 1924.2
        
HELLO GUEST, WELCOME TO CTSS.
                             
R .000+.033
           
mail hello txt m1416 guest
W 1925.1
R .016+.016

When GUEST logs in, they will see

YOU HAVE     MAIL   BOX

and they can view the message by printing the file.

p mail box
W 1926.8
        
 FROM M1416 6 IN M1416 SYSDEV AT 04/27 1925.1
HELLO GUEST, WELCOME TO CTSS.                
                             

R .016+.033

The source code for the MAIL program is in com5/ in the CTSS kit. As mentioned in the article, the program is simple (only 250 lines of MAD code) and contains a hard coded list of users who can email * *, ie all users:

    INTERNAL FUNCTION(X,Y)
    ENTRY TO USRCHK.
   R
    WHENEVER X.NE.$ M1416$ .OR. (Y.NE.$   385$ .AND. Y
   1  .NE. $  4301$ .AND. Y .NE. $  2962$ .AND. Y .NE.
   2  $  3845$)
   3  , ERROR RETURN

Questions, corrections, comments

I welcome any questions or comments, and also especially any corrections if I have got something wrong. Please email me at rupert@timereshared.com


CTSS: Security

Did the concept of computer security exist before CTSS? In the world of batch processing, access to computer resources was done by submitting a deck of punched cards or a magnetic tape to the computer operations staff who would supervise its execution, so only physical access control was needed. There was also no concept of persistent, private data residing in the system - any such data would be physical, on paper or tape, to be held by the user or computer centre offline.

CTSS introduced the idea of multiple users accessing the system at the same time, so now there was the problem of how to prevent one program interfering with another. Access to the system was from outside the computer room, via typewriters wired to the computer or even remotely via modem, so there needed to be some way to control this. Finally, CTSS had a hard disk where files could be stored and control was needed over who could access them.

File access was covered in CTSS Files & Directories; in this post we will look at resource protection and login control.

Resource protection

In a multi-user system a programmer should no longer have direct access to all hardware resources; access needs to be mediated through the operating system.

As several programs can be loaded in core memory at any time, CTSS prevents direct access to memory that is not part of your program. For example, take this assembly language program.

        CLA     0
        TSX     CHNCOM,4
 CHNCOM TIA     =HCHNCOM
        END

The important line here is the first one: CLA addr will clear the accumulator and load it with the contents of memory at address addr. The remaining two lines will cause the program to exit by calling the CHNCOM system routine.

Compiling and executing this program works fine. But if I change the address to 7000 it will give a protection error.

p test fap
W 954.3
       
	CLA	7000
	TSX	CHNCOM,4
CHNCOM	TIA	=HCHNCOM
	END             
           

R .016+.016
           
fap test
W 954.3
     LENGTH     4
R .016+.000      
           
loadgo test
W 954.4
EXECUTION.
 PROTECTION MODE VIOLATION AT 05360.
 INS.=050000015530, RI.=000000000000, PI.=005364000000
R .000+.016

Internally this is handled by the supervisor setting two protection registers representing the upper and lower bounds of memory areas the program can reach, along with a relocation register to map logical to physical address.

There is also protection against trying to change these registers, or trying to access I/O devices directly.

Login control

Early versions of CTSS had a login command to identify which user wanted to start a session: the user would provide their problem and programmer number but no password was required. The CTSS Programmer's Guide from 1963 states:

In the future an additional parameter may be required in order to afford greater security for the user. This will probably be in the form of a private code given separately; explicit instructions will be given by the login command if necessary.

Passwords were probably added around May 1964 according to Time Sharing System Notes #4. When the user entered the login command, the computer would prompt for the password and turn off the typewriter's print head so the user could enter it without it being displayed. If it matched, the user was logged in.

Note the login command prints different error messages depending on whether the user name or password was incorrect, which does mean it is possible to determine whether a user name is valid.

login nouser  
W 1025.2
Password
NOUSER NOT FOUND IN DIRECTORY
 LOGIN COMMAND INCORRECT     
READY.                  
      
login guest
W 1025.4
Password
 PASSWORD NOT FOUND IN DIRECTORY
 LOGIN COMMAND INCORRECT        
READY.

User information is stored in the file UACCNT TIMACC in common files #2. Passwords are stored in plain text. It is possible to view the file using the emulated system; in the real system this likely would have had file protection set so non-system users could not open it. Here we can see that user guest's password is system.

comfil 2
W 1050.9
R .000+.000
           
p UACCNT TIMACC
W 1050.9
...        
GUEST	 5000001000000GUEST 000001000001000000SYSTEM
000000004000000000000360000360000360000360000360    
...

Further control was provided by specifying a group of lines the user could login from, and giving each user a quota of time to access the system, divided into shifts (for example, shift 1 was Monday to Friday, 8am to 6pm).

Contents of these files were set by operations staff; it is not possible for the user to change their password online.

The structure of the user accounting files is described in the 1969 Programmer's Guide section AD.5.

Access control hacks

The IEEE put together The CTSS Fiftieth Anniversary Commemorative Overview which contains many interesting stories about CTSS. There is one entry by Allan Scherr who was about to lose access to system files and CPU quota, but wanted to find a way to keep running his program.

Late one Friday night, I submitted a request to print the password files and very early Saturday morning went to the file cabinet where printouts were placed and took the listing out of the M1416 folder. I could then continue my larceny of machine time.

As the passwords were in plain text, the printout of the file was all that was needed to get access to other accounts.

Denial of service

Another story in the IEEE overview recounted by Tom Van Vleck recounts how Bill Mathews noticed an error where the password file had been mixed up with the message of the day file so all users could see passwords for other users when they logged in. Bill wanted to limit the damage by crashing the machine: the way he did this was to enter the debugger and issue the assembly instruction XEC *. XEC means read the word at the given address and execute it; * means the current address, so this is effectively an infinite loop.

We can recreate this on the emulated system (using FAP):

edc splat fap
W 1836.2
 FILE  SPLAT   FAP NOT FOUND.
Input:                       
	XEC	*
	END

Edit:
file 

R .016+.016
           
fap splat
W 1836.4
     LENGTH     1
R .016+.033      
           
load splat
W 1836.9
R .000+.016
           
start
W 1836.9
EXECUTION.

Now the program (and all other user sessions) do not respond to further input and even the s709 emulator needs to be force killed to recover from this.

Questions, corrections, comments

I welcome any questions or comments, and also especially any corrections if I have got something wrong. Please email me at rupert@timereshared.com


CTSS: Assembly Language

I am not planning to go into too much detail on assembly language in this blog, but I will take a quick look at some of the features of the IBM 7x architecture that are different from what we are used to today, and the assembler available on CTSS.

/images/ctss/ctss-slip-assembly.png

Part of the assembly language listing of the SLIP library used in ELIZA. Source: Github jeffshrager/elizagen.org

The FAP assembler

The Fortran Assembly Program was used on batch operating systems to assemble FORTRAN compiler output and user assembly code. As mentioned in the article on early software, this was based on work one by individual 7x sites and eventually adopted by IBM as part of IBSYS and FMS. This code was ported to CTSS to run interactively and used for most of the low level operating system code.

Although the opcodes and operands are hardware specific, the format of code as shown above would be familiar to machine code programmers today, with labels on the left followed by instructions in column 8, and then optional comments.

To assemble name1 fap on CTSS, use the command FAP name1. It takes the optional argument (LIST) if you want to produce a listing, and (SYMB) to produce a symbol table for debugging.

The instruction set architecture

1s complement

The 7x uses 1s complement for quantities and arithmetic, so that the left most bit of a word is 0 for positive and 1 for negative. This has some interesting consequences such as there existing a +0 which is different from -0.

Word addressing

Access to memory is via words; there is no concept of byte or sub-word addressing.

Not many general purpose registers

There is really only the accumulator as a general purpose register. There is a multiplier/quotient register that is used when multiplying two words together where the extra space is needed. But most operations involve bringing data in from memory and applying it to the accumulator.

Index registers

/images/ctss/7094-sample-instruction.png

Arrangement of fields in a sample instruction. Source: IBM 7094 Principle of Operations at bitsavers.org

To aid with constructs like looping, there are 3 index registers on the 7090 and 7 on the 7094. These are selected by means of a 3 bit tag field in many instructions (shown as T above): on the 7090 you can select multiple index registers at once, on the 7094 you can do multiple selection for index registers 1-3 or select any single register from 1-7 depending on the mode of the machine.

Index registers are effectively 15 bits wide, as 2¹⁵ = 32k is the maximum memory space.

The contents of the selected index register(s) are subtracted from the address specified rather than added. This leads to tables of values being naturally stored in descending order of memory location, for example Fortran II does this for arrays. It is however possible to get the contents of the index registers to be added by storing a 2s complement value in a index register.

Indirect addressing

Setting the Flag field (F in the above) indicates indirect addressing. The CPU goes to the address indicated in the instruction, loads the value there and then treats that as an address to load the final value from.

Calling convention

There is no stack register on this CPU, so a different method is needed for calling subroutines. For a single parameter/return you could use the accumulator, but for more than one the convention used was to place the parameters in the program's memory just after the call instruction, use index registers to access these from the subroutine and then jump back to a fixed location after the callee and parameters. (frobenius.com has a great example of how this works in detail.)

Supervisor calls are done by branching to a subroutine whose first instruction is TIA and contains in the address field the BCD formatted name of the system call. The machine will trap into an exception where the supervisor can recognise it and take action.

Further information

All the below are on bitsavers. The IBM Principles of Operation describes the architecture and opcodes for the 7094. This should be read before the FAP manual on how to run the assembler. James Saxon's 1964 book Programming the IBM 7090 may be an easier way to learn assembly as it is structured as a series of lessons.

Questions, corrections, comments

I welcome any questions or comments, and also especially any corrections if I have got something wrong. Please email me at rupert@timereshared.com


← Previous  Next →