Site Update
That concludes the series of articles on CTSS, at least for now. I will next be looking at the Dartmouth Time-sharing System - expect posts later in June.
That concludes the series of articles on CTSS, at least for now. I will next be looking at the Dartmouth Time-sharing System - expect posts later in June.
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.
A session using QED to move text to a new file.. Source: Rupert Lane. License: CC0.
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.
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 quoted by /
/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.
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/).
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.
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.
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
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
f will replace the contents of the current buffer with
information such as date, time, login details etc.k will sort a rangel will list a file to the consoleu - the audit command - will create a buffer showing the effect of
all edits done since the last read.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.
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.
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.
A version of the above produced with TYPSET/MEMO". Source: Rupert Lane. License: CC0.
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 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 |
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.
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.
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.
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.
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.
A chat session on CTSS using "dot". Source: Rupert Lane. License: CC0.
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
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
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).
"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.
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
I welcome any questions or comments, and also especially any corrections if I have got something wrong. Please email me at rupert@timereshared.com
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.
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.
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
logincommand 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.
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.
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.
I welcome any questions or comments, and also especially any corrections if I have got something wrong. Please email me at rupert@timereshared.com