;------------------------------------------------------------------------------ ; This program demonstrates the use of FPU instructions in x86 Assembly. ; It accepts characters from the keyboard and converts them to IEEE real form ; and then converts them back to ASCII and prints them on the screen. The file ; "io32.inc" must be included for the string operations to work. "MAXSTR" is ; required by "io32.inc" and ESI and EDI must be initialized to the correct ; values before calling the procedures in "io32.inc" (see headers in io32.inc ; for the proper initialization). ; Written by Joe Toone 11/5/2003 3:02:04 PM ;------------------------------------------------------------------------------ TITLE fpu_32.asm CR equ 0dh ;ASCII 13 for carriage return LF equ 0ah ;ASCII 10 for line feed MAXSTR equ 80d ;Accept up to 80 characters strings FALSE EQU 0 ;Flags TRUE EQU 1 ; C3 EQU 0100000000000000b ;Condition codes which are checked C2 EQU 0000010000000000b ; in the FPU Status register C0 EQU 0000000100000000b ; .586 ;Allow Pentium instructions .MODEL FLAT ;FLAT memory model INCLUDE io32.inc ;Include the external I/O procedures .STACK 4096h ;4K hex bytes for stack .DATA Prompt BYTE CR,LF,'Enter a Floating Point Number --> ',0 AsciiNum BYTE MAXSTR dup(0) ;Used to store input string of digits StrOut BYTE MAXSTR dup(0) ;Used to store output string of digits NewLine BYTE CR,LF,0 ;Newline string one REAL4 1.0 ;Used in fp2a ten REAL4 10.0 ;Used in a2fp and fp2a round REAL4 0.000005 ;Used for rounding control in fp2a FPNum REAL4 ? ;Storage for entered real number byteTen BYTE 10 ;Used in fp2a point BYTE ? ;Flag for a2fp minus BYTE ? ;Flag for a2fp digit WORD ? ;Used in a2fp and fp2a exponent WORD ? ;Used in fp2a controlWd WORD ? ;Used in fp2a ;-------- Main Body ----------------------------------------------------------- ; Given :Nothing ; Process :Main Body - contains inline code and procedure calls to convert ; ASCII numbers to IEEE Floating Point values and also convert IEEE ; Floating Point values to ASCII numbers. ; Return :Nothing ;------------------------------------------------------------------------------ .CODE _main: lea esi,Prompt ;Set esi (source index) to Prompt message call PrintString ;Call the routine to print a string lea esi,AsciiNum ;Pointer to ASCII number buffer in data call GetString ;Accept the user input lea esi,AsciiNum ;Pointer to ASCII number buffer in data call a2fp ;Convert an ASCII sequence to a FP number fstp FPNum ;Pop ST into FPNum variable lea esi,NewLine ;Pointer to newline string call PrintString ;Execute a carriage return lea esi,FPNum ;Pointer to floating point number lea edi,StrOut ;Pointer to output string destination call fp2a ;Convert IEEE FP to ASCII lea esi,StrOut ;Pointer to the converted String call PrintString ;Print the ASCII number INVOKE ExitProcess, 0 ; exit with return code 0 ;--------- Convert ASCII String to Floating Point Number ---------------------- ; Given :ESI register pointing to the ASCII number to convert ; Process :After an optional leading minus sign, only digits 0-9 and a decimal ; point are accepted -- the scan terminates with any other character. ; This procedure produces an IEEE floating point number. No registers ; are changed and the flags are not affected. ; Return :The floating point value is returned in ST. ;------------------------------------------------------------------------------ a2fp PROC NEAR32 pushad ;Save the contents of all registers pushfd ; finit ;Initialize the Floating Point Stack fld1 ; divisor := 1.0 fldz ; value := 0.0 mov point, false ; no decimal point found yet mov minus, false ; no minus sign found yet lodsb ; load first character into AL register cmp al,'-' ; check for minus sign jne NotMinus ; skip if not mov minus, true ; minus sign found lodsb ; load next character into AL register NotMinus: ; was not negative number WhileMore:cmp al, '.' ; decimal point? jne NotPoint ; skip if not mov point, true ; found decimal point jmp nextChar NotPoint: cmp al, '0' ; character a digit? jl EndWhile ; exit if lower than '0' cmp al, '9' jg EndWhile ; exit if higher than '9' and ax, 000fh ; convert ASCII to integer value mov digit, ax ; put integer in memory fmul ten ; value := value * 10 fiadd digit ; value := value + digit cmp point, true ; already found a decimal point? jne endifDec ; skip if not fxch ; put divisor in ST and value in ST(1) fmul ten ; divisor := divisor * 10 fxch ; value back to ST; divisor back to ST(1) endifDec: nextChar: lodsb ; load next character into AL registe jmp WhileMore ; go process the character EndWhile: fdivr ; value := value / divisor cmp minus, true ; was there a minus sign? jne IsPositive ; no, it is a positive number fchs ; yes, value := -value IsPositive: popfd ;Restore the registers popad ; ret ; return a2fp ENDP ;--------- Convert Floating Point Number to ASCII String ---------------------- ; Given :ESI pointing to FP value to convert and EDI pointing to the output ; string destination. ; Process :Build ASCII string with format [blank/-]d.dddddE[+/-]dd and store ; the string in the data segment. (Output is always 12 characters.) ; No registers are changed and the flags are not affected. ; Return :Nothing ;------------------------------------------------------------------------------ fp2a PROC NEAR32 pushad ;Save the contents of all registers pushfd ; finit ; initialize the Floating Point Stack fstcw controlWd ; get control word push controlWd ; save control word or controlWd, 0000110000000000b fldcw controlWd ; set control to chop fld REAL4 PTR [esi] ; load ST with the FP value mov exponent, 0 ; exponent := 0 ftst ; value >= 0? fstsw ax ; status word to AX and ax, C0 ; check C0 jnz elseNeg ; skip if set (value negative) mov al,' ' ; blank for positive stosb ; write the blank to the string jmp NotNeg ; jump over negative sign elseNeg: mov al,'-' ; minus for negative stosb ; write the minus to the string fchs ; convert number to positive NotNeg: mov exponent, 0 ; exponent := 0 ftst ; value = 0? fstsw ax ; status word to AX and ax, C3 ; check C3 jne endifZero ; skip if zero fcom ten ; value > 10? fstsw ax ; status word to AX and ax, C3 or C2 or C0 ; check for all C3=C2=C0=0 jnz elseLess ; skip if value not > 10 untilLess: inc exponent ; add 1 to exponent fdiv ten ; value := value/10 fcom ten ; value < 10 fstsw ax ; status word to AX and ax, C0 ; check C0 jz untilLess ; continue until value < 10 jmp endifBigger ; exit if elseLess: whileLess: fcom one ; value < 1 fstsw ax ; status word to AX and ax, C0 ; check C0 jz endwhileLess ; exit if not less fmul ten ; value := 10*value dec exponent ; subtract 1 from exponent jmp whileLess ; continue while value < 1 endwhileLess: endifBigger: endifZero: fadd round ; add rounding value fcom ten ; value > 10? fstsw ax ; status word to AX and ax, C3 or C2 or C0 ; C3=C2=C0=0? (value > 10?) jnz endifOver ; skip if not fdiv ten ; value := value/10 inc exponent ; add 1 to exponent endifOver: ; at this point 1.0 <= value < 10.0 fist digit ; store integer part mov bx, digit ; copy integer to BX or bx, 30h ; convert digit to character mov al,bl ; copy character to AL for writing stosb ; write the character to the string mov al,'.' ; load AL with decimal point stosb ; write decimal point to string mov ecx,5 ; count of remaining digits forDigit: fisub digit ; subtract integer part fmul ten ; multiply by 10 fist digit ; store integer part mov bx, digit ; copy integer to BX or bx, 30h ; convert digit to character mov al,bl ; copy character to write stosb ; write the character to the string loop forDigit ; repeat 5 times mov al,'E' ; exponent indicator stosb ; write the character to the string mov ax, exponent ; get exponent push ax ; save the exponenet cmp ax, 0 ; exponent >= 0 ? jnge NegExp mov al,'+' ; non-negative exponent stosb ; write the character to the string pop ax ; restore the exponent if not negative jmp endifNegExp NegExp: mov al,'-' ; negative exponent stosb ; write the character to the string pop ax ; restore the exponent if negative neg ax ; change exponent to positive endifNegExp: div byteTen ; convert exponent to 2 digits or ax, 3030h ; convert both digits to ASCII stosb ; write AL character to the string mov al,ah ; copy low order character to DL stosb ; write the character to the string xor al,al ; zero the AL register stosb ; write NULL to terminate string pop controlWd ; restore control word fldcw controlWd ; load the control word back to FPU popfd ;Restore the registers popad ; ret fp2a ENDP Public _main ;declare _main as a public procedure END ;end of code segment