; float.asm

; calculate equation with high precision without math coprocessor 

; this program calculates linear equation: ax + b = 0 
; the result is printed with floating point. 

; for example: a = 7, b = 2 
;              x = -0.28571428.... 

name "float"

precision = 30  ; max digits after the dot. 


dseg    segment 'data'
cr              equ     0Dh
lf              equ     0Ah
new_line        equ     0Dh,0Ah, '$'
mess0           db      'calculation of ax + b = 0', new_line
mess1           db      'enter a (-32768..32767)!', new_line
mess2           db      lf, cr, 'enter b (-32768..32767)!', new_line
mess3           db      cr, lf, cr, lf, 'data:', '$'
mess4           db      cr, lf, ' a = ', '$'
mess5           db      cr, lf, ' b = ', '$'
mess6           db      cr, lf, 'result: ', cr, lf, ' x = ', '$'
mess7           db      cr, lf, cr, lf, 'no solution!', new_line
mess8           db      cr, lf, cr, lf, 'infinite number of solutions!', new_line
error           db      cr, lf, 'the number is out of range!', new_line
twice_nl        db      new_line, new_line
make_minus      db      ?       ; used as a flag in procedures. 
a               dw      ?
b               dw      ?
ten             dw      10      ; used as multiplier. 
four            dw      4       ; used as divider. 
dseg    ends

sseg    segment stack   'stack'
                dw      100h    dup(?)
sseg    ends

cseg    segment 'code'

;******************************************************************* 

start           proc    far

; store return address to os: 
                push    ds
                xor     ax, ax
                push    ax

; set segment registers:                 
                mov     ax, dseg
                mov     ds, ax
                mov     es, ax

; welcome message: 
                lea     dx, mess0
                call    puts            ; display the message. 

; ask for 'a' : 
                lea     dx, mess1
                call    puts            ; display the message. 
                call    scan_num        ; input the number into cx. 
                mov     a, cx

; ask for 'b' : 
                lea     dx, mess2
                call    puts            ; display the message. 
                call    scan_num        ; input the number into cx. 
                mov     b, cx

; print the data: 
                lea     dx, mess3
                call    puts

                lea     dx, mess4
                call    puts
                mov     ax, a
                call    print_num               ; print ax. 

                lea     dx, mess5
                call    puts
                mov     ax, b
                call    print_num               ; print ax. 


; check data: 
                cmp     a, 0
                jne     soluble         ; jumps when a<>0. 
                cmp     b, 0
                jne     no_solution     ; jumps when a=0 and b<>0. 
                jmp     infinite        ; jumps when a=0 and b=0. 
soluble:

; calculate the solution: 
; ax + b = 0  ->  ax = -b  ->  x = -b/a 

                neg     b

                mov     ax, b

                xor     dx, dx

                ; check the sign, make dx:ax negative if ax is negative: 
                cmp     ax, 0
                jns     not_singned
                not     dx
not_singned:
                mov     bx, a   ; divider is in bx. 

                ; '-b' is in dx:ax. 
                ; 'a' is in bx. 

                idiv    bx      ; ax = dx:ax / bx       (dx - remainder). 

                ; 'x' is in ax. 
                ; remainder is in dx. 

                push    dx      ; store the remainder. 

                lea     dx, mess6
                call    puts

                pop     dx

                ; print 'x' as float: 
                ; ax - whole part 
                ; dx - remainder 
                ; bx - divider 
                call    print_float

                jmp     end_prog
no_solution:
                lea     dx, mess7
                call    puts
                jmp     end_prog
infinite:
                lea     dx, mess8
                call    puts
end_prog:
                lea     dx, twice_nl
                call    puts

                ; wait for any key.... 
                mov     ah, 0
                int     16h


                ret
 start          endp

;*************************************************************** 

; prints number in ax and it's fraction in dx. 
; used to print remainder of 'div/idiv bx'. 
; ax - whole part. 
; dx - remainder. 
; bx - the divider that was used to get the remainder from divident. 
print_float     proc    near
        push    cx
        push    dx

        ; because the remainder takes the sign of divident 
        ; its sign should be inverted when divider is negative 
        ; (-) / (-) = (+) 
        ; (+) / (-) = (-) 
        cmp     bx, 0
        jns     div_not_signed
        neg     dx              ; make remainder positive. 
div_not_signed:

        ; print_num procedure does not print the '-' 
        ; when the whole part is '0' (even if the remainder is 
        ; negative) this code fixes it: 
        cmp     ax, 0
        jne     checked         ; ax<>0 
        cmp     dx, 0
        jns     checked         ; ax=0 and dx>=0 
        push    dx
        mov     dl, '-'
        call    write_char      ; print '-' 
        pop     dx
checked:

        ; print whole part: 
        call    print_num

        ; if remainder=0, then no need to print it: 
        cmp     dx, 0
        je      done

        push    dx
        ; print dot after the number: 
        mov     dl, '.'
        call    write_char
        pop     dx

        ; print digits after the dot: 
        mov     cx, precision
        call    print_fraction
done:
        pop     dx
        pop     cx
        ret
print_float     endp

;*************************************************************** 

