please dont rip this site
	Page	58,132
	Title	PRINTF.ASM	Generic Printf Module
;******************************************************************************
;
;   Name:	PRINTF.ASM	Generic Printf Module
;
;   Revision:	1.00
;
;   Date:	November 30, 1988
;
;   Author:	Randy Spurlock
;
;******************************************************************************
;
;  Module Functional Description:
;
;	Printf - Prints a formatted string of arguments to
;		 the requested handle.
;
;	Calling Sequence:
;
;		mov	al,HANDLE	; Get the desired handle to print on
;		lea	bx,ds:[args]	; Get a pointer to the arguments
;		lea	si,ds:[format]	; Get a pointer to the format string
;		call	printf		; Call the printf routine
;
;	The conversion characters are as follows:
;
;		%% - Print percent sign to handle
;		%c - Output the next argument as a character
;		%s - Output the next argument as a string
;		%x - Output the next argument as a hex number using abcdef
;		%X - Output the next argument as a hex number using ABCDEF
;		%h - Output the next argument as a hex number using abcdef
;		%H - Output the next argument as a hex number using ABCDEF
;		%d - Output the next argument as a decimal number (Signed)
;		%u - Output the next argument as a decimal number (Unsigned)
;		%o - Output the next argument as a octal number
;		%b - Output the next argument as a binary number
;		%f - Output the next argument as a fractional number (Signed)
;
;	Other format specifiers may precede the conversion character:
;
;		-  - Left justify the field
;		+  - Set signed field
;		n  - Specify the field width/precision
;		t  - Specify short value
;		l  - Specify long value
;		#  - Specify far argument pointer
;		&  - Specify indirect argument pointer
;		$  - Specify immediate argument value
;		*  - Variable precision/width value (From argument list)
;
;	All arguments must be pointers to the actual values.
;
;	The following escape sequences are also handled:
;
;		\\   -	Backslash
;		\n   -	Newline
;		\t   -	Horizontal Tab
;		\v   -	Vertical Tab
;		\b   -	Backspace
;		\r   -	Carriage Return
;		\f   -	Form Feed
;		\ddd -	ASCII Character (Octal Notation)
;		\xdd -	ASCII Character (Hexadecimal Notation)
;
;******************************************************************************
;
;  Changes:
;
;    DATE     REVISION				DESCRIPTION
;  --------   --------	-------------------------------------------------------
;  11/30/88	1.00	Original				Randy Spurlock
;
;******************************************************************************
	Page
;
;  Public Declarations
;
	Public	Printf			; Generic Printf routine
;
;  Define the Local Equates
;
FORMAT_CHAR	Equ	"%"             ; Format specification character
ESCAPE_CHAR	Equ	"\"             ; Escape sequence character
BASE_HEX	Equ	16		; Base 16 - Hexadecimal
BASE_DECIMAL	Equ	10		; Base 10 - Decimal
BASE_OCTAL	Equ	8		; Base 8  - Octal
BASE_BINARY	Equ	2		; Base 2  - Binary
FORMAT_LENGTH	Equ	5		; Maximum format number width
ESCAPE_LENGTH	Equ	3		; Escape sequence number width (Decimal)
HEX_LENGTH	Equ	2		; Escape sequence number width (Hex)
MAX_WORD	Equ	0FFFFh		; Maximum 16-bit count value
MAX_BYTE	Equ	0FFh		; Maximum 8-bit count value
SPACE_PAD	Equ	" "             ; Space pad character
ZERO_PAD	Equ	"0"             ; Zero pad character
LEFT_JUST	Equ	8000h		; Left justification flag
SHORT_SPEC	Equ	4000h		; Short specification flag
LONG_SPEC	Equ	2000h		; Long specification flag
UPPER_CASE	Equ	1000h		; Upper case hexadecimal flag
SIGNED_CONV	Equ	0800h		; Signed conversion flag
SIGNED_TYPE	Equ	0400h		; Signed type flag
SIGNED_VAL	Equ	0200h		; Signed value flag
PRE_PAD 	Equ	0100h		; Pre-pad sign character flag
FAR_SPEC	Equ	0080h		; Far argument specification flag
OVER_FLOW	Equ	0040h		; Field overflow flag
FRACTIONAL	Equ	0020h		; Fractional integer flag
VAR_WIDTH	Equ	0010h		; Variable width flag
VAR_PRE 	Equ	0008h		; Variable precision flag
PAD_CHAR	Equ	0004h		; Pad character flag (0=Space, 1=Zero)
INDIRECT	Equ	0002h		; Indirection flag (1 = Indirect)
IMMEDIATE	Equ	0001h		; Immediate flag (1 = Immediate)
SIGNED		Equ	8000h		; Sign flag test mask
DECIMAL_ADJUST	Equ	30h		; ASCII decimal to binary adjust value
HEX_ADJUST	Equ	07h		; ASCII hex to binary adjust value
UPPER_MASK	Equ	0DFh		; Lower to upper case mask value
FAR_SIZE	Equ	4h		; Far argument pointer size   (4 bytes)
NEAR_SIZE	Equ	2h		; Near argument pointer size  (2 bytes)
LONG_SIZE	Equ	4h		; Long numeric argument size  (4 bytes)
NORMAL_SIZE	Equ	2h		; Normal numeric argument size(2 bytes)
SHORT_SIZE	Equ	1h		; Short numeric argument size (1 bytes)
BUFF_SIZE	Equ	64		; Numeric build buffer size
DIGIT_MAX	Equ	39h		; Maximum ASCII digit value
DOS_FUNCTION	Equ	21h		; MS-DOS function request interrupt
WRITE_FILE	Equ	40h		; Write file function code
;
;  Define any ASCII characters needed
;
PLUS		Equ	"+"             ; Plus sign character
MINUS		Equ	"-"             ; Minus sign character
EQUAL		Equ	"="             ; Equal sign character
ASTERISK	Equ	"*"             ; Asterisk character
POINT		Equ	"."             ; Decimal point character
NULL		Equ	00h		; ASCII code for null
BS		Equ	08h		; ASCII code for backspace
HT		Equ	09h		; ASCII code for horizontal tab
LF		Equ	0Ah		; ASCII code for line feed
VT		Equ	0Bh		; ASCII code for vertical tab
FF		Equ	0Ch		; ASCII code for form feed
CR		Equ	0Dh		; ASCII code for carriage return
;
;  Define any Macros needed
;
	Page
;******************************************************************************
;
;	Save(Regs)
;
;		While there are registers in the list (Left to right)
;			Push this register onto the stack
;		Endwhile
;
;******************************************************************************
Save		Macro	a,b,c,d,e,f,g,h,i,j,k,l,m,n,o
	Irp	x,<a,b,c,d,e,f,g,h,i,j,k,l,m,n,o>
		Ifnb	<x>
		    push    x
		Endif
	Endm
		Endm
	Page
;******************************************************************************
;
;	Restore(Regs)
;
;		While there are registers in the list (Right to left)
;			Pop this register from the stack
;		Endwhile
;
;******************************************************************************
Restore 	Macro	a,b,c,d,e,f,g,h,i,j,k,l,m,n,o
	Irp	x,<o,n,m,l,k,j,i,h,g,f,e,d,c,b,a>
		Ifnb	<x>
		    pop     x
		Endif
	Endm
		Endm
	Page
