Not an answer, as Jean-François Fabre has already deducted all workings, but some hints about the syntax/workings of AS to understand the source.
This source is meant to be assembled using the Unix assember AS. AS is an extreme primitive assembler designed only to handle machine specific parts of Unix. It carries only the most essential functions and those even in a rather ... well, lets be positive and say 'unusual' and special to type way. Some of the features are encountered in above source:
Identifiers and Assignment
To start with, there are identifiers and assignment to them. According to the manual an identifier is:
An identifier consists of a sequence of alphanumeric characters (including period "." underscore "_", and tilde "~" as alphanumeric) of which the first may not be numeric. Only the first eight characters are significant. When a name begins with a tilde, the tilde is discarded and that occurrence of the identifier generates a unique entry in the symbol table which can match no other occurrence of the identifier. This feature is used by the C compiler to place names of local variables in the output symbol table without having to worry about making them unique.
The important implication here is that a period can not only occur at any place but as well at the first without creating any meaning - that is as long as it doesn't equals any legal pseudo-operation (like .global) Thus the definition
.write = 4.
simply creates a symbol that evaluates to the decimal value of 4 - which happens to be the sys call number for write() :)
Local Labels
AS doesn't have local labels, but uses a system of numeric labels that can be redefined. From the manual:
A numeric label consists of a digit 0 to 9 followed by a colon ( : ). Such a label serves to define temporary symbols of the form "nb" and "nf", where n is the digit of the label. As in the case of name labels, a numeric label assigns the current value and type of "." to the temporary symbol. However, several numeric labels with the same digit may be used within the same assembly. References of the form "nf" refer to the first numeric label "n:" :F:orward from the reference; "nb" symbols refer to the first "n:" label :B:ackward from the reference. This sort of temporary label was introduced by Knuth [The Art of Computer Programming, Vol I: Fundamental Algorithms]. Such labels tend to conserve both the symbol table space of the assembler and the inventive powers of the programmer.
So the lines
mov 6(r5),0f
mov 8(r5),0f+2
simply refer to the first later instance of the numeric label of '0' - in the second line with an offset of two.
Statements
The second issue one may stumble a bit is the line
sys .write; 0:..; ..
Here it's helpful to keep in mind how AS defines statements:
A source program is composed of a sequence of statements. Statements are separated either by new-lines or by semicolons.
So to the assembler this isn't a single statement but multiple, and this already happens before:
sys 0; 9f
So the assembler sees:
sys 0;
9f
The first statement is a sys instruction - which is the system trap instruction used by Unix for kernel calls. While being a two byte instruction, the second byte is of free use, any value can be put here, including zero, as seen. Sys call zero is handled by the kernel as indirect call, so it takes to next word and uses it as address to look for the 'real' call.
So the second statement simply placed the address the label holds as word into memory, enabling the indirection. This is made to avoid self modifying code and allow reentrant programming. The second sys call will not be executed, but simply pared as if it would have been made like encoded.
The sys call placed in the modifiable data segment (.data
) is just a data structure formed like a sys call.
sys .write
0:..
..
So again the first statement assembles as sys, this time using the symbol value (see above) of .write
. Write requires the file handle in R0 and a parameter list with two words holding buffer address and length after it.
The next statement defines the local label, referenced in the prior instruction as 0f
. A label can be defined here as for the assembler it's a new statement. Any number of whitespaces between the semicolon (;
) terminating the previous statement and the label itself can be inserted as labels have to have a terminating colon (:
), identifying them. The colon does as well make any following whitespaces optional, thus allowing the 'instruction' to be simply continued.
..
is a special symbol representing the relocation counter (see below), using it alone will reserve space for an address sized data item - much like that 9f
before. On runtime this will be overwritten with whatever is passed at 6(r5)
.
The third statement now repeats the reservation of an address sized word, now to be filled with the value at 8(R5)
.
(We see, this isn't programmed very portable as the size of an address word is hard coded within the move instruction, as two, instead of using a second label. Not really cool, is it?)
Relocation Counter
The relocation counter, accessed as ..
is essentially the offset of a segment within memory. by default zero. Like with the location counter (.
) it can be assigned, which may be used for very specific address layouts. In this case it's only important that it is handled as an address and will produced as a statement an address sized word. Perfect to reserve space for address parameters, isn't it?
bec 1f
then a1
label. Wouldn't0f
be the0
label?sys .write; 0:..; ..
; 0:
is the same as having a label at the start of a line.