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/11/25 01:34,
3.144.20.66: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? <A HREF="http://sxlist.com/techref/intel/16bit/formatting/printf.htm"> intel 16bit formatting printf</A> |
Did you find what you needed? |
Welcome to sxlist.com!sales, advertizing, & kind contributors just like you! Please don't rip/copy (here's why Copies of the site on CD are available at minimal cost. |
Welcome to sxlist.com! |
.