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