Joey Platt
Joey Platt

Reputation: 83

Convert fraction to decimal in NASM

We are working on 8086 microprocessor and NASM assembler. My code assembles fine but when I execute I just get a bunch of random symbols. Our assignment instructions are:

  1. Display an appropriate prompt.
  2. Call a procedure, named DEC_IN, which will a. input a valid unsigned base-10 integer (0-65,535) from the keyboard b. convert the characters as they are input into the equivalent binary number, stored in register BX
  3. Save the input value into a memory location, M
  4. Display another appropriate prompt.
  5. Call DEC_IN once again, reading the value into BX and saving it another memory location N.
  6. Use the following algorithm to print the expansion: a. Print a “.” b. Execute the following steps 10 times: i. Divide 10 x M by N, getting quotient Q and remainder R. ii.Print Q. iii. Replace M by R and go to step i.
  7. Your procedures should save and restore any registers which they change

Here is my code which is horribly broken:

org     1000h
; program converts fraction to decimal, where M < N, M & N are both positive, and upto 6 decimal places are printed

section .data
MSG1    dw  "Enter the numerator: ", '$'
MSG2    dw  "Enter the denominator: ", '$'
EMSG    dw  "Please enter a number between 0 and 9 ", '$'
section .bss
M       RESW    1   
N       RESW    1

section .text
main:
; print user prompt 
mov     dx, MSG1    ; get message
mov     ah, 09h     ; display string function
int     21h         ; display it
mov     dl, 0Ah     ; line feed moved into character display register
mov     ah, 02h     ; charcter display function
int     21h         ; display line feed
mov     dl, 0Dh     ; carriage return moved into character display register
int     21h         ; display carriage return
xor     cx, cx      ; clear cx
jmp     DEC_IN  
prompt:
; print second prompt
mov     dx, MSG2    ; get message
mov     ah, 09h     ; display string function
int     21h         ; display it
mov     dl, 0Ah     ; line feed moved into character display register
mov     ah, 02h     ; charcter display function
int     21h         ; display line feed
mov     dl, 0Dh     ; carriage return moved into character display register
int     21h         ; display carriage return
inc     cx          ; will make next iteration store denominator in N
jmp     DEC_IN
DEC_IN:
; input character from keyboard, converts ASCII to appropriate binary, stores into respective memory location
mov     ah, 01h     ; keyboard input function
int     21h         ; character input, copies character into al
mov     bx, ax      ; moves ax into bx to avoid ax being messed with
cmp     bx, 30h     ; compares input to ASCII code for 0
jl      error       ; if input is less than 0 jump to error
cmp     bx, 39h     ; compares input to ASCII code for 9
jg      error       ; if input is greater than 9 jump to error
sub     bx, 30h     ; subtracts 30h to make the ASCII code into the base 10 number
cmp     cx, 1       ; check if input is denominator
je      prepare     
mov     word [M], bx ; stores user input into memory location M
jmp     prompt

prepare:
; store denominator in N
mov     word [N], bx    ; stores input into memory location N
mov     dl, 2Eh     ; moves '.' to display character register
mov     ah, 02h     ; display character function
int     21h         ; displays it
mov     cx, 10      ; set loop to run 10 times
mov     bx, word [M]
jmp     print

print:
; loop to print 
mov     ax, 10      ; setup for multiplication
mul     bx          ; multiply numerator by 10
mov     bx, word [N]
div     bx
push    dx
mov     dx, ax      
mov     ah, 09h     ; display string function
int     21h         ; display it
pop     dx  
mov     bx, dx
loop    print
jmp     exit

error:
; displays error message then jumps back to DEC_IN
mov     dx, EMSG    ; moves error message into display string register
mov     ah, 09h     ; display string function
int     21h         ; displays it
mov     dl, 0Ah     ; line feed moved into character display register
mov     ah, 02h     ; charcter display function
int     21h         ; display line feed
mov     dl, 0Dh     ; carriage return moved into character display register
int     21h         ; display carriage return
jmp     DEC_IN      

exit:
;exit to DOS
 mov     ah, 04Ch      ; DOS function: Exit program 
 mov     al, 0         ; Return exit code value
 int     21h           ; Call DOS. Terminate program 

Upvotes: 1

Views: 1388

Answers (1)

Frank Kotler
Frank Kotler

Reputation: 3119

When your code is horribly broken, it's probably a sign that you need to back up and take smaller steps.

org 1000h is almost certainly wrong. As rjbh says, you don't want an org for an .exe file (Nasm won't accept it!). I ASSume you're doing a .com file however, which wants org 100h. This doesn't "cause" us to be loaded at 100h, merely informs Nasm that dos will load us at 100h. If that isn't just a "posto", fixing it should get your prompts on screen, at least.

Your prompts should be db not dw. (I ASSume we're not doing UNICODE). You may wish to include the carriage return / linefeed pair in your prompt, rather than printing them in your code. It would make your code slightly smaller and simpler... and thus easier to find the errors. :) Either way works.

Your variables M and N are correct. Horrible names - not very "meaningful" - but specified in the assignment. I'd have probably named them "numerator" and "denominator" - more typing, but worth it to keep track of what you're doing (IMO). There'a a little "gotcha" with denominator - we probably don't want to let the user enter 0!

Your code starts off fine, up to jmp DEC_IN. I think that's supposed to be a subroutine that is called with the call instruction and returned from with the ret instruction. It is "usual" to return a value from a subroutine in ax, but the assignment says bx. No problem...

; your prompt
call DEC_IN
mov [M], bx
; your other prompt
call DEC_IN
mov [N], bx
; call a display routine?
; or put it in-line?
exit:
; always!
; your subroutines go here
; after the "main" part of your code

That should save you from having to use cx to know which number you're doing. DEC_IN will need to end with a ret, of course, and the assignment says save and restore registers. We can't save and restore bx of course, we're returning our number in it! You start off...

DEC_IN:
; input character from keyboard, converts ASCII to appropriate binary, stores into respective memory location
mov     ah, 01h     ; keyboard input function
int     21h         ; character input, copies character into al
mov     bx, ax      ; moves ax into bx to avoid ax being messed with

Don't be in such a rush to get the number into bx. For one thing, there's still junk in ah. And first, you want to make sure it's a valid digit. And before that, you probably want to check if it's the carriage return indicating the user's done - we don't want to try to make that part of our number!

DEC_IN:
    push ax

    xor bx, bx ; zero a register for our number
.top:
    mov ah, 1
    int 21h
    cmp al, 13
    je .done
    cmp al, '0'
    jb error ; jl is for signed comparisons
    cmp al, '9'
    ja error ; jg is for signed
; okay, we have a good digit
    sub al, '0'
; multiply "result so far" by 10
    imul bx, 10 ; available on true 8086?
; get rid of the junk in al
    mov ah, 0
; NOW we can add it it bx
    add bx, ax
    jmp .top
.done:
    pop ax
    ret

I'll let you figure out the rest of it - this answer's getting too long. Ask again if you get stuck.

Upvotes: 4

Related Questions