DOS 3.30 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

414 lines
9.3 KiB

TITLE PRINTF ROUTINE FOR MS-DOS
;
; PRINTF(Control String, arg1, arg2,...,argn-1,argn)
;
; Characters are output to PFHandle according to the
; specifications contained in the Control String.
;
; The conversion characters are as follow:
;
; %c - output the next argument as a character
; %s - output the next argument as a string
; %x - output the next argument as a hexidecimal number
; using abcedf
; %X - output the next argument as a hexidecimal number
; using ABCDEF
; %d - output the next argument as a decimal number
;
;
; Other format specifiers that may precede the conversion character are:
;
; - (minus sign) - causes the field to be left-adjusted
; + (plus sign) - causes the field to be right-adjusted (default)
; n - digit specifing the minimum field width (default to 1)
; L - specifing a long integer
;
; On entry to PRINTF the stack contains the return address and a pointer
; to an argument list.
;
; ____________________
; | Ret Addr | <= SP
; --------------------
; | Ptr to Arg List |
; --------------------
;
; And the argument list contains the following:
;
; String_ptr (a pointer to the control string)
; Arg 1
; Arg 2
; .
; .
; .
; Arg n-1
; Arg n
;
; If the argument is a %s or %c the arg contains a pointer to the string
; or character.
;
; The arguments are used in one-to-one correspondence to % specifiers.
.xlist
.xcref
INCLUDE dossym.inc
.cref
.list
printf_CODE SEGMENT public byte
ASSUME CS:PRINTF_CODE,DS:NOTHING,ES:NOTHING,SS:NOTHING
PUBLIC PRINTF, PFHandle
PUBLIC PRINTF_LAST
PFHandle DW 1
PRINTF_LEFT DB 0
PRINTF_LONG DB 0
PRINTF_HEX DB 0
TABLE_INDEX DB 0
S_FLAG DB 0
PRINTF_WIDTH DW 0
PRINTF_BASE DW 0
PAD_CHAR DB " "
PRINTF_TABLE DB "0123456789ABCDEFabcdef"
PRINTF_STACK STRUC
OLDES DW ?
OLDDS DW ?
OLDSI DW ?
OLDDI DW ?
OLDAX DW ?
OLDBX DW ?
OLDCX DW ?
OLDDX DW ?
OLDBP DW ?
OLDCS DW ?
OLDIP DW ?
STRING DW ?
PRINTF_STACK ENDS
PRINTF_ARGS STRUC
CONSTR DW ?
ARG DW ?
PRINTF_ARGS ENDS
RET_ADDR1 DW ?
RET_ADDR2 DW ?
BUFSIZ = 20
PRINTF_BUF DB BUFSIZ DUP (?)
db 0 ;This buffer is always nul terminated
BUFEND DW $-PRINTF_BUF
PRINTF proc far
PUSH BP ;Save the callers' registers
PUSH DX
PUSH CX
PUSH BX
PUSH AX
PUSH DI
PUSH SI
PUSH ES
PUSH DS
MOV BP,SP
PUSH CS
POP ES ;ES points to Printf segment
MOV DI,OFFSET PRINTF_BUF ;DI points to the output buffer
MOV BP,[BP.STRING] ;BP points to the argument list
MOV SI,DS:[BP] ;SI points to the control string
XOR BX,BX ;BX is the index into the arg list
CALL Clear_flags ; initialize the world
GET_CHAR:
LODSB ;Get a character
CMP AL,"%" ;Is it a conversion specifier?
JZ CONV_CHAR ;Yes - find out which one
OR AL,AL ;Is it the end of the control string?
JZ PRINTF_DONE ;Yes - then we're done
CALL OUTCHR ;Otherwise store the character
JMP SHORT GET_CHAR ;And go get another
PRINTF_DONE:
CALL FLUSH
POP DS
POP ES
POP SI
POP DI
POP AX
POP BX
POP CX
POP DX
POP BP
POP CS:[RET_ADDR1] ;Fix up the stack
POP CS:[RET_ADDR2]
POP AX
PUSH CS:[RET_ADDR2]
PUSH CS:[RET_ADDR1]
RET
printf endp
PRINTF_PERCENT:
CALL OUTCHR
JMP GET_CHAR
CONV_CHAR:
;Look for any format specifiers preceeding the conversion character
LODSB
CMP AL,"%" ;Just print the %
JZ PRINTF_PERCENT
CMP AL,"-" ;Right justify the field
JZ LEFT_ADJ
CMP AL,"+" ;Left justify the field
JZ NXT_CONV_CHAR
CMP AL,"L" ;Is it a long integer
JZ LONG_INT
CMP AL,"l"
JZ LONG_INT
CMP AL,"0" ;Is it a precision specification
JB LOOK_CONV_CHAR
CMP AL,"9"
JA LOOK_CONV_CHAR
CMP AL,"0"
JNZ NOT_PAD
CMP CS:[PRINTF_WIDTH],0
JNZ NOT_PAD
MOV CS:BYTE PTR [PAD_CHAR],"0"
NOT_PAD:
PUSH AX ;Adjust decimal place on precision
MOV AX,10
MUL CS:[PRINTF_WIDTH]
MOV CS:[PRINTF_WIDTH],AX
POP AX
XOR AH,AH
SUB AL,"0"
ADD CS:[PRINTF_WIDTH],AX ;And save the total
JMP SHORT NXT_CONV_CHAR
;Set the correct flags for the options in a conversion
LEFT_ADJ:
INC CS:BYTE PTR[PRINTF_LEFT]
JMP SHORT NXT_CONV_CHAR
LONG_INT:
INC CS:BYTE PTR[PRINTF_LONG]
NXT_CONV_CHAR:
JMP CONV_CHAR
;Look for a conversion character
LOOK_CONV_CHAR:
CMP AL,"X"
JZ HEX_UP
;Make all other conversion characters upper case
CMP AL,"a"
JB CAPS
CMP AL,"z"
JG CAPS
AND AL,0DFH
CAPS:
CMP AL,"X"
JZ HEX_LO
CMP AL,"D"
JZ DECIMAL
CMP AL,"C"
JZ C_PUT_CHAR
CMP AL,"S"
JZ S_PUT_STRG
;Didn't find any legal conversion character - IGNORE it
call clear_flags
jmp get_char
HEX_LO:
MOV CS:[TABLE_INDEX],6 ;Will print lower case hex digits
HEX_UP:
MOV CS:[PRINTF_BASE],16 ;Hex conversion
JMP CONV_TO_NUM
DECIMAL:
MOV CS:[PRINTF_BASE],10 ;Decimal conversion
JMP CONV_TO_NUM
S_PUT_STRG:
INC CS:[S_FLAG] ;It's a string specifier
C_PUT_CHAR:
PUSH SI ;Save pointer to control string
MOV SI,BX
ADD BX,2
MOV SI,ds:[BP+SI.ARG] ;Point to the % string or character
CMP BYTE PTR CS:[S_FLAG],0
JNZ S_PUT_1
LODSB
cmp al,0
jz short c_s_end
CALL OUTCHR ;Put it into our buffer
JMP SHORT C_S_END
S_PUT_1:
mov cx,cs:[printf_width]
or cx,cx
jz s_put_2
cmp cs:byte ptr[printf_left],0
jnz s_put_2
push si
call Pad_string
pop si
s_put_2:
push si
s_put_3:
LODSB ;Put them all in our buffer
CMP AL,0
jz s_put_4
CALL OUTCHR
jmp short S_PUT_3
s_put_4:
pop si
cmp byte ptr[printf_left],0
jz c_s_end
mov cx,cs:[printf_width]
or cx,cx
jz c_s_end
call Pad_string
C_S_END:
call clear_flags
POP SI ;Restore control string pointer
JMP GET_CHAR ;Go get another character
pad_string:
xor dx,dx
count_loop:
lodsb
or al,al
jz count_done
inc dx
jmp short count_loop
count_done:
sub cx,dx
jbe count_ret
call pad
count_ret:
ret
CONV_TO_NUM:
PUSH SI ;Save pointer to control string
MOV SI,BX ;Get index into argument list
ADD BX,2 ;Increment the index
MOV AX,ds:[BP+SI.ARG] ;Lo word of number in SI
CMP BYTE PTR CS:[PRINTF_LONG],0 ;Is this is a short or long integer?
JZ NOT_LONG_INT
MOV SI,BX ;Copy index
ADD BX,2 ;Increment the index
MOV DX,ds:[BP+SI.ARG] ;Hi word of number in BP
JMP SHORT DO_CONV
NOT_LONG_INT:
XOR DX,DX ;Hi word is zero
DO_CONV:
PUSH BX ;Save index into arguemnt list
MOV si,CS:[PRINTF_BASE]
MOV cx,CS:[PRINTF_WIDTH]
CALL PNUM
CALL PAD
CONV_DONE:
call clear_flags
POP BX
POP SI
jmp get_char
PNUM:
DEC CX
PUSH AX
MOV AX,DX
XOR DX,DX
DIV SI
MOV BX,AX
POP AX
DIV SI
XCHG BX,DX
PUSH AX
OR AX,DX
POP AX
JZ DO_PAD
PUSH BX
CALL PNUM
POP BX
JMP SHORT REM
DO_PAD:
CMP CS:BYTE PTR[PRINTF_LEFT],0
JNZ REM
CALL PAD
REM:
MOV AX,BX
CMP AL,10
JB NOT_HEX
CMP CS:BYTE PTR [PRINTF_HEX],0
JNZ NOT_HEX
ADD AL,CS:BYTE PTR [TABLE_INDEX]
NOT_HEX:
MOV BX,OFFSET PRINTF_TABLE
PUSH DS
PUSH CS
POP DS
XLAT 0
POP DS
push cx
CALL OUTCHR
pop cx
RET
PAD:
OR CX,CX
JLE PAD_DONE
MOV AL,CS:BYTE PTR [PAD_CHAR]
PAD_LOOP:
push cx
CALL OUTCHR
pop cx
LOOP PAD_LOOP
PAD_DONE:
RET
OUTCHR:
STOSB
CMP DI,offset bufend-1 ;Don't count the nul
RETNZ
MOV CX,BUFSIZ
WRITE_CHARS:
push bx
MOV BX,PFHandle
push ds
PUSH CS
POP DS
MOV DX,OFFSET PRINTF_BUF
MOV AH,WRITE
INT 21H
pop ds
pop bx
MOV DI,OFFSET PRINTF_BUF
RET
FLUSH:
CMP DI,OFFSET PRINTF_BUF
RETZ
SUB DI,OFFSET PRINTF_BUF
MOV CX,DI
call write_chars
ret
CLEAR_FLAGS:
XOR ax,ax
MOV BYTE PTR CS:[PRINTF_LEFT],al ;Reset justifing flag
MOV BYTE PTR CS:[PRINTF_LONG],al ;Reset long flag
MOV BYTE PTR CS:[TABLE_INDEX],al ;Reset hex table index
MOV CS:[PRINTF_WIDTH],ax ;Reinitialize width to 0
MOV BYTE PTR CS:[PAD_CHAR]," " ;Reset padding character
MOV BYTE PTR CS:[S_FLAG],al ;Clear the string flag
ret
PRINTF_LAST LABEL WORD
printf_CODE ENDS
END