PDP7-Unix/cmd/cat.s

" cat: cat arg1 [arg2 ...]

   " Load the pointer pointer in 017777 to see if we have any arguments
   lac 017777 i
   sad d4			" Skip if we have more than four argument words
     jmp nofiles		" Only four argument words, so no arguments
   lac 017777			" Move five words past the argument word count
   tad d1			" so that AC points at the first argument
   tad d4			" and save the pointer in name
   dac name

loop:
   sys open; name:0; 0		" Open file, get fd back
   spa
     jmp badfile		" Negative fd, exit with an error message
   dac fi			" Save file descriptor in fi

1:
   jms getc			" Get a character in AC
   sad o4
     jmp 1f			" Break the loop when we get a ctrl-D
   jms putc			" Write the character on stdout
   jmp 1b			" and loop back

1:
   lac fi			" Close the file descriptor in fi
   sys close

loop1:
   -4
   tad 017777 i			" Subtract 4 from the count of argument words
   dac 017777 i
   sad d4			" Is the value 4, i.e. no args left?
     jmp done			" Yes, so exit

   lac name			" Still an argument, so move up
   tad d4			" to the next filename argument
   dac name
   jmp loop			" and loop back to cat this file

badfile:
   lac name			" Get the pointer to the filename
   dac 1f			" Store it in 1f below
   lac d1			" Load fd 1 which is stdout
   sys write; 1:0; 4		" Write the four words of the filename
   lac d1
   sys write; 1f; 2		" and then write " ?\n"
   jmp loop1			" Now try doing the next argument

1: 040;077012			" String literal: " ?\n"

nofiles:
   lac d1
   sys write; 1f; 5		" Write "No files\n" to stderr
   sys exit			" and exit

1: <no>; 040;  <fi>;<le>;<s 012

done:
   lac noc			" Is the number of characters left zero?
   sna
     sys exit			" Yes, exit
   and d1
   sna cla
     jmp 1f
   jms putc			" Store the character in the buffer
   jmp done			" and loop back
1:
   lac noc			" Get the number of characters in the buffer
   rcr				" Divide by two to convert to words
   dac 1f			" Save in the write's word count below
   lac fo			" Load fd 1, stdout
   sys write; iopt+1; 1:..	" Write the leftover buffer and exit
   sys exit

getc: 0
   lac ipt			" Load the pointer to the next word in the buffer
   sad eipt
     jmp 1f			" We've reached the end of the buffer, so read more
   dac 2f			" Save the pointer
   add o400000			" Flip the msb and save into ipt
   dac ipt
   ral				" Move the msb into the link register
   lac 2f i			" Load the word from the buffer
   szl				" Skip if this is the second character in the word
     lrss 9			" It's the first char, shift down the top character
   and o177			" Keep the lowest 7 bits
   sna
     jmp getc+1			" Skip a NUL characters and read another one
   jmp getc i			" Return the character from the subroutine
1:
   lac fi			" Buffer is empty, read another 64 characters
   sys read; iipt+1; 64
   sna
     jmp 1f			" No characters were read in
   tad iipt			" Add the word count to the base of the buffer
   dac eipt			" and store in the end buffer pointer
   lac iipt			" Reset the ipt to the base of the buffer
   dac ipt
   jmp getc+1			" and loop back to get one character
1:
   lac o4			" No character, return with ctrl-D
   jmp getc i			" return from subroutine

putc: 0
   and o177			" Keep the lowest 7 bits and save into 2f+1
   dac 2f+1
   lac opt			" Save the pointer to the empty buffer
   dac 2f			" position to 2f
   add o400000			" Flip the msb and save back into opt
   dac opt			" This also has the effect of incrementing
				" the opt pointer every second addition!

   spa				" If the bit was set, we already have one
     jmp 1f			" character at 2f+1. If no previous character,
   lac 2f i			" merge the old and new character together
   xor 2f+1
   jmp 3f			" and go to the "save it in buffer" code
1:
   lac 2f+1			" Move the character up into the top half
   alss 9
3:
   dac 2f i			" Save the word into the buffer
   isz noc			" Add 1 to the char count, never skipping
   lac noc			" Have we reached 128 characters, 64 words?
   sad d128
     skp
   jmp putc i			" No, so return (more room still in the buffer)
   lac fo			" Load fd1 (i.e stdout)
   sys write; iopt+1; 64	" and write out the 64 words in the buffer
   lac iopt
   dac opt			" Set opt pointing back to base of buffer
   dzm noc			" Set the number of chars in the buffer to 0
   jmp putc i			" and return

2: 0;0				" Current input and output word pointers
ipt: 0				" Current input buffer base
eipt: 0				" Pointer to end of data read in input buffer
iipt: .+1; .=.+64		" 64 word input buffer and pointer to it
fi: 0				" Input file descriptor
opt: .+2			" Current output buffer base
iopt: .+1; .=.+64		" 64 word output buffer and pointer to it
noc: 0				" Number of output characters
fo: 1				" Output file descriptor, fd 1 is stdout

d1: 1				" Octal and decimal constants
o4:d4: 4
d8: 8
o400000: 0400000		" Msb toggle bit
o177: 0177			" ASCII mask
d128: 128			" 128 words in the output buffer