UCR Stdlib Standard Output Routines


UCR Standard Library: Standard Output Routines
14.1 - Interface
14.2 - Generic Interface
14.3 - Putc, PutcStk, PutcTOS, PutcCS
14.3.1 - Calling Conventions and Assertions
14.3.2 - Putc Addressing Modes
14.3.3 - Syntax & Examples
14.4 - Putcr
14.4.1 - Calling Conventions and Assertions
14.4.2 - Putcr Addressing Modes
14.4.3 - Syntax & Examples
14.5 - PutcStdOut
14.5.1 - Calling Conventions
14.5.2 - PutcStdOut Addressing Modes
14.5.3 - Syntax & Examples
14.6 - PutcBIOS
14.6.1 - Calling Conventions and Assertions
14.6.2 - PutcBIOS Addressing Modes
14.6.3 - Syntax & Examples
14.7 - Puts and Print
14.7.1 - Calling Conventions and Assertions
14.7.2 - Puts and Print Addressing Modes
14.7.3 - Syntax & Examples
14.8 - Printf, Printfr, PrintfStk
14.8.1 - Calling Conventions and Assertions
14.8.2 - Syntax & Examples
14.8.3 - Alternate Syntax
14.9 - Printff
14.9.1 - Calling Conventions and Assertions
14.9.2 - Syntax & Examples
14.10 - Puth
14.10.1 - Calling Conventions and Assertions
14.10.2 - Puth Addressing Modes
14.10.3 - Syntax & Examples
14.11 - Putw
14.11.1 - Calling Conventions and Assertions
14.11.2 - PutW Addressing Modes
14.11.3 - Syntax & Examples
14.12 - Puti, PutiSize
14.12.1 - Calling Conventions and Assertions
14.12.2 - Puti and PutiSize Addressing Modes
14.12.3 - Syntax & Examples
14.13 - Putu, PutuSize
14.13.1 - Calling Conventions and Assertions
14.13.2 - Syntax & Examples
14.13.3 - Alternate Syntax
14.14 - Putl, PutlStk, Putl Size, PutlSizeStk
14.14.1 - Calling Conventions and Assertions
14.14.2 - Syntax & Examples
14.14.3 - Alternate Syntax
14.15 - Putul, PutulStk, PutulSize, PutulSizeStk
14.15.1 - Calling Conventions and Assertions
14.15.2 - Syntax & Examples
14.15.3 - Alternate Syntax
14.16 - Putf, Pute
14.16.1 - Calling Conventions and Assertions
14.16.2 - Syntax & Examples
14.17 - GetOutAdrs, SetOutAdrs
14.17.1 - Calling Conventions and Assertions
14.17.2 - Syntax & Examples
14.18 - PushOutAdrs, PopOutAdrs
14.18.1 - Calling Conventions and Assertions
14.18.2 - Syntax & Examples
14.19 - ResetStdOut
14.19.1 - Calling Conventions and Assertions
14.19.2 - Syntax & Examples


UCR Standard Library: Standard Output Routines

The UCR Standard Library (stdlib) contains a large number of routines that support data output to the standard output device as well as other devices (e.g., DOS output and BIOS output).

By default, most of the routines in this package send their output to the stdlib "standard output device"; This is a pseudo-device with an output hook that lets the system redirect output to some other procedure. By changing a far pointer (using some access methods), you can capture all output sent to the standard output device.

Normally, the stdlib standard output device is the DOS standard output device. Therefore, programs you write that send data to the stdlib standard output device can have their output redirected to a file or other device via DOS I/O redirection.


14.1 Interface

To access the routines in the standard output package, your assembly language module must include the file "stdout.a" during assembly. You can accomplish this with either of the following include statements in your assembly code:



	include	stdout.a
or
	include	ucrlib.a

The stdout.a include file exports several symbols. The UCR Standard Library prefaces all "private" names with a dollar sign ("$"). You should not call any routine in this package that begins with this symbol. To avoid name conflicts, you should not define any symbols in your programs that begin with a dollar sign ("$"). Note that future versions of the stdlib (that remain compatible with this release) may change "private" names. To remain compatible with future releases, you must not refer to these "private" names within your programs.

Source code appearing in this chapter is current as of Version Two, Release 40. There may be minor changes between this source code and the current release.


14.2 Generic Interface

Many of the standard library routines use a common generic programmer's interface. Such routines let you pass parameters to them in several different locations. Common examples include in the registers, by value in the code stream (CSi), by reference in the code stream (CS), by value on the top of stack (TOS), and by reference on the top of stack (Stk). Typically, there are separate invocation macros defined for each of these variants, e.g.,



	IsAlNum			;Passed in AL register.

	IsAlNumCS
	dword	chrPtr		;Passed by reference in code stream.

	push	'a'		;Passed by value on the stack.
	IsAlNumTOS

	push	seg chrVar		;Passed by reference on the stack.
	push	offset chrVar
	IsAlNumStk

In addition to the above forms there are two other suffixes generally applied to stdlib routine names: "m" and "x". The "m" suffix stands for "malloc". Routines with the "m" suffix typically generate a string result and malloc storage for the string on the heap, returning a pointer to this string in the ES:DI register pair. The routines with an "x" suffix also process strings. Most stdlib routines preserve the value of the ES:DI registers when processing strings; typically, they leave the ES:DI register pair pointing at the start of the string. The routines with an "x" suffix do not preserve ES:DI, they generally leave ES:DI pointing at the zero byte of the string they processed or generated.

To make it easier to use all these different variants, the standard library typically defines a macro for each routine that lets you specify various operands using stdlib "addressing modes." The allowable addressing modes vary by routines, but they typically take one of the following forms:



	name		;If operand field is blank, use "plain" version.
	name	var	;Generally passes address of var in code stream (CS).
	name	const	;Pushes const onto TOS and uses nameTOS routine.
	name	[wvar]	;Pushes DS followed by value of wVar variable
			; (assumed to be a word) and calls nameSTK
	name	[dVar]	;Pushes dword value of dVar onto stk, calls nameStk.

Since not all of these "addressing modes" are applicable to all instructions, and some instructions allow different sets of operands (including multiple operands), there are lots of special cases. Such cases are noted after the explaination for each particular routine.


14.3 Putc, PutcStk, PutcTOS, PutcCS

