5.7 printf (2340)

The procedure “printf” provides a direct, unsophisticated low-level, unbuffered way for the operating system to send messages to the system console terminal. It is used during initialisation and to report hardware errors or the imminent collapse of the system.

(These versions of “printf” and “putchar” run in kernel mode and are similar to, but not the same as, the versions invoked by a “C” program which runs in user mode. The latter versions of “printf” and “putchar” live in the library “/lib/libc.a”. You may still find it useful to read the sections “PRINTF(III)” and “PUTCHAR(III)” of the UPM at this point.)

2340:

The programmer must have been carried away when he declared all the parameters for this procedure. In fact the procedure body only contains references to “xl” and “fmt”.

This serves to reveal one of the facts of “C” programming. The rules for matching parameters in procedure calls and procedure declarations are not enforced, not even with respect to the numbers of parameters.

Parameters are placed on the stack in reverse order. Thus when “printf” is called “fmt” will be nearer to the “top of stack” than “xl”, etc.

        |   .   |
        ---------
        |   .   |
        ---------
        |   .   |        stack grows down
        ---------
        |   .   |
        ---------
        |  x2   |
        ---------
        |  xl   |
        ---------
        |  fmt  |
        ---------
        |   .   |        top of stack
        ---------

“xl” has a higher address then “fmt” but a lower address then “x2”, because stacks grow downwards on the PDP11.

2341:

“fmt” may be interpreted as a constant character pointer. This declaration is (almost) equivalent to

“char *fmt;”

The difference is that here the value of “fmt” cannot be changed;

2346:

“adx” is set to point to “xl”. The expression “&xl” is the address of “xl”. Note that since “xl” is a stack location, this expression cannot be evaluated at compile time.

(Many of the expressions you will find elsewhere involving the addresses of variables or arrays are effective because they can be evaluated at compile or load time.);

2348:

Extract into the register “c” successive characters from the format string;

2349:

If “c” is not a ‘%’ then ...

2350:

If “c” is a null character (‘\0’), this indicates the end of the format string in the normal way, and “printf” terminates;

2351:

Otherwise call “putchar” to send the character to the system console terminal;

2353:

A ‘%’ character has been seen. Get the next character (it had better not be the ‘\0’!);

2354:

If this character is a ‘d’ or ‘l’ or ‘o’, call “printn” passing as parameters the value referenced by “adx” and either the value “8” or “10” depending on whether “c” is ‘o’ or not. (The ‘d’ and ‘l’ codes are clearly equivalent.)

“printn” expresses the binary numbers as a set of digit characters according to the radix supplied as the second parameter;

2356:

If the editing character is ‘s’, then all but the last character of a null terminated string is to be sent to the terminal. “adx” should point to a character pointer in this case;

2361:

Increment “adx” to point to the next word in the stack i.e. to the next parameter passed to “printf”;

2362:

Go back to line 2347 and continue scanning the format string. Enthusiasts for structured programming will prefer to replace lines 2347 and this by “while (1) {” and “}” respectively .