The Unix Assembly Reference Manual by Dennis M. Ritchie, at §8.1 notes that:
The syntax of the address forms is identical to that in DEC assemblers, except that ‘‘*’’ has been substituted
for ‘‘@’’ and ‘‘$’’ for ‘‘#’’; the UNIX typing conventions make ‘‘@’’ and ‘‘#’’ rather inconvenient.
In a SO answer, and in this comment, fuz notes that this is because #
and @
are used as control
characters by the early TTY drivers in Unix. This is indeed so, with @
taking
the kill-line function, and #
erases the last character¹.
This can be seen in the Unix History Repository at u7.s:
jsr r0,cesc; 100 / test for @ (kill line)
br canon / character was @ so start over
jsr r0,cesc; 43 / test for # (erase last char. typed)
br 1b / character was #, go back
(100 is the octal code for @, and 43 the octal code for #).
One would expect Unix would have adopted other characters for erase-kill
processing, since the chosen ones clash with characters used by the assembler.
But recall that Unix originated in a PDP-7. Erase-kill processing with #
and @
was already in PDP-7 Unix, Dennis M. Ritchie in The Evolution of the Unix
Time-sharing System notes that:
Only a few programs (notably the shell and the editor) bothered to implement erase-kill processing.
which I think correspond to this code in ed1.s:
esc: 0
sna
jmp i esc
jms putsc; tal
sad o12
jmp 2f
sad o100
jmp 1f
sad o43
skp
jmp i esc
-1
tad tal
dac tal
and o17777
sad linpm1
jmp 1f
jmp i esc
1:
lac linep
dac tal
jmp i esc
2:
lac tal
sma cla
jmp 1f
jms putsc; tal
and init.s:
rline: 0
law ibuf-1
dac 8
1:
cla
sys read; char; 1
lac char
lrss 9
sad o100
jmp rline+1
sad o43
jmp 2f
dac 8 i
sad o12
jmp rline i
jmp 1b
2:
law ibuf-1
sad 8
jmp 1b
-1
tad 8
dac 8
jmp 1b
Mark Plotnick notes in a comment that Unix inherited the erase and kill characters from Multics. Following that lead, we can arrive at "Remote Terminal Character Stream Processing in Multics" (1970), which explains that:
It should be possible to determine from the printed page,
without ambiguity, both what went into the computer pro-
gram and what the program tried to print out.
which is the rationale for using printing characters for erase and kill (recall that back then users typed at teletypes, the typewriter-like ones).
It also mentions a special editor on the 7094 Compatible Time-Sharing System as a prior implementation of the things talked about in the paper, which may or may not refer also to erase and kill. Anyway, that special editor is probably TYPSET (of the TYPSET/RUNOFF workflow²), and it indeed used #
and @
as defaults for erase and kill characters, as mentioned in TYPSET and RUNOFF, Memorandum editor and type-out commands (1964). While Project MAC Technical Report MAC-TR-16 mentions erase and kill processing in CTSS (figure in page 23, first paragraph in page 24), that report is from a later date, and doesn't mention which characters were used.
A little more digging leads us to the CTSS Programmer's Guide, which in its 1963 edition uses "
and ?
for delete-character and delete-message while the 1966 edition already gives #
and @
. "Introduction to CTSS Usage" (July 1967) gives both "
and #
for deleting the previous character, and ?
and @
for deleting everything typed since the previous carriage return. So TYPSET may well be the first program to use #
and @
, from where it spread to CTSS, Multics, and eventually Unix.
¹ Incidentally, in early Unix as, as documented in man11 from the 1st edition of the Unix Programmer's Manual, there is another
substitution:
Character changes are:
for use
@ *
# $
; /
So, comments are marked by a slash.
² A nice history of RUNOFF/roff/... can be found at https://manpages.bsd.lv/history.html