Putc, PutcCS, PutcTOS, and PutcStk write a single character to the standard output device. The character is assumed to be a graphic ASCII character. Although you can expect most devices to handle carriage return, line feed, and backspace properly, you cannot make any general assumptions about other control characters or characters whose code is 80h..0FFh.


14.3.1 Calling Conventions and Assertions


14.3.2 Putc Addressing Modes

Putc Addressing Modes
Name Plain CS TOS Stk X CSi
Putc X X X X - -

The putc macro allows the following operands:

Putc Extended Syntax (Single/No Operands)
Name   byteVar Num const [word Var] [dword Var] Character Const 8-bit Register
Putc X X - X X X X


14.3.3 Syntax & Examples



                mov     al, 'A'
                putc
                push    word ptr CharVar
                PutcTOS
                PutcCS
                dword   CharVar
                pshadrs CharVar
                PutcStk

                putc    'A'
                putc    CharVar
                putc    BH
                putc    CL
                putc    [wvar]
                putc    [dvar]
; putc 'A'
                push    'A'
                putcTOS
                
; putc charvar
                putcCS
                dword   charvar
                
; putc BH
                xchg    bl, bh
                push    bx
                xchg    bl, bh
                putcTOS
                
; putc CL
                push    cx
                putcTOS
                
; putc [wvar]
                push    wvar
                putcStk
                
; putc [dvar]
                push    [dvar]
                putcStk


14.4 Putcr

Putcr sends the new line sequence (carriage return followed by line feed) to the standard output device.


14.4.1 Calling Conventions and Assertions


14.4.2 Putcr Addressing Modes

Putc Addressing Modes
Name Plain CS TOS Stk X CSi
Putc X - - - - -

The putcr macro allows the following operands:

Putcr Extended Syntax (Single/No Operands)
Name   byteVar Num const [word Var] [dword Var] String Const 8-bit Register
Putcr X X - - - - -


14.4.3 Syntax & Examples

Putcr does not accept any operands.



	putcr		;Print a newline to the standard output device.


14.5 PutcStdOut

The PutcStdOut routine sends the character in the AL register to the DOS standard output device. There is a subtle difference between the stdlib "Standard Output Device" and the DOS "Standard Output Device." The stdlib Putc routine (that writes a character to the stdlib Standard Output Device) jumps indirectly through a pointer than you can modify. Normally, this pointer contains the address of the PutcStdOut routine; therefore, Putc normally transfers control directly to the PutcStdOut code. However, you can modify this pointer to bypass the PutcStdOut routine, if you prefer.

The DOS Standard Output Device, on the other hand, is not so easily redirected under program control. It is easy, however, to redirect DOS' standard output via command line parameters, e.g., ">filename>.

If you directly call PutcStdOut, you can bypass the stdlib redirection process. For example, if you've currently redirected the stdlib Standard Output Device and you want to send a character to the DOS standard output without resetting the Putc pointer, you can call the PutcStdOut routine to accomplish this.

PutcStdOut uses the DOS call INT 21h, AH=02 to send its character to the standard output.


14.5.1 Calling Conventions


14.5.2 PutcStdOut Addressing Modes

PutcStdOut Addressing Modes
Name Plain CS TOS Stk X CSi
PutcStdOut X X X X - -

The PutcStdOut macro allows the following operands:

PutcStdOut Extended Syntax (Single/No Operands)
Name   byteVar Num const [word Var] [dword Var] String Const 8-bit Register
PutcStdOut X - - - - - -


14.5.3 Syntax & Examples



	mov	al, 'A'
	putcStdOut		;Print 'A' to the DOS standard output device.

14.6 PutcBIOS

The PutcBIOS routine prints the character in AL to the video display. This roughly corresponds to the standard error device. Since it is not particularly easy to redirect BIOS output, you can usually assume that any character sent through PutcBIOS will appear on the video display.

PutcBIOS prints its character using the INT 10h, AH=0Eh call. DOS usually makes this same call when printing data to the standard output (assuming no redirection), however, DOS calls the BIOS routine directly, it does not go through the PutcBIOS routine. PutcBIOS just happens to make the same call that DOS does in order to print a character to the video display.


14.6.1 Calling Conventions and Assertions


14.6.2 PutcBIOS Addressing Modes

PutcBIOS Addressing Modes
Name Plain CS TOS Stk X CSi
PutcStdOut X X X X - -

The PutcBIOS macro allows the following operands:

PutcBIOS Extended Syntax (Single/No Operands)
Name   byteVar Num const [word Var] [dword Var] String Const 8-bit Register
PutcBIOS X - - - - - -


14.6.3 Syntax & Examples



	mov	al, 'A'
	putcBIOS		;Print 'A' to the Video Display using BIOS.

14.7 Puts and Print

The Puts, PutsStk, PutsCS, and Print routines let you print string literal constants and string variables. Puts prints the zero-terminated string whose address appears in the ES:DI register pair. PutsStk prints the zero-terminated string whose far address appears on the top of stack. PutsCS prints the string pointed at by a dword following the call. Print displays the zero-terminated string literal that immediately follows the call in the code stream (This is equivalent to PutsCSi).


14.7.1 Calling Conventions and Assertions


14.7.2 Puts and Print Addressing Modes

Puts/Print Addressing Modes
Name Plain CS TOS Stk X CSi
Puts X X - X - -
Print - - - - - X

The Puts macro allows the following operands:

Puts Extended Syntax (Single/No Operands)
Name   byteVar Num const [word Var] [dword Var] String Const 8-bit Register
Puts X X - X X X -


14.7.3 Syntax & Examples

Puts without any operands along with PutsStk and Print:



StringToPrint   byte    "Print this string",nl,0
StrPointer      dword   StringToPrint
                 .
                 .
                 .

; Directly print "StringToPrint" by loading the address of this
; zero terminated string into ES:DI

                lesi    StringToPrint
                puts
                 .
                 .
                 .

; Print the string that "StrPointer" points at ("StringtoPrint" in
; this case).

                les     di, StrPointer
                puts
                 .
                 .
                 .

; PutsStk expects the far address of a zero-terminated string
; on the stack.  The following code pushes the address of
; StringToPrint onto the stack and calls PutsStk:

                push    StrPointer
                PutsStk
                 .
                 .
                 .
                
                push    seg StringToPrint
                push    offset StringToPrint
                PutsStk
                 .
                 .
                 .