; prints dx as fraction of division by bx. 
; dx - remainder. 
; bx - divider. 
; cx - maximum number of digits after the dot. 
print_fraction  proc    near
        push    ax
        push    dx
next_fraction:
        ; check if all digits are already printed: 
        cmp     cx, 0
        jz      end_rem
        dec     cx      ; decrease digit counter. 

        ; when remainder is '0' no need to continue: 
        cmp     dx, 0
        je      end_rem

        mov     ax, dx
        xor     dx, dx
        cmp     ax, 0
        jns     not_sig1
        not     dx
not_sig1:

        imul    ten             ; dx:ax = ax * 10 

        idiv    bx              ; ax = dx:ax / bx   (dx - remainder) 

        push    dx              ; store remainder. 
        mov     dx, ax
        cmp     dx, 0
        jns     not_sig2
        neg     dx
not_sig2:
        add     dl, 30h         ; convert to ascii code. 
        call    write_char      ; print dl. 
        pop     dx

        jmp     next_fraction
end_rem:
        pop     dx
        pop     ax
        ret
print_fraction  endp

;*************************************************************** 

; this procedure prints number in ax, 
; used with print_numx to print "0" and sign. 
; this procedure also stores the original ax, 
; that is modified by print_numx. 
print_num       proc    near
        push    dx
        push    ax

        cmp     ax, 0
        jnz     not_zero

        mov     dl, '0'
        call    write_char
        jmp     printed

not_zero:
        ; the check sign of ax, 
        ; make absolute if it's negative: 
        cmp     ax, 0
        jns     positive
        neg     ax

        mov     dl, '-'
        call    write_char
positive:
        call    print_numx
printed:
        pop     ax
        pop     dx
        ret
print_num       endp

;*************************************************************** 

; prints out a number in ax (not just a single digit) 
; allowed values from 1 to 65535 (ffff) 
; (result of /10000 should be the left digit or "0"). 
; modifies ax (after the procedure ax=0) 
print_numx      proc    near
        push    bx
        push    cx
        push    dx

        ; flag to prevent printing zeros before number: 
        mov     cx, 1

        mov     bx, 10000       ; 2710h - divider. 

        ; check if ax is zero, if zero go to end_show 
        cmp     ax, 0
        jz      end_show

begin_print:

        ; check divider (if zero go to end_show): 
        cmp     bx,0
        jz      end_show

        ; avoid printing zeros before number: 
        cmp     cx, 0
        je      calc
        ; if ax<bx then result of div will be zero: 
        cmp     ax, bx
        jb      skip
calc:
        xor     cx, cx  ; set flag. 

        xor     dx, dx
        div     bx      ; ax = dx:ax / bx   (dx=remainder). 

        ; print last digit 
        ; ah is always zero, so it's ignored 
        push    dx
        mov     dl, al
        add     dl, 30h    ; convert to ascii code. 
        call    write_char
        pop     dx

        mov     ax, dx  ; get remainder from last div. 

skip:
        ; calculate bx=bx/10 
        push    ax
        xor     dx, dx
        mov     ax, bx
        div     ten     ; ax = dx:ax / 10   (dx=remainder). 
        mov     bx, ax
        pop     ax

        jmp     begin_print

end_show:

        pop     dx
        pop     cx
        pop     bx
        ret
print_numx      endp

;*************************************************************** 

; displays the message (dx-address) 
puts    proc    near
        push    ax
        mov     ah, 09h
        int     21h
        pop     ax
        ret
puts    endp

;******************************************************************* 

; reads char from the keyboard into al 
; (modifies ax!!!) 
read_char       proc    near
        mov     ah, 01h
        int     21h
        ret
read_char       endp

;*************************************************************** 

; gets the multi-digit signed number from the keyboard, 
; result is stored in cx 
scan_num        proc    near
        push    dx
        push    ax

        xor     cx, cx

        ; reset flag: 
        mov     make_minus, 0

next_digit:

        call    read_char

        ; check for minus: 
        cmp     al, '-'
        je      set_minus

        ; check for enter key: 
        cmp     al, cr
        je      stop_input

        ; multiply cx by 10 (first time the result is zero) 
        push    ax
        mov     ax, cx
        mul     ten                     ; dx:ax = ax*10 
        mov     cx, ax
        pop     ax

        ; check if the number is too big 
        ; (result should be 16 bits) 
        cmp     dx, 0
        jne     out_of_range

        ; convert from ascii code: 
        sub     al, 30h

        ; add al to cx: 
        xor     ah, ah
        add     cx, ax
        jc      out_of_range    ; jump if the number is too big. 

        jmp     next_digit

set_minus:
        mov     make_minus, 1
        jmp     next_digit

out_of_range:
        lea     dx, error
        call    puts

stop_input:
        ; check flag: 
        cmp     make_minus, 0
        je      not_minus
        neg     cx
not_minus:

        pop     ax
        pop     dx
        ret
scan_num        endp

;*************************************************************** 

; prints out single char (ascii code should be in dl) 
write_char      proc    near
        push    ax
        mov     ah, 02h
        int     21h
        pop     ax
        ret
write_char      endp

;*************************************************************** 

cseg    ends
        end     start



; - Other Assembler Source Codes -



; - asm2html by emu8086 -