0

I am trying to find the average of two user inputted numbers on MASM x86 (im using 8086). I cannot seem to calculate the average!! I can get the two numbers to multiply but I do not have a clue on how to add them and then divide them by the total amount of numbers(which in my case it is only 2). Here is what i have so far(and yes i realize that i am multiplying, but that is to only show that I did attempt something, I just cant get them to add and divide the sum):

.model small
org 100h
.data

num1 db ?
num2 db ?
result db ? 
usermsg db "Enter EVEN numbers only.$"
msg1 db 13, 10, "Enter first number: $"
msg2 db 13, 10, "Enter second number: $"
msg3 db 13, 10, "The average is: $"

.code

main proc
mov ax, @data
mov ds, ax

lea dx, usermsg
mov ah, 09h
int 21h

lea dx, msg1
mov ah, 09h
int 21h

mov ah, 01h
int 21h

sub al, '0'
mov num1, al 
mov dl, al

lea dx, msg2
mov ah, 09h
int 21h

mov ah, 01h
int 21h
sub al, '0'
mov num2, al

mul num1


;add al, num1

mov result, al


idiv result, 2 ;new code
aam

add ah, '0'
add al, '0'
mov bx, ax

lea dx, msg3
mov ah, 09h
int 21h

mov ah, 02h
mov dl, bh
int 21h
mov dl, bl
int 21h

mov ax, 4c00h
int 21h
Michael Gaskill
  • 7,913
  • 10
  • 38
  • 43
howard howard
  • 49
  • 1
  • 6

2 Answers2

3

Just add your numbers in a register and divide. If they're small enough for the sum not to overflow, then it's easy.

If the you know ahead of time that you're only averaging 2 number (or any power of 2), divide using a shift.

...  your original code that gets two digits from the user
sub   al, '0'
; first number in [num1] in memory, second number in al
; We know they're both single-digit numbers, so their sum will fit in 8bits


add   al, [num1]    ; or whever you put num1: a register like cl would be a good choice, instead of memory
shr   al, 1         ;  al = al/2  (unsigned)

;; al holds the average.  Do whatever else you want.

mov   [result], al  
add   al, '0'       ; convert back to ASCII

You can average two ASCII digits without subtracting and re-adding '0', to save instructions. If asc='0' (i.e. 0x30), then

  (a+asc + b+asc) / 2
= (a+b)/2 + (asc+asc)/2
= (a+b)/2 + asc        i.e. the average as an ASCII digit

Thus:

add  al, [asciidigit1]
shr  al, 1

e.g. '5' + '8' = 0x6d. 0x6d>>1 = 0x36 = '6'.


Problems with your idiv:

There's no form of idiv that takes an immediate operand. The dividend is implicit, and the divisor is the one explicit operand. The quotient goes in AL, and the remainder goes in AH. (This is the opposite of AAM, which does accept an immediate operand, but only divides AL, not AX).

See this answer to another question where I demonstrate using div or aam to turn an integer into two ASCII digits (and print them with DOS system calls, because that's what the OP of that question wanted).

See also other links in the tag wiki.

Community
  • 1
  • 1
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • Thank you so much for taking the time to answer my question. I do have another question now, so I would essentially have to print the contents of "result" to have the avg print out? – howard howard May 16 '16 at 22:37
  • Also, the user will only enter two even number integers always, so dividing by two to get the average would be fine. – howard howard May 16 '16 at 22:38
  • @howardhoward: Yeah, when you're done, the result is an ASCII digit in `al`. If you were writing a function (other than `main()`), then the normal thing would be to just `ret`, with the return value in `eax`, `ax`, or `al` (depending on the return type). You can have the function store it into `[result]` in static memory, but there's no need for that. If it was a function, then the caller could print the return value of do whatever it wanted. Since this code is just part of your main program, then sure print `al` as an ASCII character. – Peter Cordes May 17 '16 at 00:06
-1

Dont know much about asm, but are you sure you can use idiv like that?

This: http://www.electronics.dit.ie/staff/tscarff/8086_instruction_set/8086_instruction_set.html#IDIV says you would load 'result' into AX and then just go idiv 2, and the result would go in AL. So I guess you'd try

;add al, num1

movzx ax, al
mov   dl, 2
idiv  dl

AL will then contain the result (quotient) of the division, and AH the remainder.


Alternatively, because you are dividing by 2, you should just shift right by 1. The destination can be a register, or memory

shr result,1

will shift 'result' right by 1 bit and store the answer in 'result'

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
matt
  • 4,042
  • 5
  • 32
  • 50
  • You're right that there is no form of `idiv` that takes an immediate operand. `AAM` does, but only divides AL, not AX, and puts the results in opposite registers. However, `shr` is by far the better suggestion. `div` is slow. – Peter Cordes May 16 '16 at 05:52
  • Also, `mov ax, al` doesn't work. To zero-extend, just zero AH, or (386 and later) `movzx ax, al`. To sign-extend, `movsx ax,al` or `cbw`. – Peter Cordes May 16 '16 at 05:54
  • A better url would be [`this one for `idiv`](http://www.felixcloutier.com/x86/IDIV.html). – Peter Cordes May 16 '16 at 05:55
  • 1
    Thanks for your help! I really appreciate it! – howard howard May 16 '16 at 22:37