; PutsCS expects the far address of a zero-terminated string
; immediately following the call:

                PutsStk
                dword   StringToPrint
                 .
                 .
                 .
                
                 
                 
; The Print routine lets you print string literals (constants) rather than
; a string variable.

                print
                byte    "String to print",nl,0
The Puts and Print macros support an alternate syntax that allows operands on the same line with the Puts and Print macros. If you include an operand for the Print statement, the Print macro assumes you're supplying a sequence of bytes and emits a "byte" directive with the operand you specify. Print automatically supplies a zero terminating byte when you use Print in this manner. Do not include a zero terminating byte as part of the operand; furthermore, you cannot follow this particular call to Print with additional string data using byte directives.

Examples:



                print   "String to print"
                  .
                  .
                  .
                print   "This string takes two lines",nl,"to print",nl

The Puts macro lets you specify several different types of arguments. If you supply a string literal or a constant, Puts emits a call to the Print routine. If you do this, Puts behaves exactly like the call to Print above. Like Print, you can supply multiple parameters. Puts automatically supplies the zero-terminating byte, you must not explicitly supply this yourself.

Examples:



                puts   "String to print"
                 .
                 .
                 .
                puts   "This string takes two lines",nl,"to print",nl

If you supply the name of a byte variable as the Puts operand, the Puts macro will assume that this is the name of a string variable you wish to print. Puts will push the address of this string literal onto the stack and emit a call to the PutsStk routine.

Example:



MyString        byte    "String to print",nl,0
                 .
                 .
                 .
                puts    MyString
                
; Note: the above is equivalent to the following sequence.

                PutsCS
                dword   MyString

If you supply the name of a word variable as the Puts operand, the Puts macro will assume that this variable contains a near address of a zero terminated string to print. Puts will assume that the string is in the data segment at the specified offset. Puts will push the current DS value following by the word variable's value onto the stack and then call the PutsStk routine.

Example:



; Note: this code assumes that the following two statements are in the
;       current data segment.

MyString        byte    "String to print",nl,0
NearPtr         word    MyString
                 .
                 .
                 .
                puts    [NearPtr]
                
; Note: the above is equivalent to the following sequence.

                push    DS
                push    NearPtr
                PutsStk

If you supply the name of a double word variable as the Puts operand, Puts will assume this is a far pointer to a zero terminated string. Puts will push that pointer onto the stack and call the PutsStk code.

Example:



MyString        byte    "String to print",nl,0
FarPtr          dword    MyString
                 .
                 .
                 .
                puts    [FarPtr]
                
; Note: the above is equivalent to the following sequence.

                pushd    FarPtr
                PutsStk

14.8 Printf, Printfr, PrintfStk

Printf, like its "C" namesake, provides formatted output capabilities for the stdlib package. A typical call to Printf generally takes the following form:



                        printf
                        byte            "format string",0
                        dword           operand1, operand2, ..., operandn

