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