;
;  Define the standard code segment
;
Code	Segment Word Public 'CODE'      ; Define the standard code segment
	Assume	cs:Code, ds:Nothing, es:Nothing
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Printf(Format_String, Arguments, Handle)
;
;		Save the required registers
;		While next character from Format_String <> 0
;			If next character is a format character (percent sign)
;				Get the next character from Format_String
;				If a format specifier (+,-,n,l,#,&,$,*)
;					Set the appropriate specifier flag
;				Else not a format specifier
;					If a conversion character (c,s,x,d,o,b)
;						Print argument using conversion
;						Increment argument pointer
;					Else not a conversion character
;						Ignore this format
;					Endif
;				Endif
;			Else the character is not a format character
;				If character is a escape character (backslash)
;					Get next character from Format_String
;					If a valid escape sequence
;						Handle the escape sequence
;					Else an invalid escape sequence
;						Print the character to Handle
;					Endif
;				Else the character is not an escape character
;					Print the character to Handle
;				Endif
;			Endif
;		Endwhile
;		Restore the required registers
;		Return to the caller
;
;	Registers on Entry:
;
;		AL    - File Handle to Print on
;		DS:BX - Pointer to Argument List
;		DS:SI - Pointer to Format String
;
;	Registers on Exit:
;
;		FL    - DR flag is cleared (Move forward)
;
;******************************************************************************
Printf		Proc	Near		; Generic printf procedure
	Save	ax,bx,cx,dx,si,di,bp,ds,es
	mov	ah,al			; Save print handle in AH
	cld				; Clear the direction flag (Forward)
;
;  Get a character from format string and decide what to do with it
;
Get_Char:
	call	Clear_Flags		; Clear all of the specifier flags
	lodsb				; Get a character from format string
	cmp	al,FORMAT_CHAR		; Check for a format character (%)
	je	Do_Format		; Jump if a format character
	cmp	al,ESCAPE_CHAR		; Check for an escape character (\)
	je	Do_Escape		; Jump if a escape character
	or	al,al			; Check for end of the format string
	jnz	Normal_Output		; Jump if a normal character to output
	jmp	Printf_Exit		; Jump if end of the format string
;
;  It is a escape character, get the next character and check it
;
Do_Escape:
	lodsb				; Get next character from format string
	push	cs			; Put copy of CS onto the stack
	pop	es			; Put copy of current CS into ES
	lea	di,cs:[Escape_Table]	; Setup the escape character table
	mov	cx,ESCAPE_SIZE		; Get the escape character table size
	repne	scasb			; Scan the escape table for a match
	je	Go_Escape		; Jump if an there was a table match
	mov	ch,BASE_OCTAL		; Set the current base value to OCTAL
	mov	cl,ESCAPE_LENGTH	; Set the escape maximum number count
	call	Check_Digit		; Check for numeric digit (Character)
	jc	Normal_Output		; Jump if an unknown escape character
Get_Code:
	dec	si			; Backup to the start of the number
	push	dx			; Save the field width/precision
	call	Get_Number		; Call routine to get the number
	mov	al,dl			; Put the character number into AL
	pop	dx			; Restore the field width/precision
	jmp	Short Normal_Output	; Go get next format string character
Go_Escape:
	mov	di,ESCAPE_SIZE		; Get the escape table size
	sub	di,cx			; Compute the matching entry number
	dec	di			; Convert number to zero based
	shl	di,1			; Make number into jump table index
	call	cs:[di+Escape_Jump]	; Call the correct routine
	jmp	Short Get_Char		; Go get the next character
Normal_Output:
	call	Output			; Not a special character, output it
	jmp	Short Get_Char		; Go get next format string character
;
;  Not a valid format specifier, check for a conversion character
;
Do_Convert:
	lea	di,cs:[Convert_Table]	; Setup the convert character table
	mov	cx,CONVERT_SIZE 	; Get the convert character table size
	repne	scasb			; Scan the convert table for a match
	jne	Get_Char		; Jump if an unknown convert character
	mov	di,CONVERT_SIZE 	; Get the convert table size
	sub	di,cx			; Compute the matching entry number
	dec	di			; Convert number to zero based
	shl	di,1			; Make number into jump table index
	call	cs:[di+Convert_Jump]	; Call the correct routine
	jmp	Short Get_Char		; Go get the next character
;
;  It is a format character, get the next character and check it
;
Do_Format:
	lodsb				; Get next character from format string
	push	cs			; Put copy of CS onto the stack
	pop	es			; Put copy of current CS into ES
	lea	di,cs:[Format_Table]	; Setup the format character table
	mov	cx,FORMAT_SIZE		; Get the format character table size
	repne	scasb			; Scan the format table for a match
	je	Go_Format		; Jump if an there was a table match
	mov	ch,BASE_DECIMAL 	; Set the current base value to DECIMAL
	mov	cl,FORMAT_LENGTH	; Set the format maximum number count
	cmp	al,POINT		; Check for a decimal point
	jne	Chk_Var 		; Jump if no decimal point found
	dec	si			; Correct pointer for no width value
	xor	dh,dh			; Setup a width value of zero
	jmp	Short Chk_Pre		; Go check for a precision value
Chk_Var:
	cmp	al,ASTERISK		; Check for variable width field
	jne	Do_Width		; Jump if not a variable width field
	or	bp,VAR_WIDTH		; Set variable width flag bit
	mov	dh,MAX_BYTE		; Set width value as already set
	jmp	Short Chk_Pre		; Go check for a precision value
Do_Width:
	call	Check_Digit		; Check for numeric digit (Field width)
	jc	Do_Convert		; Jump if an unknown format character
	dec	si			; Backup to the start of the number
	or	al,al			; Check for a leading zero
	jnz	Get_Width		; Jump if first digit not a zero
	or	bp,PAD_CHAR		; First digit zero, use zero pad
	or	bp,PRE_PAD		; Set pre-pad sign character flag
Get_Width:
	call	Get_Number		; Call routine to get the field width
	mov	dh,dl			; Save the field width in DH
	cmp	Byte Ptr ds:[si],ASTERISK
	jne	Chk_Pre 		; Jump if not a variable width field
	inc	si			; Increment past variable character
	or	bp,VAR_WIDTH		; Set variable width flag bit
	mov	dh,MAX_BYTE		; Set width value as already set
Chk_Pre:
	xor	dl,dl			; Setup a precision of zero
	cmp	Byte Ptr ds:[si],POINT	; Check for a decimal point
	jne	Do_Format		; Jump if no precision given
	or	bp,FRACTIONAL		; Set the fractional conversion flag
	dec	dl			; Set precision as already set
	inc	si			; Increment past the decimal point
	cmp	Byte Ptr ds:[si],ASTERISK
	jne	Get_Pre 		; Jump if not a variable precision
	inc	si			; Increment past variable character
	or	bp,VAR_PRE		; Set variable precision flag bit
	jmp	Short Do_Format 	; Go check for more format characters
Get_Pre:
	call	Get_Number		; Call routine to get the precision
	adc	dl,0			; Setup correct precision value
	jmp	Short Do_Format 	; Go check for more format characters
Go_Format:
	mov	di,FORMAT_SIZE		; Get the format table size
	sub	di,cx			; Compute the matching entry number
	dec	di			; Convert number to zero based
	shl	di,1			; Make number into jump table index
	call	cs:[di+Format_Jump]	; Call the correct routine
	jmp	Short Do_Format 	; Go check for more format characters
;
;  Restore the registers and return to the caller
;
Printf_Exit:
	Restore ax,bx,cx,dx,si,di,bp,ds,es
	ret				; Return to the caller
	Page
;
;  Define the format specifier character and jump tables
;
Format_Table	Label	Byte
		Db	'-'             ; Left justify format specifier
		Db	'+'             ; Set signed specifier
		Db	't'             ; Short format specifier
		Db	'T'             ; Short format specifier
		Db	'l'             ; Long format specifier
		Db	'L'             ; Long format specifier
		Db	'#'             ; Far format specifier
		Db	'&'             ; Indirect format specifier
		Db	'$'             ; Immediate format specifier
FORMAT_SIZE	Equ	This Byte - Format_Table
Format_Jump	Label	Word
		Dw	Left_Justify
		Dw	Set_Signed
		Dw	Short_Specify
		Dw	Short_Specify
		Dw	Long_Specify
		Dw	Long_Specify
		Dw	Far_Specify
		Dw	Set_Indirect
		Dw	Set_Immediate
;
;  Define the escape character and jump tables
;
Escape_Table	Label	Byte
		Db	'n'             ; Newline escape character
		Db	't'             ; Horizontal tab escape character
		Db	'v'             ; Vertical tab escape character
		Db	'b'             ; Backspace escape character
		Db	'r'             ; Carriage return escape character
		Db	'f'             ; Form feed escape character
		Db	'x'             ; Output character (Hex representation)
ESCAPE_SIZE	Equ	This Byte - Escape_Table
Escape_Jump	Label	Word
		Dw	New_Line
		Dw	Horz_Tab
		Dw	Vert_Tab
		Dw	Back_Space
		Dw	Carr_Ret
		Dw	Form_Feed
		Dw	Out_Hex
;
;  Define the convert character and jump tables
;
Convert_Table	Label	Byte
		Db	'%'             ; Print the percent sign
		Db	'c'             ; Print next argument as a character
		Db	'C'             ; Print next argument as a character
		Db	's'             ; Print next argument as a string
		Db	'S'             ; Print next argument as a string
		Db	'x'             ; Print next argument as HEX (abcdef)
		Db	'X'             ; Print next argument as HEX (ABCDEF)
		Db	'h'             ; Print next argument as HEX (abcdef)
		Db	'H'             ; Print next argument as HEX (ABCDEF)
		Db	'd'             ; Print next argument as DECIMAL (+/-)
		Db	'D'             ; Print next argument as DECIMAL (+/-)
		Db	'u'             ; Print next argument as UNSIGNED
		Db	'U'             ; Print next argument as UNSIGNED
		Db	'o'             ; Print next argument as OCTAL
		Db	'O'             ; Print next argument as OCTAL
		Db	'b'             ; Print next argument as BINARY
		Db	'B'             ; Print next argument as BINARY
		Db	'f'             ; Print next argument as FRACTIONAL
		Db	'F'             ; Print next argument as FRACTIONAL
CONVERT_SIZE	Equ	This Byte - Convert_Table
Convert_Jump	Label	Word
		Dw	Print_Format	; Print format routine
		Dw	Do_Char 	; Print character routine
		Dw	Do_Char 	; Print character routine
		Dw	Do_String	; Print string routine
		Dw	Do_String	; Print string routine
		Dw	Do_Hex_Lower	; Print lowercase hexadecimal routine
		Dw	Do_Hex_Upper	; Print uppercase hexadecimal routine
		Dw	Do_Hex_Lower	; Print lowercase hexadecimal routine
		Dw	Do_Hex_Upper	; Print uppercase hexadecimal routine
		Dw	Do_Decimal	; Print signed decimal routine
		Dw	Do_Decimal	; Print signed decimal routine
		Dw	Do_Unsigned	; Print unsigned decimal routine
		Dw	Do_Unsigned	; Print unsigned decimal routine
		Dw	Do_Octal	; Print octal routine
		Dw	Do_Octal	; Print octal routine
		Dw	Do_Binary	; Print binary routine
		Dw	Do_Binary	; Print binary routine
		Dw	Do_Fractional	; Print decimal fractional routine
		Dw	Do_Fractional	; Print decimal fractional routine
;
;  Define the end of the printf procedure
;
Printf		Endp			 ; End of the Printf procedure
	Page
;******************************************************************************
;
;	Format Specificer Routines
;
;
;		These routines handle the following format specifiers:
;
;
;	Specifier		Action Taken
;	---------		------------
;
;	    -		The following field will be left justified.
;
;	    +		The following field will be have a sign (+/-)
;			if it is a signed type field (d).
;
;	    t		The following field is a short value.
;
;	    T		The following field is a short value.
;
;	    l		The following field is a long value.
;
;	    L		The following field is a long value.
;
;	    #		The following argument has a far address.
;
;	    &		The following argument has an indirect address.
;
;	    $		The following argument is an immediate value.
;
;		These routines simply set or reset flags which are
;	used later during actual conversion to perform the special
;	formatting options.
;
;******************************************************************************
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Left_Justify()
;
;		Set the left justification flag
;		Return to the caller
;
;	Registers on Entry:
;
;		None
;
;	Registers on Exit:
;
;		BP    - Left justification flag set
;
;******************************************************************************
Left_Justify	Proc	Near		; Left justify procedure
	or	bp,LEFT_JUST		; Set the left justification flag
	ret				; Return to the caller
Left_Justify	Endp			; End of the Left_Justify procedure
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Set_Signed()
;
;		Set the signed flag
;		If the field width is non-zero
;			Set the pre-pad sign flag
;		Endif
;		Return to the caller
;
;	Registers on Entry:
;
;		DH    - Field width (Not given if zero)
;
;	Registers on Exit:
;
;		BP    - Signed flag set
;
;******************************************************************************
Set_Signed	Proc	Near		; Set signed procedure
	or	bp,SIGNED_CONV		; Set the signed conversion flag
	or	dh,dh			; Check the field width
	jz	Sign_Ret		; Jump if field width not set
	or	bp,PRE_PAD		; Set the pre-pad sign flag
Sign_Ret:
	ret				; Return to the caller
Set_Signed	Endp			; End of the Set_Signed procedure
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Short_Specify()
;
;		Set the short specification flag
;		Return to the caller
;
;	Registers on Entry:
;
;		None
;
;	Registers on Exit:
;
;		BP    - Short specification flag set
;
;******************************************************************************
Short_Specify	Proc	Near		; Short specify procedure
	or	bp,SHORT_SPEC		; Set the short specification flag
	ret				; Return to the caller
Short_Specify	Endp			; End of the Short_Specify procedure
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Long_Specify()
;
;		Set the long specification flag
;		Return to the caller
;
;	Registers on Entry:
;
;		None
;
;	Registers on Exit:
;
;		BP    - Long specification flag set
;
;******************************************************************************
Long_Specify	Proc	Near		; Long specify procedure
	or	bp,LONG_SPEC		; Set the long specification flag
	ret				; Return to the caller
Long_Specify	Endp			; End of the Long_Specify procedure
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Far_Specify()
;
;		Set the far specification flag
;		Return to the caller
;
;	Registers on Entry:
;
;		None
;
;	Registers on Exit:
;
;		BP    - Far specification flag set
;
;******************************************************************************
Far_Specify	Proc	Near		; Far specify procedure
	or	bp,FAR_SPEC		; Set the far specification flag
	ret				; Return to the caller
Far_Specify	Endp			; End of the Far_Specify procedure
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Set_Indirect()
;
;		Set the indirect flag
;		Return to the caller
;
;	Registers on Entry:
;
;		None
;
;	Registers on Exit:
;
;		BP    - Indirection flag set
;
;******************************************************************************
Set_Indirect	Proc	Near		; Set indirect procedure
	or	bp,INDIRECT		; Set the indirection flag
	ret				; Return to the caller
Set_Indirect	Endp			; End of the Set_Indirect procedure
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Set_Immediate()
;
;		Set the immediate flag
;		Return to the caller
;
;	Registers on Entry:
;
;		None
;
;	Registers on Exit:
;
;		BP    - Immediate flag set
;
;******************************************************************************
Set_Immediate	Proc	Near		; Set immediate procedure
	or	bp,IMMEDIATE		; Set the immediate flag
	ret				; Return to the caller
Set_Immediate	Endp			; End of the Set_Immediate procedure
	Page
;******************************************************************************
;
;	Escape Sequence Routines
;
;
;		These routines handle the following escape sequences:
;
;
;	Character		Escape Sequence
;	---------		---------------
;
;	    n		Newline is output to requested handle
;
;	    t		Horizontal tab is output to requested handle
;
;	    v		Vertical tab is output to requested handle
;
;	    b		Backspace is output to requested handle
;
;	    r		Carriage return is output to requested handle
;
;	    f		Form feed is output to requested handle
;
;	    x		Character is output to requested handle (Hex code)
;
;
;		All of these routines except for the "x" escape
;	sequence simply send the desired character(s) out to the
;	requested handle. The "x" routine get the hexadecimal
;	number given and outputs the corresponding character to
;	the requested handle.
;
;******************************************************************************
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	New_Line(Handle)
;
;		Output carriage return to handle
;		Output line feed to handle
;		Return to the caller
;
;	Registers on Entry:
;
;		AH    - Handle
;
;	Registers on Exit:
;
;		AL    - Destroyed
;
;******************************************************************************
New_Line	Proc	Near		; Output new line procedure
	mov	al,CR			; Get carriage return ASCII character
	call	Output			; Output the carriage return to handle
	mov	al,LF			; Get a line feed ASCII character
	call	Output			; Output the line feed to handle
	ret				; Return to the caller
New_Line	Endp			; End of the New_Line procedure
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Horz_Tab(Handle)
;
;		Output horizontal tab to handle
;		Return to the caller
;
;	Registers on Entry:
;
;		AH    - Handle
;
;	Registers on Exit:
;
;		AL    - Destroyed
;
;******************************************************************************
Horz_Tab	Proc	Near		; Output horizontal tab procedure
	mov	al,HT			; Get horizontal tab ASCII character
	call	Output			; Output the horizontal tab to handle
	ret				; Return to the caller
Horz_Tab	Endp			; End of the Horz_Tab procedure
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Vert_Tab(Handle)
;
;		Output vertical tab to handle
;		Return to the caller
;
;	Registers on Entry:
;
;		AH    - Handle
;
;	Registers on Exit:
;
;		AL    - Destroyed
;
;******************************************************************************
Vert_Tab	Proc	Near		; Output vertical tab procedure
	mov	al,VT			; Get vertical tab ASCII character
	call	Output			; Output the vertical tab to handle
	ret				; Return to the caller
Vert_Tab	Endp			; End of the Vert_Tab procedure
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Back_Space(Handle)
;
;		Output backspace to handle
;		Return to the caller
;
;	Registers on Entry:
;
;		AH    - Handle
;
;	Registers on Exit:
;
;		AL    - Destroyed
;
;******************************************************************************
Back_Space	Proc	Near		; Output backspace procedure
	mov	al,BS			; Get backspace ASCII character
	call	Output			; Output the backspace to handle
	ret				; Return to the caller
Back_Space	Endp			; End of the Back_Space procedure
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Carr_Ret(Handle)
;
;		Output carriage return to handle
;		Return to the caller
;
;	Registers on Entry:
;
;		AH    - Handle
;
;	Registers on Exit:
;
;		AL    - Destroyed
;
;******************************************************************************
Carr_Ret	Proc	Near		; Output carriage return procedure
	mov	al,CR			; Get carriage return ASCII character
	call	Output			; Output the carriage return to handle
	ret				; Return to the caller
Carr_Ret	Endp			; End of the Carr_Ret procedure
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Form_Feed(Handle)
;
;		Output form feed to handle
;		Return to the caller
;
;	Registers on Entry:
;
;		AH    - Handle
;
;	Registers on Exit:
;
;		AL    - Destroyed
;
;******************************************************************************
Form_Feed	Proc	Near		; Output form feed procedure
	mov	al,FF			; Get form feed ASCII character
	call	Output			; Output the form feed to handle
	ret				; Return to the caller
Form_Feed	Endp			; End of the Form_Feed procedure
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Out_Hex(String, Handle)
;
;		Set number base to hexadecimal
;		Set maximum number length
;		Call routine to get the number
;		Output the number (Character) to handle
;		Return to the caller
;
;	Registers on Entry:
;
;		AH    - Handle
;		DS:SI - Pointer to number
;
;	Registers on Exit:
;
;		AL    - Destroyed
;		CX    - Destroyed
;		DL    - Destroyed
;		DS:SI - Pointer set to first character past number
;
;******************************************************************************
Out_Hex 	Proc	Near		; Output hex character procedure
	mov	ch,BASE_HEX		; Set number base to hexadecimal
	mov	cl,HEX_LENGTH		; Set maximum hex digit length
	call	Get_Number		; Call routine to get the number
	jc	Out_Exit		; Jump if no number present
	mov	al,dl			; Move the number value into AL
	call	Output			; Output the corresponding character
Out_Exit:
	ret				; Return to the caller
Out_Hex 	Endp			; End of the Out_Hex procedure
	Page
;******************************************************************************
;
;	Conversion Formatting Routines
;
;
;		These routines handle the following conversion types:
;
;
;	Character		Conversion Done
;	---------		---------------
;
;	    c		Convert next argument as a character
;
;	    s		Convert next argument as a string
;
;	    x		Convert next argument as a hex number using abcdef
;
;	    X		Convert next argument as a hex number using ABCDEF
;
;	    d		Convert next argument as a decimal number (Signed)
;
;	    u		Convert next argument as a decimal number (Unsigned)
;
;	    o		Convert next argument as a octal number
;
;	    b		Convert next argument as a binary number
;
;
;		These routines format the arguments passed to them.
;	Numeric arguments can be either word or double word (long)
;	values and for the decimal option it can be formatted as
;	signed or unsigned. All arguments are passed as pointers,
;	either near or far, to the actual argument value.
;
;******************************************************************************
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Do_Char(Argument, Handle, Flags)
;
;		Save the required registers
;		Call routine to check for variable width/precision
;		Call routine to get the character address
;		If field width is not set (Zero)
;			Set field width to character value (1)
;		Endif
;		If character length (1) > field width
;			Set the field overflow flag
;			Set character length to field width
;		Endif
;		Set pad character to a space
;		Call routine to calculate pad counts
;		Call routine to output pre-string pad characters
;		Get character to output
;		Call routine to output character to handle
;		Call routine to output post-string pad characters
;		Restore the required registers
;		Return to the caller
;
;	Registers on Entry:
;
;		AH    - Handle
;		DS:BX - Pointer to argument
;		DH    - Current field width
;		BP    - Formatting flags
;
;	Registers on Exit:
;
;		AL    - Destroyed
;		BX    - Points to next argument
;		CX    - Destroyed
;		DL    - Destroyed
;		DI    - Destroyed
;		ES    - Destroyed
;
;******************************************************************************
Do_Char 	Proc	Near		; Character formatting procedure
	Save	si,ds			; Save the required registers
	call	Variable		; Call routine to check width/precision
	call	Get_Address		; Call routine to get argument address
	mov	cl,1			; Set actual width to character value
	or	dh,dh			; Check the current field width
	jnz	Width_Chk		; Jump if field width specified
	mov	dh,1			; Set field width to character length
Width_Chk:
	cmp	cl,dh			; Compare actual width to given width
	jbe	Send_Pre		; Jump if string fits in field width
	or	bp,OVER_FLOW		; Set the field overflow flag
	mov	cl,dh			; Clip string to the field width
Send_Pre:
	mov	al,cl			; Save the actual string length
	and	bp,Not PAD_CHAR 	; Set current pad character to a space
	call	Calculate		; Call routine to calculate pad values
	call	Pad			; Call routine to send pad characters
Send_Char:
	lodsb				; Get the character to output
	call	Output			; Call routine to output the character
Send_Post:
	mov	cl,ch			; Get the calculated pad counts
	call	Pad			; Call routine to send pad characters
	Restore si,ds			; Restore the required registers
	ret				; Return to the caller
Do_Char 	Endp			; End of the Do_Char routine
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Do_String(Argument, Handle, Width, Flags)
;
;		Save the required registers
;		Call routine to check for variable width/precision
;		Call routine to get the string address
;		Call routine to compute string length
;		If field width is not set (Zero)
;			Set field width to string length
;		Endif
;		If string length > field width
;			Set the field overflow flag
;			Set string length to field width
;		Endif
;		Set pad character to a space
;		Call routine to calculate pad counts
;		Call routine to output pre-string pad characters
;		Output the string characters
;		Call routine to output post-string pad characters
;		Restore the required registers
;		Return to the caller
;
;	Registers on Entry:
;
;		AH    - Handle
;		DS:BX - Pointer to argument
;		DH    - Current field width
;		BP    - Formatting flags
;
;	Registers on Exit:
;
;		AL    - Destroyed
;		BX    - Points to next argument
;		CX    - Destroyed
;		DL    - Destroyed
;		DI    - Destroyed
;		ES    - Destroyed
;
;******************************************************************************
Do_String	Proc	Near		; String formatting procedure
	Save	si,ds			; Save the required registers
	call	Variable		; Call routine to check width/precision
	call	Get_Address		; Call routine to get argument address
	call	Get_Length		; Call routine to get string length
	jz	String_Exit		; Jump if nothing to output
	or	dh,dh			; Check the current field width
	jnz	Chk_Width		; Jump if field width specified
	mov	dh,cl			; Set field width to string length
Chk_Width:
	cmp	cl,dh			; Compare actual width to given width
	jbe	Do_Pre			; Jump if string fits in field width
	or	bp,OVER_FLOW		; Set the field overflow flag
	mov	cl,dh			; Clip string to the field width
Do_Pre:
	mov	al,cl			; Save the actual string length
	and	bp,Not PAD_CHAR 	; Set current pad character to a space
	call	Calculate		; Call routine to calculate pad values
	mov	dl,al			; Setup the string output length
	call	Pad			; Call routine to send pad characters
Send_String:
	Save	ax,bx,cx,dx		; Save the required registers
	mov	cl,dl			; Get the computed string length
	xor	ch,ch			; Convert string length to a full word
	mov	dx,si			; Get pointer to the character string
	mov	bl,ah			; Get the file handle value
	xor	bh,bh			; Convert file handle to a full word
	mov	ah,WRITE_FILE		; Get write file function code
	int	DOS_FUNCTION		; Try to write the string to the file
	Restore ax,bx,cx,dx		; Restore the required registers
Do_Post:
	mov	cl,ch			; Get the calculated pad counts
	call	Pad			; Call routine to send pad characters
String_Exit:
	Restore si,ds			; Restore the required registers
	ret				; Return to the caller
Do_String	Endp			; End of the Do_String routine
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Do_Hex(Argument, Handle, Width, Precision, Flags, Type)
;
;		Save the required registers
;		If type is uppercase
;			Set the uppercase format flag
;		Endif
;		Call routine to check for variable width/precision
;		Set current number base to hexadecimal
;		Call routine to get the argument address
;		Call routine to output the numeric string
;		Restore the required registers
;		Return to the caller
;
;	Registers on Entry:
;
;		AH    - Handle
;		DS:BX - Pointer to argument
;		DH    - Current field width
;		DL    - Current field precision
;		BP    - Formatting flags
;
;	Registers on Exit:
;
;		BX    - Points to next argument
;
;******************************************************************************
Do_Hex		Proc	Near		; Hexadecimal formatting procedure
Do_Hex_Upper	Label	Near		; Do_Hex_Upper entry point (ABCDEF)
	or	bp,UPPER_CASE		; Set uppercase formatting flag
Do_Hex_Lower	Label	Near		; Do_Hex_Lower entry point (abcdef)
	Save	si,ds			; Save the required registers
	call	Variable		; Call routine to check width/precision
	mov	ch,BASE_HEX		; Set the current number base to hex
	call	Get_Address		; Call routine to get argument address
	call	Compute 		; Call routine to output number
	Restore si,ds			; Restore the required registers
	ret				; Return to the caller
Do_Hex		Endp			; End of the Do_Hex routine
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Do_Decimal(Argument, Handle, Width, Precision, Flags)
;
;		Save the required registers
;		Call routine to check for variable width/precision
;		Set the signed type formatting flag
;		Set current number base to decimal
;		Call routine to get the argument address
;		Call routine to output the numeric string
;		Restore the required registers
;		Return to the caller
;
;	Registers on Entry:
;
;		AH    - Handle
;		DS:BX - Pointer to argument
;		DH    - Current field width
;		DL    - Current field precision
;		BP    - Formatting flags
;
;	Registers on Exit:
;
;		BX    - Points to next argument
;
;******************************************************************************
Do_Decimal	Proc	Near		; Decimal formatting procedure
	Save	si,ds			; Save the required registers
	call	Variable		; Call routine to check width/precision
	or	bp,SIGNED_TYPE		; Set signed type formatting flag
	mov	ch,BASE_DECIMAL 	; Set current number base to decimal
	call	Get_Address		; Call routine to get argument address
	call	Compute 		; Call routine to output number
	Restore si,ds			; Restore the required registers
	ret				; Return to the caller
Do_Decimal	Endp			; End of the Do_Decimal routine
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Do_Unsigned(Argument, Handle, Width, Precision, Flags)
;
;		Save the required registers
;		Call routine to check for variable width/precision
;		Set current number base to decimal
;		Call routine to get the argument address
;		Call routine to output the numeric string
;		Restore the required registers
;		Return to the caller
;
;	Registers on Entry:
;
;		AH    - Handle
;		DS:BX - Pointer to argument
;		DH    - Current field width
;		DL    - Current field precision
;		BP    - Formatting flags
;
;	Registers on Exit:
;
;		BX    - Points to next argument
;
;******************************************************************************
Do_Unsigned	Proc	Near		; Unsigned decimal formatting procedure
	Save	si,ds			; Save the required registers
	call	Variable		; Call routine to check width/precision
	mov	ch,BASE_DECIMAL 	; Set current number base to decimal
	call	Get_Address		; Call routine to get argument address
	call	Compute 		; Call routine to output number
	Restore si,ds			; Restore the required registers
	ret				; Return to the caller
Do_Unsigned	Endp			; End of the Do_Unsigned routine
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Do_Octal(Argument, Handle, Width, Precision, Flags)
;
;		Save the required registers
;		Call routine to check for variable width/precision
;		Set current number base to octal
;		Call routine to get the argument address
;		Call routine to output the numeric string
;		Restore the required registers
;		Return to the caller
;
;	Registers on Entry:
;
;		AH    - Handle
;		DS:BX - Pointer to argument
;		DH    - Current field width
;		DL    - Current field precision
;		BP    - Formatting flags
;
;	Registers on Exit:
;
;		BX    - Points to next argument
;
;******************************************************************************
Do_Octal	Proc	Near		; Octal formatting procedure
	Save	si,ds			; Save the required registers
	call	Variable		; Call routine to check width/precision
	mov	ch,BASE_OCTAL		; Set current number base to octal
	call	Get_Address		; Call routine to get argument address
	call	Compute 		; Call routine to output number
	Restore si,ds			; Restore the required registers
	ret				; Return to the caller
Do_Octal	Endp			; End of the Do_Octal routine
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Do_Binary(Argument, Handle, Width, Precision, Flags)
;
;		Save the required registers
;		Call routine to check for variable width/precision
;		Set current number base to binary
;		Call routine to get the argument address
;		Call routine to output the numeric string
;		Retore the required registers
;		Return to the caller
;
;	Registers on Entry:
;
;		AH    - Handle
;		DS:BX - Pointer to argument
;		DH    - Current field width
;		DL    - Current field precision
;		BP    - Formatting flags
;
;	Registers on Exit:
;
;		BX    - Points to next argument
;
;******************************************************************************
Do_Binary	Proc	Near		; Binary formatting procedure
	Save	si,ds			; Save the required registers
	call	Variable		; Call routine to check width/precision
	mov	ch,BASE_BINARY		; Set current number base to binary
	call	Get_Address		; Call routine to get argument address
	call	Compute 		; Call routine to output number
	Restore si,ds			; Restore the required registers
	ret				; Return to the caller
Do_Binary	Endp			; End of the Do_Binary routine
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Do_Fractional(Argument, Handle, Width, Precision, Flags)
;
;		Save the required registers
;		Call routine to check for variable width/precision
;		Set the signed type formatting flag
;		Set current number base to decimal
;		Call routine to get the argument address
;		Call routine to output the numeric string
;		Restore the required registers
;		Return to the caller
;
;	Registers on Entry:
;
;		AH    - Handle
;		DS:BX - Pointer to argument
;		DH    - Current field width
;		DL    - Current field precision
;		BP    - Formatting flags
;
;	Registers on Exit:
;
;		BX    - Points to next argument
;
;******************************************************************************
Do_Fractional	Proc	Near		; Fractional formatting procedure
	Save	si,ds			; Save the required registers
	call	Variable		; Call routine to check width/precision
	or	bp,SIGNED_TYPE		; Set signed type formatting flag
	or	bp,FRACTIONAL		; Set the fractional formatting flag
	mov	ch,BASE_DECIMAL 	; Set current number base to decimal
	call	Get_Address		; Call routine to get argument address
	call	Compute 		; Call routine to output number
	Restore si,ds			; Restore the required registers
	ret				; Return to the caller
Do_Fractional	Endp			; End of the Do_Fractional routine
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Get_Address(Argument, Flags)
;
;		Save the required registers
;		If immediate specifier has been set
;			Set DS:SI to current value of DS:BX
;			Increment BX to next argument (1,2,4)
;		Else no immediate specifier
;			If far format specifier has been set
;				Set DS:SI to far pointer at DS:BX
;				If indirect format specifier has been set
;					Set DS:SI to far pointer at DS:SI
;				Endif
;				Increment BX to next argument (4)
;			Else no far specifier
;				Set SI to near pointer at DS:BX
;				If indirect format specifier has been set
;					Set SI to near pointer at DS:SI
;				Endif
;				Increment BX to next argument (2)
;			Endif
;		Endif
;		Restore the required registers
;		Return to the caller
;
;	Registers on Entry:
;
;		DS:BX - Pointer to argument list
;		BP    - Formatting flags
;
;	Registers on Exit:
;
;		DS:SI - New address pointer (Character or string)
;		BX    - Points to the next argument
;
;******************************************************************************
Get_Address	Proc	Near		; Get address procedure
	Save	ax			; Save the required registers
	test	bp,IMMEDIATE		; Check for immediate specifier set
	jz	Check_Specifier 	; Jump if no immediate specifier
	mov	si,bx			; Setup the address into DS:SI
	mov	ax,NORMAL_SIZE		; Default to normal size argument
Check_Short:
	test	bp,SHORT_SPEC		; Check for a short immediate argument
	jz	Check_Long		; Jump if not a short argument
	mov	ax,SHORT_SIZE		; Setup to short size argument
	jmp	Short Immediate_Fixup	; Go perform the address fixup
Check_Long:
	test	bp,LONG_SPEC		; Check for a long immeidate argument
	jz	Immediate_Fixup 	; Jump if not a long argument
	mov	ax,LONG_SIZE		; Setup to long size argument
Immediate_Fixup:
	add	bx,ax			; Update the argument pointer value
	jmp	Short Get_Exit		; Go return control to the caller
Check_Specifier:
	test	bp,FAR_SPEC		; Check for far specifier set
	jz	Near_Addr		; Jump if a normal near address
Far_Addr:
	lds	si,Dword Ptr ds:[bx]	; Load the far address into DS:SI
	test	bp,INDIRECT		; Check for indirect address
	jz	Far_Fixup		; Jump if no indirection specified
	lds	si,Dword Ptr ds:[si]	; Get the indirect far address
Far_Fixup:
	add	bx,FAR_SIZE		; Update the argument pointer value
	jmp	Short Get_Exit		; Go return to the caller
Near_Addr:
	mov	si,Word Ptr ds:[bx]	; Load the near address into SI
	test	bp,INDIRECT		; Check for indirect address
	jz	Near_Fixup		; Jump if no indirection specified
	mov	si,Word Ptr ds:[si]	; Get the indirect near address
Near_Fixup:
	add	bx,NEAR_SIZE		; Update the argument pointer value
Get_Exit:
	Restore ax			; Restore the required registers
	ret				; Return to the caller
Get_Address	Endp			; End of the Get_Address procedure
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Variable(Width, Precision, Flags)
;
;		Save the required registers
;		If variable width specified
;			Get the actual width value (Byte)
;		Endif
;		If variable precision specified
;			Get the actual precision value (Byte)
;		Endif
;		Restore the required registers
;		Return to the caller
;
;	Registers on Entry:
;
;		DS:BX - Pointer to argument list
;		BP    - Formatting flags
;
;	Registers on Exit:
;
;		DH    - Actual width value
;		DL    - Actual precision value
;		BX    - Points to the next argument
;
;******************************************************************************
Variable	Proc	Near		; Get variable width/precision procedure
	Save	si			; Save the required registers
	test	bp,VAR_WIDTH		; Check for a variable width value
	jz	Pre_Chk 		; Jump if no variable width
	mov	si,Word Ptr ds:[bx]	; Load the near address into SI
	add	bx,NEAR_SIZE		; Update the argument pointer value
	mov	dh,Byte Ptr ds:[si]	; Get the actual field width value
Pre_Chk:
	test	bp,VAR_PRE		; Check for a variable precision value
	jz	Var_Exit		; Jump if no variable precision
	mov	si,Word Ptr ds:[bx]	; Load the near address into SI
	add	bx,NEAR_SIZE		; Update the argument pointer value
	mov	dl,Byte Ptr ds:[si]	; Get the actual precision value
Var_Exit:
	Restore si			; Restore the required registers
	ret				; Return to the caller
Variable	Endp			; End of the Variable procedure
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Get_Length(String)
;
;		Calculate the length of the string (Null terminator)
;		Return to the caller
;
;	Registers on Entry:
;
;		DS:SI - Pointer to string
;
;	Registers on Exit:
;
;		AL    - Destroyed
;		CX    - String length
;		DI    - Destroyed
;		ES    - Destroyed
;		ZR    - Zero set if zero length
;
;******************************************************************************
Get_Length	Proc	Near		; Get string length procedure
	push	ds			; Put a copy of DS onto the stack
	pop	es			; Set ES to the current DS value
	mov	di,si			; Set DI to the current SI value
	mov	al,NULL 		; Setup to scan for null terminator
	mov	cx,MAX_WORD		; Setup to scan for maximum length
	repne	scasb			; Scan for the string terminator
	not	cx			; Correct the count value
	dec	cx			; Adjust to get actual string length
	ret				; Return to the caller
Get_Length	Endp			; End of the Get_Length routine
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Calculate(Width, Length)
;
;		Save the required registers
;		Calculate total pad length
;		If total pad length > 0
;			If left justification is not requested
;				Set pre pad count to total
;				Zero post pad count
;			Else left justification requested
;				Set post pad count to total
;				Zero pre pad count
;			Endif
;		Else total pad length < 0
;			Set pre pad count to zero
;			Set post pad count to zero
;		Endif
;		Restore the required registers
;		Return to the caller
;
;	Registers on Entry:
;
;		CL    - Length of the string
;		DH    - Field width
;		BP    - Formatting flags
;
;	Registers on Exit:
;
;		CH    - Post string pad count
;		CL    - Pre string pad count
;
;******************************************************************************
Calculate	Proc	Near		; Calculate pad length procedure
	Save	ax			; Save the required registers
	mov	al,dh			; Get the current field width
	mov	ah,cl			; Get the length of the output string
	xor	cx,cx			; Default pre/post pad counts to zero
	sub	al,ah			; Compute the total pad count
	jbe	Calc_Exit		; Jump if no pad necessary
	mov	cl,al			; Default to right justification
	test	bp,LEFT_JUST		; Check if left justify was specified
	jz	Calc_Exit		; Jump if no left justification
	mov	ch,cl			; Make post pad count the total count
	xor	cl,cl			; Zero the pre pad count value
Calc_Exit:
	Restore ax			; Restore the required registers
	ret				; Return to the caller
Calculate	Endp			; End of the Calculate procedure
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Pad(Handle, Pad, Count)
;
;		Save the required registers
;		While count > 0
;			Output the current pad character
;			Decrement the count
;		Endwhile
;		Restore the required registers
;		Return to the caller
;
;	Registers on Entry:
;
;		AH    - Handle
;		CL    - Pad count
;
;	Registers on Exit:
;
;		CL    - Destroyed
;
;******************************************************************************
Pad		Proc	Near		; Pad character procedure
	Save	ax			; Save the required registers
	or	cl,cl			; Check for no padding required
	jz	Pad_Exit		; Jump if no padding is required
	mov	al,SPACE_PAD		; Default to a space pad character
	test	bp,PAD_CHAR		; Check for a zero pad character
	jz	Pad_Loop		; Jump if using a space pad character
	mov	al,ZERO_PAD		; Setup to use a zero pad character
Pad_Loop:
	call	Output			; Call routine to output pad character
	dec	cl			; Decrement the pad count
	jnz	Pad_Loop		; Jump if more pad characters to send
Pad_Exit:
	Restore ax			; Restore the required registers
	ret				; Return to the caller
Pad		Endp			; End of the Pad procedure
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Clear_Flags(Flags)
;
;		Clear the formatting flags
;		Default to space padding
;		Zero the current field width
;		Clear direction flag (All string operations forward)
;		Return to the caller
;
;	Registers on Entry:
;
;		None
;
;	Registers on Exit:
;
;		BP    - Destroyed (BP contains the flags and is zeroed)
;		DH    - Set to zero (Current field width)
;		DL    - Set to zero (Current field precision)
;
;******************************************************************************
Clear_Flags	Proc	Near		; Clear formatting flags procedure
	xor	bp,bp			; Clear all of the formatting flags
	xor	dh,dh			; Zero the current field width
	xor	dl,dl			; Zero the current field precision
	cld				; Clear the direction flag
	ret				; Return to the caller
Clear_Flags	Endp			; End of the Clear_Flags procedure
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Print_Format(Handle)
;
;		Save the required registers
;		Output format specification character to handle
;		Restore the required registers
;		Return to the caller
;
;	Registers on Entry:
;
;		AH    - Handle
;
;	Registers on Exit:
;
;		AL    - Destroyed
;
;******************************************************************************
Print_Format	Proc	Near		; Output format specifier procedure
	mov	al,FORMAT_CHAR		; Get format specifier character
	call	Output			; Output the format specifier to handle
	ret				; Return to the caller
Print_Format	Endp			; End of the Print_Format procedure
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Check_Digit(Base, Character)
;
;		Save the required registers
;		Call routine to convert character to binary
;		If the character can be converted
;			If value is greater than base
;				Set carry flag (Character not a digit)
;			Endif
;		Else character cannot be converted
;			Set carry flag (Character not a digit)
;		Endif
;		Restore the required registers
;		Return to the caller
;
;	Registers on Entry:
;
;		AL    - Digit to check (ASCII)
;		CH    - Number system base
;
;	Registers on Exit:
;
;		AL    - Binary value of digit (If it is a digit)
;		FL    - CY set if character is not a digit in current base
;
;******************************************************************************
Check_Digit	Proc	Near		; Check digit procedure
	Save	bx			; Save the required registers
	Save	ax			; Save the original digit value
	call	Convert 		; Call routine to convert character
	mov	bl,al			; Save converted value in BL
	Restore ax			; Restore the original digit value
	jc	Check_Exit		; Jump if could not be converted
	cmp	bl,ch			; Check against current number base
	cmc				; Set correct carry flag state
	jc	Check_Exit		; Jump if not valid for this base
	mov	al,bl			; Digit is valid, save binary value
Check_Exit:
	Restore bx			; Restore the required registers
	ret				; Return to the caller
Check_Digit	Endp			; End of the Check_Digit procedure
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Convert(Character)
;
;		If character is a decimal digit (0 to 9)
;			Convert ASCII digit to binary (Subtract 30h)
;			Clear the carry flag (Character could be converted)
;		Else character is not a decimal digit
;			Convert character to uppercase
;			If character is a hex digit (A to F)
;				Convert ASCII digit to binary (Subtract 37h)
;				Clear carry flag (Character could be converted)
;			Else character is not a hex digit
;				Set carry flag (Could not be converted)
;			Endif
;		Endif
;		Return to the caller
;
;	Registers on Entry:
;
;		AL    - Digit to check (ASCII)
;		CH    - Number system base
;
;	Registers on Exit:
;
;		AL    - Binary value of digit (If it is a digit)
;		FL    - CY set if character is not a digit in current base
;
;******************************************************************************
Convert 	Proc	Near		; Convert character procedure
	sub	al,DECIMAL_ADJUST	; Adjust character for ASCII decimal
	jc	Convert_Exit		; Jump if below decimal limit
	cmp	al,BASE_DECIMAL 	; Check for a valid decimal character
	cmc				; Set carry flag to correct state
	jnc	Convert_Exit		; Jump if there is a valid digit
	and	al,UPPER_MASK		; Convert anything else to uppercase
	sub	al,HEX_ADJUST		; Adjust character for ASCII hexadecimal
	cmp	al,BASE_DECIMAL 	; Check for a valid hex character
	jc	Convert_Exit		; Jump if below hexadecimal limit
	cmp	al,BASE_HEX		; Check against upper hex limit
	cmc				; Set carry flag to correct state
Convert_Exit:
	ret				; Return to caller with value and flag
Convert 	Endp			; End of the Convert routine
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Output(Character, Handle)
;
;		Save the required registers
;		Output the character to requested handle
;		Restore the required registers
;		Return to the caller
;
;	Registers on Entry:
;
;		AL    - Character to output
;		AH    - Handle (For output)
;
;	Registers on Exit:
;
;		None
;
;******************************************************************************
Output		Proc	Near		; Output character procedure
	Save	bx,cx,dx,ds		; Save the required registers
	push	ss			; Put a copy of SS onto the stack
	pop	ds			; Setup DS to the current SS value
	Save	ax			; Save the character and handle values
	mov	dx,sp			; Setup buffer pointer into stack
	xchg	al,ah			; Put requested handle into AL
	cbw				; Convert handle to full word
	mov	bx,ax			; Move handle number to BX
	mov	cx,1			; Setup to write one character
	mov	ah,WRITE_FILE		; Get the write file function code
	int	DOS_FUNCTION		; Attempt to write the character
	Restore ax			; Restore the character/handle values
	Restore bx,cx,dx,ds		; Restore required registers
	ret				; Return to the caller
Output		Endp			; End of the Output procedure
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Get_Number(String, Base, Length)
;
;		Save the required registers
;		While length > 0
;			Decrement the length
;			Get the next character from string
;			Call routine to check for a valid digit
;			If character is a valid digit
;				Add new value into total
;			Endif
;		Endwhile
;		Decrement pointer back to last character
;		Restore the required registers
;		Return to the caller
;
;	Registers on Entry:
;
;		CH    - Number Base
;		CL    - Maximum number length
;		DS:SI - Current string pointer
;
;	Registers on Exit:
;
;		CL    - Destroyed
;		DL    - Number retrieved (Zero if none)
;		DS:SI - Pointer to character following number
;		FL    - CY set if no number was found
;			ZR set if zero number found
;
;******************************************************************************
Get_Number	Proc	Near		; Get number procedure
	Save	ax,bx			; Save the required registers
	mov	dl,MAX_BYTE		; Get maximum value for a byte
	xor	ax,ax			; Initialize the current total
Get_Loop:
	mov	bx,ax			; Save current total in BX register
	lodsb				; Get the next string character
	call	Check_Digit		; Call routine to check for digit
	jc	Number_Done		; Jump if this is not a valid digit
	inc	dl			; Increment no number present flag
	cbw				; Convert binary value into full word
	xchg	ax,bx			; Move total to AX, digit value in BX
	mul	ch			; Multiply current total by number base
	add	ax,bx			; Add in the new digit to current total
	dec	cl			; Decrement the number length count
	jnz	Get_Loop		; Jump if more digits are allowed
	mov	bx,ax			; Move the current total into BX
	inc	si			; Increment to next character
Number_Done:
	dec	si			; Decrement back to non-digit character
	add	dl,1			; Set carry to indicate presence
	jc	Number_Exit		; Jump if no number was present
	mov	dl,bl			; Save the computed number in DL
	or	dl,dl			; Set zero flag for zero result
Number_Exit:
	Restore ax,bx			; Restore the required registers
	ret				; Return to the caller
Get_Number	Endp			; End of the Get_Number procedure
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Compute(Argument, Handle, Width, Precision, Base, Flags)
;
;		Save the required registers
;		Allocate buffer space on the stack
;		If argument value is long
;			Get the long numeric value (4 bytes)
;		Else if argument value is short
;			Get the short numeric value (1 byte)
;			Convert short to long value
;		Else argument value is normal
;			Get the normal numeric value (2 bytes)
;			Convert normal to long value
;		Endif
;		If signed type is specified
;			If the value is signed
;				Set the signed value flag
;				Take the twos complement of the value
;			Endif
;		Endif
;		Zero the fraction value (Integer default)
;		If fractional type conversion
;			Separate number into integer and fraction
;		Endif
;		Convert integer to ASCII string in buffer
;		If fractional type conversion
;			Convert fraction to ASCII string in buffer
;		Endif
;		Calculate the numeric string length
;		Default to no sign character
;		If signed type conversion
;			If numeric value was signed (Negative)
;				Setup minus sign as sign character
;				Increment the string length (For sign character)
;			Else numeric value was not signed
;				If signed conversion was specified
;					Setup plus sign as sign character
;					Increment the string length
;				Endif
;			Endif
;		Endif
;		If field width is not set (Zero)
;			Set field width to string length
;		Endif
;		If string length > field width
;			Set the field overflow flag
;			Set string length to field width - 1
;		Endif
;		Call routine to calculate pad counts
;		If sign character is present
;			If pre-pad sign character
;				Output sign character to handle
;				Call routine to output pre-string pad characters
;			Else post-pad sign character
;				Call routine to output pre-string pad characters
;				Output sign character to handle
;			Endif
;		Else sign character is not present
;			Call routine to output pre-string pad characters
;		Endif
;		While length > 0
;			Get next character of string
;			Call routine to output character to handle
;			Decrement the length
;		Endwhile
;		Set current pad character to a space
;		Call routine to output post-string pad characters
;		If the field overflow flag is set
;			Output the overflow character
;		Endif
;		Restore the required registers
;		Return to the caller
;
;	Registers on Entry:
;
;		AH    - Handle
;		DS:SI - Pointer to argument
;		CH    - Current number base
;		DH    - Current field width
;		DL    - Current field precision
;		BP    - Formatting flags
;
;	Registers on Exit:
;
;		FL    - DR flag is cleared (Move Forward)
;
;******************************************************************************
Compute 	Proc	Near		; Output numeric string procedure
	Save	bx,si,ds		; Save the required registers
;
;  Allocate the buffer area on the current stack
;
	sub	sp,BUFF_SIZE		; Allocate buffer space on the stack
	mov	di,sp			; Setup the buffer pointer
	add	di,BUFF_SIZE/2		; Set the starting buffer position
	push	ax			; Save the output handle value
	push	dx			; Save the field width/precision
	xor	dh,dh			; Convert precision to a full word
	push	dx			; Save the field precision
	mov	cl,ch			; Move current base value into CL
	xor	ch,ch			; Make current base value into a word
	xor	ax,ax			; Default to an unsigned
	xor	dx,dx			;			 non-long value
	test	bp,SHORT_SPEC		; Check for short value specified
	jnz	Get_Short		; Jump if short value specified
	test	bp,LONG_SPEC		; Check for long value specified
	jnz	Get_Long		; Jump if long value specified
	mov	ax,ds:[si]		; Get the normal value (2 Bytes)
	test	bp,SIGNED_TYPE		; Check for a signed conversion
	jz	Chk_Frac		; Jump if not a signed conversion
	cwd				; Signed conversion, do sign extension
	jmp	Short Chk_Sign		; Go check for signed argument
Get_Short:
	mov	al,ds:[si]		; Get the short value (1 Byte)
	test	bp,SIGNED_TYPE		; Check for a signed conversion
	jz	Chk_Frac		; Jump if not a signed conversion
	cbw				; Signed conversion,
	cwd				;		     do sign extension
	jmp	Short Chk_Sign		; Go check for signed argument
Get_Long:
	mov	ax,ds:[si]		; Get the
	mov	dx,ds:[si + 2]		;	  long value (4 Bytes)
;
;  If a signed type and a signed number, take the twos complement
;
Chk_Sign:
	test	bp,SIGNED_TYPE		; Check for a signed type
	jz	Chk_Frac		; Jump if not signed
	test	dx,SIGNED		; Check the number for signed
	jz	Chk_Frac		; Jump if number is not signed
	or	bp,SIGNED_VAL		; Set the signed value flag
	not	ax			; Ones complement of the LSW
	not	dx			; Ones complement of the MSW
	add	ax,1			; Twos complement of the LSW
	adc	dx,0			; Twos complement of the MSW
;
;  If a fractional type, separate the integer and the fraction
;
Chk_Frac:
	mov	si,ss			; Get the current stack segment
	mov	ds,si			; Setup DS to current stack segment
	mov	es,si			; Setup ES to current stack segment
	xor	bx,bx			; Zero fraction value (Integer default)
	test	bp,FRACTIONAL		; Check for fractional conversion
	jz	Do_Integer		; Jump if standard integer conversion
	test	bp,SHORT_SPEC		; Check for short value specified
	jnz	Do_Short		; Jump if a short fractional value
	test	bp,LONG_SPEC		; Check for long value specified
	jnz	Do_Long 		; Jump if a long fractional value
	mov	bh,al			; Move fraction value to MSB (BH)
	xor	bl,bl			; Zero the lower LSB of fraction (BL)
	mov	al,ah			; Move integer value to LSB (AL)
	xor	ah,ah			; Zero the upper MSB of integer (AH)
	xor	dx,dx			; Zero the upper MSW of integer (DX)
	jmp	Short Do_Integer	; Go convert the integer portion
Do_Long:
	mov	bx,ax			; Move fraction value to BX
	mov	ax,dx			; Move integer value to LSW (AX)
	xor	dx,dx			; Zero the upper MSW of integer (DX)
	jmp	Short Do_Integer	; Go convert the integer portion
Do_Short:
	mov	bh,al			; Move fraction value to MSB (BH)
	xor	bl,bl			; Zero the lower LSB of fraction (BL)
	xor	ax,ax			; Zero the lower LSW of integer (AX)
	xor	dx,dx			; Zero the upper MSW of integer (DX)
;
;  Convert the integer to ASCII and store in the buffer
;
Do_Integer:
	push	di			; Save the starting position
	cld				; Clear direction flag (Move forward)
Integer_Loop:
	mov	si,ax			; Save LSW of value in SI register
	mov	ax,dx			; Move MSW of value into AX
	xor	dx,dx			; Setup to do the first divide
	div	cx			; Divide the MSW by the current base
	xchg	ax,si			; Setup for the second division
	div	cx			; Divide the result by the current base
	xchg	ax,dx			; Put the remainder into AX
	call	Digit			; Call routine to convert it to a digit
	stosb				; Store the ASCII digit into buffer
	xchg	ax,dx			; Restore quotient of second division
	mov	dx,si			; Restore quotient of first division
	or	si,ax			; Check for a zero result
	jnz	Integer_Loop		; Jump if more digits to get
;
;  Convert the fraction (If any) to ASCII and store in the buffer
;
Do_Fraction:
	pop	dx			; Restore the starting position
	pop	si			; Restore the field precision
	mov	ax,bx			; Get the fraction value into AX
	mov	bx,di			; Save the final position in BX
	mov	di,dx			; Get the starting position
	test	bp,FRACTIONAL		; Check for fractional conversion
	jz	Calc_Length		; Jump if standard integer conversion
	pop	dx			; Restore the field width/precision
	push	dx			; Save the field width/precision
	or	dh,dh			; Check for a zero field width value
	jnz	Set_Position		; Jump if field width was given
	mov	dx,bx			; Get the final position value
	sub	dx,di			; Compute the actual string length
	dec	dx			; Check for a single digit
	jnz	Set_Position		; Jump if more than a single digit
	cmp	Byte Ptr es:[di],ZERO_PAD
	jne	Set_Position		; Jump if single digit is NOT a zero
	test	bp,PAD_CHAR		; Check for zero character pad
	jz	Set_Direction		; Jump if a blank character pad
Set_Position:
	dec	di			; Decrement to get new start position
Set_Direction:
	std				; Set direction flag (Move Backward)
	mov	Byte Ptr es:[di],POINT	; Put a decimal point into buffer
	or	si,si			; Check for zero precision
	jz	Calc_Length		; Jump if no digits to compute
	dec	di			; Update pointer for decimal point
Fraction_Loop:
	mul	cx			; Multiply by the current base
	xchg	ax,dx			; Put the MSW of result into AX
	call	Digit			; Call routine to convert it to a digit
	stosb				; Store the ASCII digit into buffer
	xchg	ax,dx			; Restore fraction from multiply
	dec	si			; Decrement the precision count
	jnz	Fraction_Loop		; Jump if more digits left to do
	inc	di			; Correct for length calculation
Do_Round:
	mul	cx			; Compute the next actual digit
	shl	dx,1			; Multiply digit value by two
	cmp	dx,cx			; Compare value to current base
	jb	Calc_Length		; Jump if below current base value
	push	di			; Save the current position
	mov	ch,cl			; Move current base to CH
Round_Loop:
	mov	al,es:[di]		; Get the digit to round
	call	Convert 		; Convert the ASCII to binary
	inc	al			; Round the digit up
	mov	ah,al			; Save the rounded value in AH
	call	Digit			; Convert the binary digit to ASCII
	cmp	ah,ch			; Check the value against current base
	jb	Round_Done		; Jump if rounding is complete
	xor	al,al			; Zero the AL register value
	call	Digit			; Convert to an ASCII zero value
	mov	es:[di],al		; Zero the current digit value
	inc	di			; Increment to the next digit position
	cmp	di,bx			; Check against final position
	jbe	Round_Loop		; Jump if more digits to round with
	mov	bx,di			; Update the new final position
	xor	al,al			; Zero the AL register value
	inc	al			; Increment AL to a one value
	call	Digit			; Convert the one value to ASCII
Round_Done:
	mov	es:[di],al		; Save the last rounded digit
	pop	di			; Restore the current position
;
;  Calculate the length of the numeric ASCII string
;
Calc_Length:
	pop	dx			; Restore field width/precision
	pop	ax			; Restore the file handle
	mov	cx,bx			; Get the final buffer pointer
	sub	cx,di			; Compute the numeric string length
;
;  Determine whether or not a sign character is needed for the value
;
	xor	al,al			; Default to no sign character
	test	bp,SIGNED_TYPE		; Check for signed type
	jz	Do_Check		; Jump if not a signed type
	test	bp,SIGNED_VAL		; Check for a signed value
	jz	Chk_Conv		; Jump if not a signed value
	mov	al,MINUS		; Setup minus as sign character
	inc	cx			; Increment the string length
	jmp	Short Do_Check		; Go check the field width
Chk_Conv:
	test	bp,SIGNED_CONV		; Check for a signed conversion
	jz	Do_Check		; Jump if not a signed type
	mov	al,PLUS 		; Setup plus as sign character
	inc	cx			; Increment the string length
;
;  Setup the correct field width based on string length
;
Do_Check:
	or	dh,dh			; Check the current field width
	jnz	Width_Check		; Jump if field width specified
	mov	dh,cl			; Set field width to string length
Width_Check:
	cmp	cl,dh			; Check actual width to field width
	jbe	Do_Calc 		; Jump if string fits in the field
	or	bp,OVER_FLOW		; Set the field overflow flag
	mov	cl,dh			; Set string width to field width
	dec	cl			; Adjust for the overflow character
	jz	Compute_Exit		; Jump if no more room in the field
;
;  Calculate the pad counts and handle outputting a sign if necessary
;
Do_Calc:
	push	ax			; Save the sign character (If any)
	mov	al,cl			; Save the actual string length
	call	Calculate		; Call routine to calculate pad values
	mov	dl,al			; Setup the string output length
	pop	ax			; Restore the sign character (If any)
	test	bp,PRE_PAD		; Check for pre pad sign character
	jz	Do_Pad			; Jump if not pre pad sign character
	or	al,al			; Check for a sign needed
	jz	Do_Pad			; Jump if no sign is needed
	call	Output			; Call routine to output sign character
	dec	dl			; Decrement the output count
	jz	Compute_Exit		; Jump if no more room in field
Do_Pad:
	call	Pad			; Call routine to output pad characters
	test	bp,PRE_PAD		; Check for post pad sign character
	jnz	Do_Setup		; Jump if not post pad sign character
	or	al,al			; Check for a sign needed
	jz	Do_Setup		; Jump if no sign character needed
	call	Output			; Call routine to output the sign
	dec	dl			; Decrement the output count
	jz	Compute_Exit		; Jump if no more room in field
Do_Setup:
	mov	si,bx			; Setup the source pointer to buffer
	dec	si			; Point back to first character
	std				; Set direction flag (Reverse order)
;
;  Send the string characters to the output handle
;
Send_Loop:
	lodsb				; Get the next character to output
	call	Output			; Call routine to output character
	dec	dl			; Decrement the output count
	jnz	Send_Loop		; Jump if more characters to output
	mov	cl,ch			; Get the calculated pad counts
;
;  Change pad character, output pad characters and deallocate buffer
;
	and	bp,Not PAD_CHAR 	; Set pad character to a space
	call	Pad			; Call routine to send pad characters
;
;  Restore the registers and return to the caller
;
Compute_Exit:
	test	bp,OVER_FLOW		; Check for field overflow
	jz	Compute_Done		; Jump if no field overflow
	mov	al,ASTERISK		; Get the field overflow character (*)
	call	Output			; Output the field overflow character
Compute_Done:
	add	sp,BUFF_SIZE		; Deallocate the buffer area
	Restore bx,si,ds		; Restore the required registers
	cld				; Clear the direction flag
	ret				; Return to the caller
Compute 	Endp			; End of the Compute procedure
	Page
;******************************************************************************
;
;  Routine Functional Description
;
;	Digit(Value, Flags)
;
;		Save the required registers
;		Translate character to ASCII value
;		If uppercase flag is set
;			If ASCII value is not a digit (abcdef)
;				Convert to uppercase  (ABCDEF)
;			Endif
;		Endif
;		Restore the required registers
;		Return to the caller
;
;	Registers on Entry:
;
;		AL    - Binary value (0 - 15)
;
;	Registers on Exit:
;
;		AL    - ASCII character
;
;******************************************************************************
Digit		Proc	Near		; Convert to ASCII digit procedure
	Save	bx			; Save the required registers
	lea	bx,cs:[Digit_Table]	; Get pointer to digit translate table
	xlat	byte ptr cs:[bx]	; Translate to ASCII digit
	test	bp,UPPER_CASE		; Check for uppercase flag
	jz	Digit_Exit		; Jump if no uppercase flag set
	cmp	al,DIGIT_MAX		; Check for uppercase adjust needed
	jbe	Digit_Exit		; Jump if adjustment not needed
	and	al,UPPER_MASK		; Convert character to uppercase
Digit_Exit:
	Restore bx			; Restore the required registers
	ret				; Return to the caller
Digit		Endp			; End of the Digit procedure
;
;  Define the ASCII digit translation table
;
Digit_Table	Label	Byte		; Digit translation table
	Db	"0123456789abcdef"
;
;  Define the end of the standard code segment
;
Code	Ends				; End of the standard code segment
	End				; End of the Printf module

file: /Techref/intel/16bit/formatting/printf.htm, 71KB, , updated: 2000/2/16 13:39, local time: 2024/12/25 08:33,
TOP NEW HELP FIND: 
3.144.242.149:LOG IN

 ©2024 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions?
Please DO link to this page! Digg it! / MAKE!

<A HREF="http://linistepper.com/techref/intel/16bit/formatting/printf.htm"> intel 16bit formatting printf</A>

After you find an appropriate page, you are invited to your to this massmind site! (posts will be visible only to you before review) Just type a nice message (short messages are blocked as spam) in the box and press the Post button. (HTML welcomed, but not the <A tag: Instead, use the link box to link to another page. A tutorial is available Members can login to post directly, become page editors, and be credited for their posts.


Link? Put it here: 
if you want a response, please enter your email address: 
Attn spammers: All posts are reviewed before being made visible to anyone other than the poster.
Did you find what you needed?