Category : Assembly Language Source Code
Archive   : RHSTDLIB.ZIP
Filename : SPRINTF.ASM

 
Output of file : SPRINTF.ASM contained in archive : RHSTDLIB.ZIP

stdlib segment para public 'slcode'
assume cs:stdlib
extrn sl_ISize:far, sl_ULSize:far
extrn sl_LSize:far, sl_USize:far
extrn sl_itoa:far, sl_free:far
extrn sl_wtoa:far, sl_ltoa:far
extrn sl_ultoa:far, sl_htoa:far
extrn sl_utoa:far
extrn sl_malloc:far, sl_realloc:far
;
;
;
; Sprintf- Like the "C" routine by the same name. Calling sequence:
;
; call sprintf
; db "format string",0
; dd item1, item2, ..., itemn
;
; Just like the PRINTF routine except it performs an in-memory format
; operation rather than printing the data to the current output device.
; Returns a pointer to the formatted string in ES:DI.
; See the PRINTF routine for more details about this guy.
;
;
cr equ 0dh
ff equ 0ch
lf equ 0ah
tab equ 09h
bs equ 08h
;
RtnAdrs equ 2[bp]
;
;
;
aindex dw ?
aptr dd ?
;
;
; sp_BufSize is a public variable so the user can adjust the size.
;
public sp_BufSize
sp_BufSize dw 2048
;
;
public sl_sprintf
sl_sprintf proc far
push bp
mov bp, sp
pushf
push ax cx
;
; Request some memory from the system. If there isn't enough available,
; try half as much and repeat. If there is no memory available, return
; with the carry set.
;
mov cx, sp_BufSize
TryAgain: call sl_malloc
jnc DoSPRINTF
shr cx, 1
cmp cx, 128 ;Need at least 128 bytes.
jae TryAgain
pop cx ax
popf
pop bp
stc
ret
;
; The following code simulates a far call to sbprintf. We can't make the
; call because we need to skip the MOV BP, SP instruction which appears
; at the beginning of the code.
;
DoSPRINTF: push cs ;Push fake return address
mov ax, offset RA
push ax
push bp ;Push stuff on stack
pushf
push ax bx cx dx di si es ds
jmp sl_sbprintf2
;
; Return back to this point from sbprintf.
;
RA: push di
mov cx, 1
mov al, ch
FindLength: cmp al, es:[di]
jz AtEnd
inc cx
inc di
jmp FindLength
;
AtEnd: pop di
call sl_realloc
pop cx ax
popf
pop bp
clc
ret
sl_sprintf endp
;
;
;
;
; SBPRINTF- Like sprintf except it doesn't allocate storage for the
; formatted string. Instead, you must pass it the address of a suitable
; buffer in es:di.
;
;
public sl_sbprintf
sl_sbprintf proc far
push bp
mov bp, sp
pushf
push ax bx cx dx di si es ds
;
; Save ptr to buffer area.
;
sl_sbprintf2: mov word ptr cs:aptr, di
mov word ptr cs:aptr+2, es
mov cs:aindex, 0
;
; Get pointers to the return address (format string).
;
cld
les di, RtnAdrs
lds si, RtnAdrs
;
; Okay, search for the end of the format string. After these instructions,
; di points just beyond the zero byte at the end of the format string. This,
; of course, points at the first address beyond the format string.
;
mov al, 0
mov cx, 65535
repne scasb
;
PrintItems: lodsb ;Get char si points at.
cmp al, 0 ;EOS?
jz PrintfDone
cmp al, "%" ;Start of a format string?
jz FmtItem
cmp al, "\" ;Escape character?
jnz PrintIt
call GetEscChar
PrintIt: call PutIt
jmp PrintItems
;
FmtItem: call GetFmtItem ;Process the format item here.
jmp PrintItems
;
PrintfDone: mov RtnAdrs, di ;Put out new return address.
pop ds
pop es
pop si
pop di
pop dx
pop cx
pop bx
pop ax
pop bp
popf
clc
ret
sl_sbprintf endp
;
; GetEscChar- Handles items immediately following the escape character "\".
;
; Special escape characters (upper/lower case is acceptable):
;
; n Newline (cr/lf)
; t tab
; b backspace
; r return
; l line feed
; f formfeed
; \ \
; % &
; 0xhh Char with hex character code hh. Must have exactly
; two hexadecimal digits.
;
GetEscChar proc near
lodsb ;Get next character
cmp al, 'n'
je RtnNL
cmp al, 'N'
je RtnNL
cmp al, 't'
je RtnTab
cmp al, 'T'
je RtnTab
cmp al, 'b'
je RtnBS
cmp al, 'B'
je RtnBS
cmp al, 'r'
je RtnRtn
cmp al, 'R'
je RtnRtn
cmp al, 'l'
je RtnLF
cmp al, 'L'
je RtnLF
cmp al, 'f'
je RtnFF
cmp al, 'F'
je RtnFF
;
; Check for the presence of a 0xhh value here:
;
cmp al, '0'
jne RtnChar
cmp byte ptr [si], 'x'
je GetHex
cmp byte ptr [si], 'X'
jne RtnChar
;
; Okay, process the hex value here. Note that exactly two hex digits must
; follow the 0x.
;
GetHex: inc si ;Point at first hex digit.
lodsb ;Get first hex digit.
and al, 05fh ;l.c. -> u.c.
cmp al, 'A'
jb GotIt
sub al, '7'
GotIt: shl al, 1 ;Put into H.O. nibble.
shl al, 1
shl al, 1
shl al, 1
mov ah, al ;Save for later
lodsb ;Get next char.
and al, 05fh
cmp al, 'A'
jb GotIt2
sub al, '7'
GotIt2: and al, 0fh
or al, ah
ret ;Return hex constant.
;
; RtnNL (return Newline) cheats. It needs to return two characters.
; Since GetEscChar only returns a single character, this code goes ahead
; and calls putc to output the CR and the returns the LF.
;
RtnNL: mov al, cr
call PutIt
mov al, lf
ret
;
RtnTab: mov al, tab
ret
;
RtnBS: mov al, bs
ret
;
RtnRtn: mov al, cr
ret
;
RtnLF: mov al, lf
ret
;
RtnFF: mov al, ff
RtnChar: ret
;
GetEscChar endp
;
;
;
GetFmtItem proc near
lodsb ;Get char beyond "%"
;
mov cx, 1 ;Default field width is 1.
mov dl, 0 ;Default is right justified
mov dh, ' ' ;Default fill char is space.
mov ah, ' ' ;Assume straight ptr, not handle.
;
; See if the user wants the value left justified:
;
cmp al, '-'
jne NotLeftJust
inc dl ;Set to right justified
lodsb ;Get next character.
;
; See if the user wants to change the padding character.
;
NotLeftJust: cmp al, '\'
jne NoPadChange
lodsb ;Get Padding Character.
mov dh, al ;Save padding character.
lodsb ;Get next character
;
; See if the user wants a different field width:
;
NoPadChange: cmp al, '0'
jb NoFldWidth
cmp al, '9'
ja NoFldWidth
call GetDecVal
;
; See if the user wants to specify a handle rather than a straight pointer
;
NoFldWidth: cmp al, '^'
jne ChkFmtChars
mov ah, al
lodsb ;Skip "^" character
;
; Okay, process the format characters down here.
;
ChkFmtChars: and al, 05fh ;l.c. -> U.C.
cmp al, 'D'
je PrintDec
cmp al, 'I'
je PrintDec
cmp al, 'C'
je PrintChar
;
cmp al, 'X'
jne TryH
jmp PrintHexWord
;
TryH: cmp al, 'H'
jne TryU
jmp PrintHexByte
;
TryU: cmp al, 'U'
jne TryString
jmp PrintUDec
;
TryString: cmp al, 'S'
jne TryLong
jmp PrintString
;
TryLong: cmp al, 'L'
jne Default
;
; If we've got the "L" modifier, this is a long value to print, get the
; data type character as the next value:
;
lodsb
and al, 05fh ;l.c. -> U.C.
cmp al, 'D'
je JmpDec
cmp al, 'I'
jne TryLU
JmpDec: jmp LongDec
;
TryLU: cmp al, 'U'
jne TryX
jmp LongU
;
TryX: cmp al, 'X'
jne Default
jmp LongX
;
;
;
; If none of the above, simply return without printing anything.
;
Default: ret
;
;
;
;
;
; Print a signed decimal value here.
;
PrintDec: call GetPtr ;Get next pointer into ES:BX
mov ax, es:[bx] ;Get value to print.
call sl_ISize ;Get the size of this guy.
sub cx, ax ;Compute padding
mov ax, es:[bx] ;Retrieve value to print.
js NoPadDec ;Is CX negative?
cmp dl, 0 ;Right justified?
jne LeftJustDec
call PrintPad ;Print padding characters
call PutIti ;Print the integer
ret ;We're done!
;
; Print left justified value here.
;
LeftJustDec: call PutIti
call PrintPad
ret
;
; Print non-justified value here:
;
NoPadDec: call PutIti
ret
;
;
;
; Print a character variable here.
;
PrintChar: call GetPtr ;Get next pointer into ES:BX
mov al, es:[bx] ;Retrieve value to print.
dec cx
js NoPadChar ;Is CX negative?
cmp dl, 0 ;Right justified?
jne LeftJustChar
call PrintPad ;Print padding characters
call PutIt ;Print the character
ret ;We're done!
;
; Print left justified value here.
;
LeftJustChar: call PutIt
call PrintPad
ret
;
; Print non-justified character here:
;
NoPadChar: call PutIt
ret
;
;
;
;
; Print a hexadecimal word value here.
;
PrintHexWord: call GetPtr ;Get next pointer into ES:BX
mov ax, es:[bx] ;Get value to print.
sub cx, 4 ;Compute padding
js NoPadHexW ;Is CX negative?
cmp dl, 0 ;Right justified?
jne LeftJustHexW
call PrintPad ;Print padding characters
call PutItw ;Print the hex value
ret ;We're done!
;
; Print left justified value here.
;
LeftJustHexW: call PutItw
call PrintPad
ret
;
; Print non-justified value here:
;
NoPadHexW: call PutItw
ret
;
;
;
;
; Print hex bytes here.
;
;
PrintHexByte: call GetPtr ;Get next pointer into ES:BX
mov ax, es:[bx] ;Get value to print.
sub cx, 4 ;Compute padding
js NoPadHexB ;Is CX negative?
cmp dl, 0 ;Right justified?
jne LeftJustHexB
call PrintPad ;Print padding characters
call PutIth ;Print the hex value
ret ;We're done!
;
; Print left justified value here.
;
LeftJustHexB: call PutIth
call PrintPad
ret
;
; Print non-justified value here:
;
NoPadHexB: call PutIth
ret
;
;
;
; Output unsigned decimal numbers here:
;
PrintUDec: call GetPtr ;Get next pointer into ES:BX
mov ax, es:[bx] ;Get value to print.
call sl_USize ;Get the size of this guy.
sub cx, ax ;Compute padding
mov ax, es:[bx] ;Retrieve value to print.
js NoPadUDec ;Is CX negative?
cmp dl, 0 ;Right justified?
jne LeftJustUDec
call PrintPad ;Print padding characters
call PutItu ;Print the integer
ret ;We're done!
;
; Print left justified value here.
;
LeftJustUDec: call PutItu
call PrintPad
ret
;
; Print non-justified value here:
;
NoPadUDec: call PutItu
ret
;
;
;
;
; Output a string here:
;
PrintString: call GetPtr ;Get next pointer into ES:BX
;
; Compute the length of the string:
;
push di
push cx
mov cx, -1
mov di, bx
mov al, 0
repne scasb
mov ax, cx

