Search code examples
assemblyaveragemasmemu8086

How to calculate average? (masm x86 8086)


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

Solution

  • 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.