The format string is comparable to the one provided in the "C" programming language. For most characters, Printf simply prints the characters in the format string up to the terminating zero byte. The two exceptions are characters prefixed by a backslash ("\") and characters prefixed by a percent sign ("%"). Like C's printf, stdlib's printf uses the backslash as an escape character and the percent sign as a lead-in to a format string.

Printf uses the escape character ("\") to print special characters in a fashion similar to, but not identical to C's printf. Stdlib's Printf routine supports the following special characters:



                *  r     Print a carriage return (but no line feed)
                *  n     Print a new line character (carriage return/line feed).
                *  b     Print a backspace character.
                *  t     Print a tab character.
                *  l     Print a line feed character (but no carriage return).
                *  f     Print a form feed character.
                *  \     Print the backslash character.
                *  %     Print the percent sign character.
                *  0xhh  Print ASCII code hh, represented by two hex digits.

C users should note a couple of differences between stdlib's escape sequences and C's. First, use "\%" to print a percent sign within a format string, not "%%". C doesn't allow the use of "\%" because the C compiler processes "\%" at compile time (leaving a single "%" in the object code) whereas printf processes the format string at run-time. It would see a single "%" and treat it as a format lead-in character. Stdlib's Printf, on the other hand, processes both the "\" and "%" at run-time, therefore it can distinguish "\%".

Strings of the form "\0xhh" must contain exactly two hex digits. The current Printf routine isn't robust enough to handle sequences of the form "\0xh" which contain only a single hex digit. Keep this in mind if you find Printf chopping off characters after you print a value.

There is absolutely no reason to use any escape character sequences except "\0x00". Printf grabs all characters following the call to Printf up to the terminating zero byte (which is why you'd need to use "\0x00" if you want to print the null character, Printf will not print such values). Stdlib's Printf routine doesn't care how those characters got there. In particular, you are not limited to using a single string after the printf call. The following is perfectly legal:



                printf
                byte    "This is a string",13,10
                byte    "This is on a new line",13,10
                byte    "Print a backspace at the end of this line:"
                byte    8,13,10,0

Your code will run a tiny amount faster if you avoid the use of the escape character sequences. More importantly, the escape character sequences take at least two bytes. You can encode most of them as a single byte by simply embedding the ASCII code for that byte directly into the code stream. Don't forget, you cannot embed a zero byte into the code stream. A zero byte terminates the format string. Instead, use the "\0x00" escape sequence.

Format sequences always between with "%". For each format sequence you must provide a far pointer to the associated data immediately following the format string, e.g.,



                        printf
                        byte    "%i %i",0
                        dword   i,j

Format sequences take the general form "%s\cn^f" where:

The "s", "\c", "n", and "^" items are optional, the "%" and "f" items must be present. Furthermore, the order of these items in the format item is very important. The "\c" entry, for example, cannot precede the "s" entry. Likewise, the "^" character, if present, must follow everything except the "f" character(s).

The characters i, d, x, h, u, c, s, ld, li, lx, and lu control the conversion from a binary format to ASCII text. These format characters specify the type and size of the corresponding memory variable.

The i and d format characters are synonyms; they tell Printf to print the corresponding value as a 16-bit signed decimal integer. If the corresponding value is negative, Printf will print apreceding minus sign. Printf will not print any leading zeros for these values:



IntVal          sword   -12345
IntVal2         sword   54321
                 .
                 .
                 .
                printf
                byte    "IntVal = %d,  IntVal2 = %i",nl,0
                dword   IntVal, IntVal2

If you specify u, Printf prints the value as a 16-bit unsigned decimal integer. Printf will not print any leading zeros for the number (although see the description of the pad character below).



UnsVal          word   12345
UnsVal2         word   54321
                 .
                 .
                 .
                printf
                byte    "UnsVal = %u,  UnsVal2 = %u",nl,0
                dword   UnsVal, UnsVal2

The x and h format characters instruct Printf to print the specified value as a 16-bit or 8-bit hexadecimal value (respectively). Printf always prints 16-bit values using exactly four hexadecimal digits with leading zeros, if necessary. Likewise, Printf always prints eight-bit hexadecimal value using exactly two characters. Printf does not print a leading minus sign if the H.O. bit of the corresponding value is set.



HexVal          word   0ABCDh
HexVal2         byte   0FFh
                 .
                 .
                 .
                printf
                byte    "HexVal = %x,  UnsVal2 = %h",nl,0
                dword   HexVal, HexVal2

Using the "c" format character tells Printf to print the value as a single character. Printf prints the character corresponding to the ASCII code of the corresponding parameter value.



CharVal          byte   'A'
                 .
                 .
                 .
                printf
                byte    "ASCII code of '%c' is 0x%h",nl,0
                dword   CharVal, CharVal
                
The "s" format option tells printf that you're supplying the address of a zero-terminated character string. Printf prints that string up to the zero terminating byte.



String          byte    "Hello World",0
                 .
                 .
                 .
                printf
                byte    "String = '%s'",nl,0
                dword   String

The ld, li, lx, and lu entries are long (32-bit) versions of d/i, x, and u. The corresponding address points at a 32-bit value that Printf will format and print to the standard output. Note that the lx option (hexadecimal) always prints eight hex characters including any leading zeros.



L               sdword  -123456789
UL              dword   123456789
HL              dword   0ABCDEFh
                  .
                  .
                  .
                printf
                byte    "L=%ld, L=%li, UL=%lu, HL=%lx",nl,0
                dword   L, L, UL, HL

The following code sequence provides one more example of these format items:



i            sword       -25
u            word        20
HexC         byte        2ah
C            byte        'A'
S            byte        "Hello World", nl, 0
l            dword       123456789
               .
               .
               .
             printf
             byte      "I= %i, U= %u, HexC= %h, HexI= %x, C= %c, "
             byte      "S= %s",nl
             byte      "L= %ld",nl,0
             dword     i,u,c,i,c,s,l

The number of far addresses (specified by operands to the "dword" pseudo-opcode) must match the number of "%" format items in the format string. For each "%" format specifier appearing in the format string, Printf fetches a single far address immediately after the format string. When Printf encounters the zero terminating byte in the format string, it uses the address after the last dword address it has fetched as the return address. If the number of items do not match, the return address for Printf will be incorrect and the program will probably hang or otherwise malfunction. Likewise (as for the print routine), the format string must end with a zero byte.

When used in the format above, printf always prints the values using the minimum number of print positions for each operand. If you want to specify a minimum field width, you can do so using the "n" format option. A format item of the format "%10d" prints a decimal integer using at least ten print positions. Likewise, "%16s" prints a string using at least 16 print positions. If the value to print requires more than the specified number of print positions, Printf will use however many are necessary. If the value to print requires fewer, Printf will always print the specified number, padding the value with blanks.



             printf
             byte      "I= %i, U= %u, HexC= %h, HexI= %x, C= %c, "
             byte      "S= %s",nl
             byte      "L= %ld",nl,0
             dword     i,u,c,i,c,s,l

Printf will print the value right justified in the print field (regardless of the data's type). Note that this is the "intuitive" operation for numeric values, but it is counter-intuitive for strings and characters. If you want to print the value left justified in its field, use the "-" format character as a prefix to the field width, e.g.,



string          byte    "0123456789",0
                 .
                 .
                 .
                printf
                byte    "%17s",nl
                byte    "%-17s",0
                dword   string, string

This displays:



_ _ _ _ _ _ _ 0 1 2 3 4 5 6 7 8 9 
0 1 2 3 4 5 6 7 8 9  _ _ _ _ _ _ _

In this example, printf prints the string using a 17 character long field with the string left and right justified in the output field. By default, Printf pads the output field with spaces if the value requires fewer print positions than specified by the format item. The "\c" format item allows you to change the padding character. For example, to print a value, right justified, using "*" as the padding character you would use the format item "%\*10d". To print it left justified you would use the format item "%-\*10d". Note that the "-" must precede the "\*"; the operands must appear in this order.



string          byte    "0123456789",0
                 .
                 .
                 .
                printf
                byte    "%\#17s",nl
                byte    "%-\@17s",0
                dword   string, string

This displays:



# # # # # # # 0 1 2 3 4 5 6 7 8 9 
0 1 2 3 4 5 6 7 8 9 @ @ @ @ @ @ @

Normally, the address(es) following the Printf format string must be far pointers to the actual data to print. On occasion, especially when allocating storage on the heap (using malloc), you may not know (at assembly time) the address of the object you want to print. You may have only a pointer to the data you want to print. The "^" format option tells Printf that the far pointer following the format string is the address of a pointer to the data rather than the address of the data itself. This option lets you access the data indirectly. Consider the following example:



StrPtr          dword   ?
                  .
                  .
                  .
                getsm                   ;Read a string from the user
                mov     wp StrPtr, di   ;Save pointer to string malloc'd
                mov     wp Strptr+2, es ; on the heap in the StrPtr var.
                  .
                  .
                  .
                printf
                byte    "The string you entered was %^s\n",0
                dword   StrPtr
                
Printf's parameters must all be variables whose address is known at assembly time. In addition to excluding direct access to values allocated on the heap, Printf cannot access a procedure's local variables that you allocate on the stack and access via the BP register. Likewise, Printf cannot directly access values in one of the 80x86's registers. These limitations may be removed in a future version of Printf.

Unlike C, stdlib's printf routine does not support floating point output. Putting floating point into printf would increase the size of this routine a tremendous amount. Since most people don't need the floating point output facilities, it doesn't appear here. Check out PRINTFF.

The PrintfR routine is similar to Printf except you pass the pointers to the format string and parameter list in the FS:SI and ES:DI register pairs, respectively. This provides you with the ability to construct the format string (and, possibly, the parameter list) at run-time.

The PrintfStk routine also lets you pass the addresses of the format string and dword parameter list to it; however, you pass the pointers to the format string and parameter list on the stack (push the address of the parameter list first, the format string second).


14.8.1 Calling Conventions and Assertions


14.8.2 Syntax & Examples

The Printf procedure uses the following generic syntax:



                        printf
                        byte            "format string",0
                        dword           operand1, operand2, ..., operandn
The primary point to keep in mind when using Printf is that the number of (dword) parameters following the format string must exactly match the number of "%" format items appearing in the format string. Failure to do so will produce undefined results, often resulting in a crashed program.

Examples of various Printf features:



IntVar          sword   -1234
UnsignedVar     word    556
CharVar         byte    'A'
StrVar          byte    "String",0
LongUnsigned    dword   1234567
LongSigned      sdword  -123456

StrPtr          dword   StrVar
IntPtr          dword   IntVar
                 .
                 .
                 .
                 
; Simple examples of Printf

                printf
                byte    "IntVar = %d\n"
                byte    "UnsignedVar = %u\n"
                byte    "CharVar = '%c'\n"
                byte    "StrVar = '%s'\n"
                byte    "LongUnsigned = %lu\n"
                byte    "LongSigned = %ld\n"
                byte    "Hex(IntVar) = %x\n"
                byte    "ASCII code of '%c' is %h\n"
                byte    nl, 0
                dword   IntVar, UnsignedVar, CharVar, StrVar
                dword   LongUnsigned, LongSigned, IntVar
                dword   CharVar, CharVar
                  .
                  .
                  .
                  
; Examples involving field widths:

                printf
                byte    "IntVar =       %8d\n"
                byte    "UnsignedVar =  %8u\n"
                byte    "CharVar =      %8c\n"
                byte    "LongUnsigned = %8lu\n"
                byte    "LongSigned =   %8ld\n"
                byte    "Hex(IntVar) =  %8x\n"
                byte    "ASCII ('%c') = %8h\n"
                byte    nl, 0
                dword   IntVar, UnsignedVar, CharVar
                dword   LongUnsigned, LongSigned, IntVar
                dword   CharVar, CharVar
                  .
                  .
                  .
; The following example shows how to use the pad character to
; force leading zeros on decimal integers.  In this example,
; Printf prints the integer value in a field width of eight positions,
; right justified, with leading zeros between the "$" symbol and the
; actual start of the number.

                printf
                byte    "Pay exactly $%\08d Dollars and no cents",nl,0
                dword   IntVar
                  .
                  .
                  .
; Numeric values typically look best when right justified in a print
; field.  String and character values, however, generally look best when
; left justified in a print field.  The following example uses left
; justification to print character and string fields:

                printf
                byte    "The string is %-10s, the character is %-4c.",nl,0
                dword   StrVar, CharVar
                  .
                  .
                  .
; Sometimes you may only know the address of a pointer variable that
; contains the address of an object, you might not know the actual
; address of the object until run-time.  The following example demonstrates
; how to use the indirection operator, "^", to print the value referenced
; by a pointer variable.

                printf
                byte    "String = '%-8^s'",nl
                byte    "Integer= %8^s",nl,0
                dword   StrPtr, IntPtr
               

14.8.3 Alternate Syntax

The Printf macro lets you specify the format string and other parameters as operands. The first operand in the operand field must be the format string. Printf automatically places a zero byte after the first operand, so the first operand you specify must be a complete format string. If any other operands appear after the format string, Printf assumes these are the parameters that match the "%" format items and it emits these parameters as part of a dword directive. This variant of Printf is especially useful for short, simple, calls to Printf.

Examples:



                printf  "I=%5d, J=%5d,%s", I, J, String
                
                printf  "Array[%d] = %d\n"
                dword   Index, ArrayValue


14.9 Printff

This code works just like printf except it also allows the output of floating point values. The output formats are the following:

Since PRINTFF supports everything PRINTF does, you should not use both routines in the same program (just use PRINTF). Using both will not cause your program to fail, but it will make your program unnecessarily larger. You should not use PRINTFF unless you really need to print floating point values. When you use PRINTFF, it forces the linker to load in the entire floating point package, making your program considerably larger.


14.9.1 Calling Conventions and Assertions


14.9.2 Syntax & Examples

Printff supports all the standard Printf formats, modes, and alternate syntax. See the section on Printf for more details. The following examples demonstrate features specific to Printff.



fpVar           real4   1.23456
dpVar           real8   1.234567890e4
epVar           real10  -123.456e-789
                  .
                  .
                  .

                printff
                byte    "Single Precision FP Value = %8.3f",nl
                byte    "Scientific Notation Valut = %10e",nl
                byte    "Double Precision FP Value = $%\015.2gf",nl
                byte    "Double Precision Scientific = %20ge",nl
                byte    "Extended Precision Value = %15.8lf",nl
                byte    "Also Extended Precision = %20le",nl,0
                dword   fpVar, fpVar
                dword   dpVar, dpVar
                dword   epVar, epVar
                
                printff "Alternate Syntax: %8.3f", fpVar

14.10 Puth

The Puth, PuthCS, PuthTOS, and PuthStk routines output an eight-bit value as two hexadecimal digits to the standard output device. Puth displays the value in the AL register. PuthCS prints the hex value whose dword address immediately follows the call. PuthTOS prints the L.O. byte of the word appearing on the top of the stack. PuthStk prints the byte whose dword address appears on the top of stack. All these routines print exactly two digits, including a necessary leading zero character if the value is in the range 0..F.

Note that Puth, PuthCS, PuthTOS, and PuthStk, by default, use uppercase characters to display values in the range A..F. You can change this by calling the HexOutLC and HexOutUC routines. These routines do not print any prefix or suffix characters (e.g., "h" or "0x") to denote the hexadecimal radix. You must print these yourself if you need them.


14.10.1 Calling Conventions and Assertions


14.10.2 Puth Addressing Modes

Puth Addressing Modes
Name Plain CS TOS Stk X CSi
Puth X X X X - -

The PutcBIOS macro allows the following operands:

Puth Extended Syntax (Single/No Operands)
Name   byteVar Num const [word Var] [dword Var] String Const 8-bit Register
Puth X X X X X - X


14.10.3 Syntax & Examples



HexValue        byte    0ABh
wHex            word    HexValue
dHex            dword   HexValue
                 .
                 .
                 .
                mov     al, HexValue
                Puth

                push    word ptr HexValue
                PuthTOS
                
                pshadrs HexValue
                PuthStk
                
                PuthCS
                dword   HexValue

                puth            ;Prints value in AL

;-------------------------------------------
; An eight-bit register is a legal operand:

                puth    bl
                
; Equivalent to:
                push    bx
                PuthTOS
                
                
                
                
                puth    bh

; Equivalent to:
                xchg    bl, bh
                push    bx
                xchg    bl, bh
                PuthTOS
        

;------------------------------------------
; An eight-bit constant is a legal operand:

                Puth    123
                
; Equivalent to:
                push    123
                PuthTOS
                
                
;-------------------------------------------
; An eight-bit variable is a legal operand:
                
                Puth    HexValue
                
;Equivalent to:
                PuthCS
                dword   HexValue
                

;-----------------------------------------------
; An 16-bit pointer variable is a legal operand:

                Puth    [wHex]
                
; Equivalent to:
                push    ds
                push    wHex
                puthStk
                
;-----------------------------------------------
; A 32-bit pointer variable is a legal operand:

                Puth    [dHex]
                
; Equivalent to:
                pushd   dHex
                puthStk
                


14.11 Putw

The Putw, PutwCS, PutwTOS, and PutwStk routines output a 16-bit value as four hexadecimal digits to the standard output device. Putw displays the value in the AX register; PutwTOS prints the word appearing on the top of the stack; PutwCS prints the word pointed at by a dword pointer following the call; PutwStk prints the word pointed at by the dword pointer on TOS. All these routines print exactly four digits, including any necessary leading zero characters.

Note that Putw, PutwCS, PutwTOS, and PutwStk, by default, use uppercase characters to display values in the range A..F. You can change this by calling the HexOutLC and HexOutUC routines. They do not print any prefix or suffix characters (e.g., "h" or "0x") to denote the hexadecimal radix. You must print these yourself if you need them.


14.11.1 Calling Conventions and Assertions


14.11.2 PutW Addressing Modes

Putw Addressing Modes
Name Plain CS TOS Stk X CSi
Putw X X X X - -

The PutcBIOS macro allows the following operands:

Putw Extended Syntax (Single/No Operands)
Name   word Var Num const [word Var] [dword Var] String Const 16-bit Register
Putw X X X X X - X


14.11.3 Syntax & Examples

Standard calls for Putw, PutwCS, PutwTOS, and PutwStk:




HexValue        word    0ABCDh
wHex            word    HexValue
dHex            dword   HexValue
                 .
                 .
                 .
                mov     ax, HexValue
                Putw

                push    HexValue
                PutwTOS
                
                pshadrs HexValue
                PutwStk
                
                PutwCS
                dword   HexValue


                putw            ;Prints value in AX

;-------------------------------------------
; A 16-bit register is a legal operand:

                putw    bx
                
; Equivalent to:
                push    bx
                PutwTOS
                
                
                
                
;------------------------------------------
; A 16-bit constant is a legal operand:

                Putw    1234
                
; Equivalent to:
                push    1234
                PutwTOS
                
                
;-------------------------------------------
; A 16-bit variable is a legal operand:
                
                Putw    HexValue
                
;Equivalent to:
                PutwCS
                dword   HexValue
                

;-----------------------------------------------
; A 16-bit pointer variable is a legal operand:

                Puth    [wHex]
                
; Equivalent to:
                push    ds
                push    wHex
                putwStk
                
;-----------------------------------------------
; A 32-bit pointer variable is a legal operand:

                Putw    [dHex]
                
; Equivalent to:
                pushd   dHex
                putwStk
                


14.12 Puti, PutiSize

The Putixxx routines print a 16-bit signed decimal integer. Puti prints the value in the AX register; PutiTOS prints the 16-bit value you push on the stack; PutiCS prints the integer pointed at by the dword immediately following the call; PutiStk prints the 16-bit integer pointed at by a dword pointer on the top of stack. The PutiSizexxxx routines routines also print a signed integer value, but they let you specify the minimum field width for the value as well. PutiSize expects the integer value in AX and the field width value in CX. PutiSizeTOS expects the value to print on NOS (next on stack) and the minimum field width on TOS (top of stack). PutiSizeCS expects a dword pointer to a word followed by a dword field width in the code stream.

The minimum field width value specifies the number of print positions to use when outputting a value. If the number to print requires fewer than this specified number of print positions to display, the PutiSizexxxx routines will pad the number with spaces; these two routines will print the number right justified in the specified field width (by adding an appropriate number of spaces before the number). If the number requires more print positions than specified, PutiSizexxxx will ignore the minimum field width specification and print the number using however many positions are necessary. Don't forget than negative numbers use one print position for the leading minus sign.


14.12.1 Calling Conventions and Assertions


14.12.2 Puti and PutiSize Addressing Modes

Puti/PutiSize Addressing Modes
Name Plain CS TOS Stk X CSi
Puti X X X X - -
PutiSize X X X X - -

The Puti and PutiSize macros allow the following operands:

Puti/PutiSize Extended Syntax (Single/No Operands)
Name   word Var Num const [word Var] [dword Var] String Const 16-bit Register
Puti X X X X X - X
PutiSize X X X X X - -


14.12.3 Syntax & Examples




DecValue        word    0ABCDh
wDec            word    DecValue
dDec            dword   DecValue
                 .
                 .
                 .
                mov     ax, DecValue
                Puti

                push    DecValue
                PutiTOS
                
                pshadrs DecValue
                PutiStk
                
                PutiCS
                dword   DecValue


                puti            ;Prints value in AX

;-------------------------------------------
; A 16-bit register is a legal operand:

                puti    bx
                
; Equivalent to:
                push    bx
                PutiTOS
                
                
                
                
;------------------------------------------
; A 16-bit constant is a legal operand:

                Puti    1234
                
; Equivalent to:
                push    1234
                PutiTOS
                
                
;-------------------------------------------
; A 16-bit variable is a legal operand:
                
                Puti    HexValue
                
;Equivalent to:
                PutiCS
                dword   HexValue
                

;-----------------------------------------------
; A 16-bit pointer variable is a legal operand:

                Puti    [wHex]
                
; Equivalent to:
                push    ds
                push    wHex
                putiStk
                
;-----------------------------------------------
; A 32-bit pointer variable is a legal operand:

                Puti    [dHex]
                
; Equivalent to:
                pushd   dHex
                putiStk

;-----------------------------------------------
;-----------------------------------------------

14.13 Putu, PutuSize

The Putuxxx routines print an unsigned decimal integer. Putu prints the value in the AX register, PutuStk prints the 16-bit value you push on the stack. The PutuSize and PutuSizeStk routines routines also print an unsigned integer value, but they let you specify the minimum field width for the value as well. PutuSize expects the integer value in AX and the field width value in CX. PutuSizeStk expects the value to print on NOS (next on stack) and the minimum field width on TOS (top of stack); that is, it expects you to push the value then the minimum field width before calling PutuSizeStk.

The minimum field width value specifies the number of print positions to use when outputting a value. If the number to print requires fewer than this specified number of print positions to display, the PutuSize and PutuSizeStk routines will pad the number with spaces; these two routines will print the number right justified in the specified field width (by adding an appropriate number of spaces before the number). If the number requires more print positions than specified, PutuSize and PutuSizeStk will ignore the minimum field width specification and print the number using however many positions are necessary.


14.13.1 Calling Conventions and Assertions


14.13.2 Syntax & Examples



UnsValue        sword    12345
                 .
                 .
                 .
                mov     ax, UnsValue
                Putu

                push    UnsValue
                PutuStk
                mov     ax, UnsValue
                mov     cx, 10       ;Minimum print field width = 10.
                PutuSize
                push    UnsValue
                push    10          ;Field width is ten print positions
                PutuSizeStk

14.13.3 Alternate Syntax

Putu provides an alternate syntax allowing operands. If the single operand to Putu is a single memory location or 16-bit register, Putu pushes the memory location or register onto the stack and calls PutuStk



                putu    ax
                putu    bx
                putu    cx
                putu    dx
                putu    si
                putu    di
                putu    bp
                putu    Mem16

If you supply two operands to Putu, the macro will push these two operands onto the stack and call PutuSizeStk (the first operand is the value to print, the second operand is the minimum field width value):



                putu    ax, cx
                putu    mem16, 10
                putu    bp, ax
                putu    mem16, mem16
                putu    mem16, cx


14.14 Putl, PutlStk, Putl Size, PutlSizeStk

The Putlxxx routines print a 32-bit signed decimal integer. Putl prints the value in the EAX register, PutlStk prints the 32-bit value you push on the stack. The PutlSize and PutlSizeStk routines routines also print a 32-bit signed integer value, but they let you specify the minimum field width for the value as well. PutlSize expects the integer value in EAX and the field width value in CX. PutlSizeStk expects the 32-bit value to print on NOS (next on stack) and the 16-bit minimum field width on TOS (top of stack); that is, it expects you to push the value then the minimum field width before calling PutlSizeStk.

The minimum field width value specifies the number of print positions to use when outputting a value. If the number to print requires fewer than this specified number of print positions to display, the PutlSize and PutlSizeStk routines will pad the number with spaces; these two routines will print the number right justified in the specified field width (by adding an appropriate number of spaces before the number). If the number requires more print positions than specified, PutlSize and PutlSizeStk will ignore the minimum field width specification and print the number using however many positions are necessary. Don't forget than negative numbers use one print position for the leading minus sign.


14.14.1 Calling Conventions and Assertions


14.14.2 Syntax & Examples



LongValue        sdword    -12345
                 .
                 .
                 .
                mov     eax, LongIntValue
                Putl

                push    LongValue
                PutlStk
                mov     ex, LongValue
                mov     cx, 10       ;Minimum print field width = 10.
                PutlSize
                push    LongValue
                push    10          ;Field width is ten print positions
                PutlSizeStk

14.14.3 Alternate Syntax

Putl provides an alternate syntax allowing operands. If the single operand to Putl is a memory location or 32-bit register, Putl pushes the memory location or register onto the stack and calls PutlStk



                putl    eax
                putl    ebx
                putl    ecx
                putl    edx
                putl    esi
                putl    edi
                putl    ebp
                putl    Mem32

If you supply two operands to Putl, the macro will push these two operands onto the stack and call PutlSizeStk (the first operand is the value to print and must be a 32-bit value, the second operand is the minimum field width value which must be 16-bits):



                putl    eax, cx
                putl    mem32, 10
                putl    ebp, ax
                putl    mem32, mem16
                putl    mem32, cx

14.15 Putul, PutulStk, PutulSize, PutulSizeStk

The Putulxxx routines print a 32-bit unsigned decimal integer. Putul prints the value in the EAX register, PutulStk prints the 32-bit value you push on the stack. The PutulSize and PutulSizeStk routines routines also print an unsigned integer value, but they let you specify the minimum field width for the value as well. PutulSize expects the integer value in EAX and the field width value in CX. PutulSizeStk expects the 32-bit value to print on NOS (next on stack) and the 16-bit minimum field width on TOS (top of stack); that is, it expects you to push the value then the minimum field width before calling PutulSizeStk.

The minimum field width value specifies the number of print positions to use when outputting a value. If the number to print requires fewer than this specified number of print positions to display, the PutulSize and PutulSizeStk routines will pad the number with spaces; these two routines will print the number right justified in the specified field width (by adding an appropriate number of spaces before the number). If the number requires more print positions than specified, PutulSize and PutulSizeStk will ignore the minimum field width specification and print the number using however many positions are necessary.


14.15.1 Calling Conventions and Assertions


14.15.2 Syntax & Examples



UnsValue32        dword    123456789
                 .
                 .
                 .
                mov     ax, UnsValue32
                Putul

                push    UnsValue32
                PutulStk
                mov     eax, UnsValue32
                mov     cx, 10       ;Minimum print field width = 10.
                PutulSize
                push    UnsValue32
                push    10          ;Field width is ten print positions
                PutulSizeStk

14.15.3 Alternate Syntax

Putul provides an alternate syntax allowing operands. If the single operand to Putul is a single memory location or 32-bit register, Putul pushes the memory location or register onto the stack and calls PutulStk



                putul    eax
                putul    ebx
                putul    ecx
                putul    edx
                putul    esi
                putul    edi
                putul    ebp
                putul    Mem32

If you supply two operands to Putu, the macro will push these two operands onto the stack and call PutuSizeStk (the first operand is the value to print, the second operand is the minimum field width value):



                putul    eax, cx
                putul    mem32, 10
                putul    ebp, ax
                putul    mem32, mem16
                putul    mem32, cx

14.16 Putf, Pute

The Putf and Pute functions print floating point values. Putf prints the values in decimal form (e.g., 123.456) and Pute prints the values using scientific notation (e.g., 1.23456e+2). Both functions print the floating point value appearing on the top of the FPU stack (i.e., 80x87 register ST(0)).

The Putf routine expects two additional parameters: the minimum field width in the AL register and the number of positions to print after the decimal point in AH:



AL (Field Width) = 17
vvvvvvvvvvvvvvvvv
        -1234.567
              ^^^
AH (Fractional Width) = 3

Unlike the integer output routines, Putf will not attempt to print the number if it cannot display the number using the specified field width. Instead, Putf will fill the specified field width with "#" characters.

Pute requires a single field width parameter in the AL register. This field width must account for a leading sign, a decimal pointer, the "E" symbol, an exponent sign, and a two, three, or four character exponent (depending on the dynamic range of the floating point value):



-1.234567e+1000
^^^^^^^^^^^^^^^
AL (Field width) = 15

Putf and Pute print the 80-bit (real10) value on TOS. The FPU will automatically convert 32-bit (real4) and 64-bit (real8) floating point numbers to 80 bits when loading these values into the FPU. Note that with 32-bit floating point values there are only 6-1/2 digits of precision available. If you specify a field width that would cause Putf or Pute to print more than 6-1/2 digits, the extra digits are noise. Likewise, 64-bit floating point values support only 14-1/2 digits of precision and 80-bit floating point values support only 18 digits of precision. Note that converting a 32-bit or 64-bit value to 80-bits (by loading it into the FPU) does not give you additional digits of precision. If you attempt to print more than 18 digits, the Putf and Pute functions will print zeros for the additional digit positions. This does not imply that there would actually be zeros in those positions in a correct computation.

The current versions of Putf and Pute limit the field width to 30 characters. If you specify a field width larger than this, these routines will clip the value to 30 positions.


14.16.1 Calling Conventions and Assertions


14.16.2 Syntax & Examples



FPValue        real10    9.87654e+3
                 .
                 .
                 .
                fld     FPValue
                mov     al, 12
                mov     ah, 2
                Putf

                fld     FPValue
                mov     al, 12
                pute

14.17 GetOutAdrs, SetOutAdrs

These methods provide access to the stdlib output redirection pointer (the "Putc" pointer). GetOutAdrs returns the current Putc pointer value in the ES:DI register pair. SetOutAdrs stores the value in ES:DI into the Put pointer. The address you pass to SetOutAdrs must be the address of a far procedure.

Typically, you would use the GetOutAdrs function to obtain and save the current output function, then you would change this pointer using the SetOutAdrs call. Later, you would restore the original value using a second call to SetOutAdrs.

Note: to redirect the output pointer to the DOS standard output device, load the address of the "$PutcStdOut" routine into ES:DI. To redirect the output to the standard error device (the BIOS output routines), load ES:DI with the address of the "PutcBIOS" subroutine.

The character handler routine should expect an ASCII character code in the AL register. It most output this character to the appropriate device. On return, it must preserve all registers.


14.17.1 Calling Conventions and Assertions


14.17.2 Syntax & Examples



wp              textequ <word ptr>
AdrsSave        dword   ?
                  .
                  .
                  .
; Preserve the address of the current output routine.

                GetOutAdrs
                mov     wp AdrsSave, di
                mov     wp AdrsSave+2, es

; Load the address of "MyCharHandler" into the Putc pointer.

                lesi    MyCharHandler
                SetOutAdrs

; Print a string through "MyCharHandler".

                print
                byte    "DOUBLE",cr,lf,0

; Restore the Putc address to its original value.

                les     di, AdrsSave
                SetOutAdrs
                  .
                  .
                  .

; Redirect the output to the standard error output device (BIOS):

                lesi    $PutcBIOS
                SetOutAdrs
                print
                byte    "This text goes to the video display"
                byte    nl, 0
                 .
                 .
                 .

; Redirect the output back to the DOS standard output device.

                lesi    $PutcStdOut
                SetOutAdrs
                 .
                 .
                 .
; MyCharHandler- Prints every character sent to it twice through
;                the DOS standard output device.

MyCharHandler   proc    far
                putcStdout
                putcStdOut
                ret
MyCharHandler   endp

14.18 PushOutAdrs, PopOutAdrs

PushOutAdrs and PopOutAdrs are another pair of routines that let you manipulate the Putc output pointer. PushOutAdrs pushes the output routine address onto an internal stack (not the 80x86 hardware stack) and then copies the value in ES:DI to the Putc pointer. PopOutAdrs pops a 32-bit address off the internal stack to the Putc pointer.

These two routines provide a convenient way to temporarily switch the Putc pointer to some special handler (e.g., file output) and then restore the original output vector without manually saving the original pointer (vector) first.

The stdlib's internal address stack is large enough to hold 16 pointers. It would be hard to imagine any (error-free) program needing to stack up this many Putc pointers, so there should never be a problem with stack overflow under normal circumstances.


14.18.1 Calling Conventions and Assertions


14.18.2 Syntax & Examples

PushOutAdrs and PopOutAdrs provide a very convenient mechanism for temporarily redirecting the standard output to a routine that sends such output to a file. The following example demonstrates this usage.



wp              textequ <word ptr>
                 .
                 .
                 .

; Redirect the standard output to a routine that will send each
; character to a file rather than to the display.

                lesi    MyFileOut
                PushOutAdrs
                 .
                 .
                 .
                printf
                byte    "This data goes to a file",nl
                byte    "I = %4d,  J = %4d",nl
                byte    0
                dword   I,J
                 .
                 .
                 .

; Stop redirecting output to a file and resume sending it to the
; DOS standard output device:

                PopOutAdrs
                 .
                 .
                 .
MyFileOut       proc    far

        <Code that sends the ASCII character in >
        <AL to a file.                          >

MyFileOut       endp


14.19 ResetStdOut

The ResetStdOut routine initializes the stdlib standard output system. It cleans up and initializes the Putc pointer stack and it initializes the Putc pointer with the address of the PutcStdOut procedure.


14.19.1 Calling Conventions and Assertions


14.19.2 Syntax & Examples

Since ResetStdOut requires no parameters, the call is very simple:



                ResetStdOut