;; DEC2FP.ASM--Translate an ASCII string into a floating point number ;; and leave it on the floating point stack. Assumes ds:si points to ;; the beginning of the string on entry, and leaves si pointing to the ;; first character beyond the end of the scanned string. If conversion ;; successful, the carry flag is clear, otherwise it is set. Four free ;; entries on the floating point stack are required. ;; The code is in the form of a rather large finite state machine, with ;; the states given as the address of their first processing statement ;; rather than a number. This code gives spaghetti code a good name. ;; ;; Library source text from "Assembly Language for the IBM PC Family" by ;; William B. Jones, (c) Copyright 1997, Scott/Jones Inc. ;; .MODEL SMALL .DATA state EQU di TenPows DQ 1E256, 1E128, 1E64, 1E32, 1E16, 1E8, 1E4, 1E2 Ten DQ 1E1 point1 DQ 1E-1 sign db ? esign db ? digit DW ? .CODE Public Dec2FP Dec2FP PROC push ax push bx push cx push dx push di push es push ds pop es ASSUME es : @data, ds : NOTHING, ss : NOTHING mov state, OFFSET START ; Initial state. States of finite ; state machine indicated by all caps mov sign, 0 ; guess '+' MainLoop: lodsb ; get next character mov bl, al ; Test and convert digit only once sub bh, bh sub bx, '0' ; If a digit, bx is now numeric value mov digit, bx jl switch ; can't jl state; don't need to testit cmp bl, 9 jle testit mov bx, -1 ; bx < 0 indicates not a digit testit: test bx, bx ; set the flags switch: jmp state START: jl st3 ; jump if not a digit st1: mov state, OFFSET WHOLE ; process first digit st2: fild digit jmp MainLoop st3: cmp al, ' ' ; Skip over white space je MainLoop cmp al, 9 ; TAB je MainLoop cmp al, '-' jne st4 mov sign, 1 ; process - sign jmp st5 st4: cmp al, '+' je st5 jmp sg0 ; check for decimal point st5: mov state, OFFSET SIGNED jmp MainLoop eload: fldz ; error and nothing yet in fp stack enoload: dec si ; si is already one ahead of where it should pt stc ; set carry flag to indicate error return: pop ds pop di pop dx pop cx pop bx pop ax ret OKret: cmp sign, 0 je OKret1 fchs ; negate OKret1: dec si clc jmp return SIGNED: ; Have just scanned the initial sign jge st1 ; got a digit sg0: cmp al, '.' jne eload mov state, OFFSET FRACT1 ; starting fraction. must get at least ; one real digit sg1: fldz ; nothing was loaded yet sg2: fld point1 ; value of next digit jmp MainLoop WHOLE: ; whole part of number jl wh1 fmul Ten fiadd digit ; new number = old number * 10 + new digit jmp MainLoop wh1: cmp al, '.' jne wh2 mov state, OFFSET FRACT ; starting fraction. don't need any digs jmp sg2 wh2: cmp al, 'e' je wh3 cmp al, 'E' je wh3 jmp OKret wh3: mov state, OFFSET EXPON mov esign, 0 jmp MainLoop FRACT1: ; Have dec pt and no digits. Must have jge fr11 jmp eload fr11: mov state, OFFSET FRACT jmp fr1 ; insert the digit's value FRACT: jge fr1 ; if digit, insert its value fcomp ST ; pop digit value jmp wh2 ; and look for exponent fr1: ; ST ST(1) ST(2) ; multiplier number fxch ; number multiplier fld ST(1) ; multiplier number multiplier fimul digit ; digit val number multiplier fadd ; new number multiplier fxch ; multiplier new number fmul Point1 ; new mult new number jmp MainLoop EXPON: jl ex2 ex1: mov cx, bx ; accumulate exponent in cx mov state, OFFSET EDIG jmp MainLoop ex2: cmp al, '+' ; look for exponent sign je ex4 cmp al, '-' jne es1 ; error if not digit or sign ex3: mov esign, 1 ex4: mov state, OFFSET ESIGNED ; still need a digit jmp MainLoop ESIGNED: ; got E+-, still need a digit jge ex1 ; got one es1: jmp enoload EDIG: jl Edone mov ax, cx mov cx, 10 mul cx test ax, 0f000h ; test for overflow jne eexp test dx, dx jne eexp add ax, bx mov cx, ax jmp MainLoop eexp: jmp enoload Edone: ; multiply by exponent fld1 ; the exponent, initially cmp cx, 256 jl Edone1 fmul TenPows ; 10^256 sub cx, 256 jmp Edone Edone1: mov bx, 256 mov di, OFFSET TenPows Edone2: jcxz Etest shr bx, 1 add di, 8 cmp cx, bx jl Edone2 fmul QWORD PTR es:[di] ; 10^cx sub cx, bx jmp Edone2 Etest: cmp esign, 0 je Emult fld1 fdivr ; invert exponent Emult: fmul jmp OKret Dec2FP ENDP END