neg ax
dec ax
dec ax
pop cx
pop di
sub cx, ax ;Field width - String Length.
;
js NoPadStr ;Is CX negative?
cmp dl, 0 ;Right justified?
jne LeftJustStr
call PrintPad ;Print padding characters
call Puts ;Print the string
ret ;We're done!
;
; Print left justified value here.
;
LeftJustStr: call Puts
call PrintPad
ret
;
; Print non-justified value here:
;
NoPadStr: call Puts
ret
GetFmtItem endp
;
;
;
; Print a signed long decimal value here.
;
LongDec: call GetPtr ;Get next pointer into ES:BX
mov ax, es:[bx] ;Get value to print.
push dx
mov dx, es:2[bx]
call sl_LSize ;Get the size of this guy.
pop dx
sub cx, ax ;Compute padding
mov ax, es:[bx] ;Retrieve value to print.
js NoPadLong ;Is CX negative?
cmp dl, 0 ;Right justified?
jne LeftJustLong
call PrintPad ;Print padding characters
mov dx, es:2[bx] ;Get H.O. word
call PutItL ;Print the integer
ret ;We're done!
;
; Print left justified value here.
;
LeftJustLong: push dx
mov dx, es:2[bx] ;Get H.O. word
call PutItL
pop dx
call PrintPad
ret
;
; Print non-justified value here:
;
NoPadLong: mov dx, es:2[bx] ;Get H.O. word
call PutItl
ret
;
;
; Print an unsigned long decimal value here.
;
LongU: call GetPtr ;Get next pointer into ES:BX
mov ax, es:[bx] ;Get value to print.
push dx
mov dx, es:[bx]
call sl_ULSize ;Get the size of this guy.
pop dx
sub cx, ax ;Compute padding
mov ax, es:[bx] ;Retrieve value to print.
js NoPadULong ;Is CX negative?
cmp dl, 0 ;Right justified?
jne LeftJustULong
call PrintPad ;Print padding characters
mov dx, es:2[bx] ;Get H.O. word
call PutItUL ;Print the integer
ret ;We're done!
;
; Print left justified value here.
;
LeftJustULong: mov dx, es:2[bx] ;Get H.O. word
call PutItUL
call PrintPad
ret
;
; Print non-justified value here:
;
NoPadULong: mov dx, es:2[bx] ;Get H.O. word
call Putitul
ret
;
;
; Print a long hexadecimal value here.
;
LongX: call GetPtr ;Get next pointer into ES:BX
sub cx, 8 ;Compute padding
js NoPadXLong ;Is CX negative?
cmp dl, 0 ;Right justified?
jne LeftJustXLong
call PrintPad ;Print padding characters
mov ax, es:2[bx] ;Get H.O. word
call PutItw
mov ax, es:[bx]
call PutItw
ret ;We're done!
;
; Print left justified value here.
;
LeftJustxLong: mov ax, es:2[bx] ;Get H.O. word
call PutItw
mov ax, es:[bx] ;Get L.O. word
call PutItw
call PrintPad
ret
;
; Print non-justified value here:
;
NoPadxLong: mov ax, es:2[bx] ;Get H.O. word
call PutItw
mov ax, es:[bx]
call PutItw
ret
;
;
;
;
; Puts- Outputs the zero terminated string pointed at by ES:BX.
;
Puts proc near
PutsLp: mov al, es:[bx]
cmp al, 0
je PutsDone
call putIt
inc bx
jmp PutsLp
;
PutsDone: ret
Puts endp
;
;
;
;
;
; PrintPad- Prints padding characters. Character to print is in DH.
; We must print it CX times. CX must be greater than zero.
;
PrintPad proc near
push ax
mov al, dh
jcxz NoPadding
PPLoop: call PutIt
loop PPLoop
NoPadding: pop ax
ret
PrintPad endp
;
;
;
;
;
; GetPtr- Grabs the next pointer which DS:DI points at and returns this
; far pointer in ES:BX.
;
GetPtr proc near
les bx, [di]
add di, 4
;
; See if this is a handle rather than a pointer.
;
cmp ah, '^'
jne NotHandle
les bx, es:[bx]
NotHandle: ret
GetPtr endp
;
;
;
;
;
; GetDecVal- Converts the string of decimal digits in AL and [SI] into
; an integer and returns this integer in CX.
;
GetDecVal proc near
push dx
dec si
xor cx, cx
DecLoop: lodsb
cmp al, '0'
jb NoMore
cmp al, '9'
ja NoMore
and al, 0fh
shl cx, 1 ;Compute CX := CX*10 + al
mov dx, cx
shl cx, 1
shl cx, 1
add cx, dx
add cl, al
adc ch, 0
jmp DecLoop
NoMore: pop dx
ret
GetDecVal endp
;
;
; PutItL - outputs the unsigned long value in AX to the string.
;
PutItL proc
push bx cx es si ds di
call sl_ltoa
call ConCat
pop di ds si es cx bx
ret
PutItL endp
;
;
; PutItUL - outputs the unsigned long value in AX to the string.
;
PutItUL proc
push bx cx es si ds di
call sl_ultoa
call ConCat
pop di ds si es cx bx
ret
PutItUL endp
;
;
;
; PutItw - outputs the hexadecimal value in AX to the string.
;
PutItw proc
push bx cx es si ds di
call sl_wtoa
call ConCat
pop di ds si es cx bx
ret
PutItw endp
;
;
; PutIth - outputs the hexadecimal value in AL to the string.
;
PutIth proc
push bx cx es si ds di
call sl_htoa
call ConCat
pop di ds si es cx bx
ret
PutIth endp
;
;
;
; PutIti - outputs the integer in AX to the string.
;
PutIti proc
push bx cx es si ds di
call sl_itoa
call ConCat
pop di ds si es cx bx
ret
PutIti endp
;
;
; PutItu - outputs the unsigned integer in AX to the string.
;
PutItu proc
push bx cx es si ds di
call sl_utoa
call ConCat
pop di ds si es cx bx
ret
PutItu endp
;
;
;
; ConCat- Concatenates the string pointed at by ES:DI to the end of our
; formatted string.
;
ConCat proc near
push di
lds si, cs:aptr
mov bx, cs:aindex
sub di, bx
PILp: mov al, es:[di][bx]
mov [si][bx], al
inc bx
cmp al, 0
jne PILp
dec bx
mov cs:aindex, bx
pop di
call sl_free
ret
ConCat endp
;
; PutIt writes the character in AL to the string buffer area.
;
PutIt proc
push es si bx
mov bx, cs:aindex
les si, cs:aptr
mov es:[si][bx], al
mov byte ptr es:1[si][bx], 0
inc cs:aindex
pop bx si es
ret
PutIt endp
;
stdlib ends
end