Howdy;
I am currently working on a program to review what I have learned so far, in the PCASM book written by Dr. Paul Carter. I have accomplished 3/4ths of my overall goal, however; my program doesn't seem to like me especially the division part of it.

The goal of my program is: output the sum, difference, product, and quotient of 2 numbers inputted by the user.
My question: How come my division doesn't seem to want to work?
Here is what I have so far:

; file math_review.asm
; This program, will aks the user to input two numbers,
; then based on user input, output the sum, difference,
; product and quotient of the specified values

; using Linux and gcc:
; nasm -f elf math_review.asm
; gcc -o math_review math_review.o driver.c asm_io.o    

%include "asm_io.inc"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
segment .data
;
; Input prompts
;
user_input_prompt_1 db      "Enter first integer: ", 0
user_input_prompt_2 db      "Enter second integer: ", 0
;
; Output strings
;
sum_of_input        db      "Sum: ", 0
diff_of_input       db      "Difference: ", 0
prod_of_input   db      "Product: ", 0
quot_of_input   db      "Quotient: ", 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
segment .bss
;
; labels that represent double words that will store user input
;
user_input_1        resd    1
user_input_2        resd    1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
segment .text
    global asm_main
asm_main:
    enter   0, 0
    pusha
;
; get first user input
;
    mov eax, user_input_prompt_1
    call        print_string
    call        read_int
    mov [user_input_1], eax
;
; get second user input
;
    mov eax, user_input_prompt_2
    call        print_string
    call        read_int
    mov [user_input_2], eax
;
; addition
;
    mov eax, [user_input_1]
    add eax, [user_input_2]
    mov ebx, eax
    mov eax, sum_of_input
    call        print_string
    mov eax, ebx
    call        print_int
    call        print_nl
;
; subtraction
;
    mov eax, [user_input_1]
    sub eax, [user_input_2]
    mov ebx, eax
    mov eax, diff_of_input
    call        print_string
    mov eax, ebx
    call        print_int
    call        print_nl
;
; multiplication
;
    mov eax, [user_input_1]
    imul    eax, [user_input_2]
    mov ebx, eax
    mov eax, prod_of_input
    call        print_string
    mov eax, ebx
    call        print_int
    call        print_nl
;
; division
;
    mov eax, [user_input_1]
    idiv    eax, [user_input_2]
    mov ebx, eax
    mov eax, quot_of_input
    call        print_string
    mov eax, ebx
    call        print_int
    call        print_nl
;
; el fin
;
    popa
    mov eax, 0
    leave
    ret

a few of the methods that you see have been provided by the textbook, these are what specified methods do:
print_string - prints the string within the EAX register
read_int - reads user input (integer), and stores it within' the EAX register
print_nl - creates a new line

Thank you for any help/suggestions.

Recommended Answers

All 7 Replies

Excerpt from Intel's manual

Divides the (signed) value in the AX, DX:AX, or EDX:EAX (dividend) by the source operand (divisor) and stores the result in the AX (AH:AL), DX:AX, or EDX:EAX registers. The source operand can be a general-purpose register or a memory location. The action of this instruction depends on the operand size (dividend/divisor).

Nullify EDX @ line 89.

I am unsure of what you mean, I didn't even know that EDX was involved with the line, idiv eax, [user_input_2], do you mean change EAX into EDX? or am I totally overlooking something? Any help would be appreciated, thank you.

A div always involves two registers, but these registers depend on the size of your operand:

  • DIV BL divides AX by BL
  • DIV BX divides DX:AX by BX
  • DIV EBX divides EDX:EAX by EBX

As you can see, an 8-bit operand divides AX, a 16-bit operand divides DX:AX and a 32-bit operand divides EDX:EAX. (Please note the the semicolon merely means concatenation, not a segment:offset pair).

So in your case, your operand size is 32 bits wide, so it'll be dividing EDX:EAX by [user_input_2]. Who knows what bogus/rubbish value might be in EDX, so it is good practice to zero-it out:

xor edx, edx

Hope this helps.

I am unsure of what you mean, I don't understand how I would implement xor edx, edx. Would I place that in before of the idiv instruction, after, or am I way off? Also what about 64 bit stuff?... I'm just curious how that would work because I understand 8/16/32 bit division (conceptually ... obviously not practically) about what you said, however; how does 64 bit work? Also, how come division compared to the other basic operations is so special?

After some tweaking I have the division sort of working, it only works if one operand perfectly divides into another, I would really like to also output the remainder. What would I have to do in order to output the remainder? Thank you for any help, and my newest code is below

; file math_review.asm
; This program, will aks the user to input two numbers,
; then based on user input, output the sum, difference,
; product and quotient of the specified values

; using Linux and gcc:
; nasm -f elf math_review.asm
; gcc -o math_review math_review.o driver.c asm_io.o    

%include "asm_io.inc"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
segment .data
;
; Input prompts
;
user_input_prompt_1 db      "Enter first integer: ", 0
user_input_prompt_2 db      "Enter second integer: ", 0
;
; Output strings
;
sum_of_input        db      "Sum: ", 0
diff_of_input       db      "Difference: ", 0
prod_of_input   db      "Product: ", 0
quot_of_input   db      "Quotient: ", 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
segment .bss
;
; labels that represent double words that will store user input
;
user_input_1        resd    1
user_input_2        resd    1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
segment .text
    global asm_main
asm_main:
    enter   0, 0
    pusha
;
; get first user input
;
    mov eax, user_input_prompt_1
    call        print_string
    call        read_int
    mov [user_input_1], eax
;
; get second user input
;
    mov eax, user_input_prompt_2
    call        print_string
    call        read_int
    mov [user_input_2], eax
;
; addition
;
    mov eax, [user_input_1]
    add eax, [user_input_2]
    mov ebx, eax
    mov eax, sum_of_input
    call        print_string
    mov eax, ebx
    call        print_int
    call        print_nl
;
; subtraction
;
    mov eax, [user_input_1]
    sub eax, [user_input_2]
    mov ebx, eax
    mov eax, diff_of_input
    call        print_string
    mov eax, ebx
    call        print_int
    call        print_nl
;
; multiplication
;
    mov eax, [user_input_1]
    imul    eax, [user_input_2]
    mov ebx, eax
    mov eax, prod_of_input
    call        print_string
    mov eax, ebx
    call        print_int
    call        print_nl
;
; division
;
    mov eax, [user_input_1]
    cdq
    mov ecx, [user_input_2]
    idiv    ecx
    mov ebx, eax
    mov eax, quot_of_input
    call        print_string
    mov eax, ebx
    call        print_int
    call        print_nl
;
; el fin
;
    popa
    mov eax, 0
    leave
    ret

After some skimming around my textbook, I have successfully accomplished my goal for this program. Therefore, I'll set it as solved, but I do have a few questions. Ill post my questions below my code, because I have some questions in regards to what I wrote.

; file math_review.asm
; This program, will aks the user to input two numbers,
; then based on user input, output the sum, difference,
; product and quotient of the specified values

; using Linux and gcc:
; nasm -f elf math_review.asm
; gcc -o math_review math_review.o driver.c asm_io.o    

%include "asm_io.inc"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
segment .data
;
; Input prompts
;
user_input_prompt_1 db      "Enter first integer: ", 0
user_input_prompt_2 db      "Enter second integer: ", 0
;
; Output strings
;
sum_of_input        db      "Sum: ", 0
diff_of_input       db      "Difference: ", 0
prod_of_input   db      "Product: ", 0
square_of_prod  db      "Squared: ", 0
quot_of_input   db      "Quotient: ", 0
rem_of_quo      db      "Remainder: ", 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
segment .bss
;
; labels that represent double words that will store user input
;
user_input_1        resd    1
user_input_2        resd    1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
segment .text
    global asm_main
asm_main:
    enter   0, 0
    pusha
;
; get first user input
;
    mov eax, user_input_prompt_1
    call        print_string
    call        read_int
    mov [user_input_1], eax
;
; get second user input
;
    mov eax, user_input_prompt_2
    call        print_string
    call        read_int
    mov [user_input_2], eax
;
; summation
;
    mov eax, [user_input_1]
    add eax, [user_input_2]
    mov ebx, eax
    mov eax, sum_of_input
    call        print_string
    mov eax, ebx
    call        print_int
    call        print_nl
;
; difference
;
    mov eax, [user_input_1]
    sub eax, [user_input_2]
    mov ebx, eax
    mov eax, diff_of_input
    call        print_string
    mov eax, ebx
    call        print_int
    call        print_nl
;
; product
;
    mov eax, [user_input_1]
    imul    eax, [user_input_2]
    mov ebx, eax
    mov eax, prod_of_input
    call        print_string
    mov eax, ebx
    call        print_int
    call        print_nl
;
; product squared
;
    mov eax, square_of_prod
    call        print_string
    mov eax, [user_input_1]
    imul    eax, [user_input_2]
    mov ebx, eax
    mul eax
    call        print_int
    call        print_nl
;
; division
;
    mov eax, [user_input_1]
    cdq
    mov ecx, [user_input_2]
    idiv    ecx
    mov ebx, eax
    mov eax, quot_of_input
    call        print_string
    mov eax, ebx
    call        print_int
    call        print_nl
;
; remainder
;
    mov eax, rem_of_quo
    call print_string
    mov eax, ecx
    call print_int
    call print_nl
;
; el fin
;
    popa
    mov eax, 0
    leave
    ret

Question 1 : In regards to the squared section of the code, is there any other way I can find the square of the product? If so, could you please explain it as 'down-to-earth' as possible.
Question 2 : In the division section of the code, I am still trying to grasp the basic concept of why this actually works, I understand the whole thing about working with different bit registers, however; why do I have to use cdq? Also where would I use xor edx, edx?
Question 3 : In the remainder part of the code, I am unsure as to why it works as it does. It seems that the remainder magically lies in the ecx register. Is this the case everytime, sometimes, or just a strike of luck?

Thank you once again

I am unsure of what you mean, I don't understand how I would implement xor edx, edx. Would I place that in before of the idiv instruction, after, or am I way off?

It must be before the div or idiv instruction. Imagine that before you called a div, but EDX had a value in it that was put there from a previous piece of code:

; EDX might equal, say, 0x00003EF1 because of previous code
mov eax, 0xE0F253DC
div 0x10

DIV would divide EDX:EAX (0x00003EF1E0F253DC) by 0x10 instead of only dividing EAX. To fix this, you'd zero-out EDX before doing the division, but after any instruction that modifies it. It's safest to zero it out right before it's needed to be zero, so in this case, right before your div. With EDX zeroed-out, div would be dividing by 0x00000000E0F253DC, which is what we wanted in that example.

Also what about 64 bit stuff?... I'm just curious how that would work because I understand 8/16/32 bit division (conceptually ... obviously not practically) about what you said, however; how does 64 bit work?

64-bit stuff... That's a good question. I don't have a 64-bit system but I'm damned sure it'd divide the 128-bit value RDX:RAX by your operand.

In the division section of the code, I am still trying to grasp the basic concept of why this actually works, I understand the whole thing about working with different bit registers, however; why do I have to use cdq? Also where would I use xor edx, edx?

Taken from http://faydoc.tripod.com/cpu/cdq.htm:

The CDQ instruction copies the sign (bit 31) of the value in the EAX register into every bit position in the EDX register.

In the case of a signed division (which is what you're doing), CDQ should really be used instead of xor edx,edx because it sign-extends the number instead of mangling it.

Also, how come division compared to the other basic operations is so special?

The reason that the dividend is two registers is because the quotient is only ever going to be smaller than the dividend (unless for some reason you're dividing by 1).

Just FYI, I wrote a table a couple of years ago to help with div:

Divisor Size        : 1 Byte    : 2 Bytes   : 4 Bytes
------------------------------------------------------------------------
Dividend            : AX        : DX:AX     : EDX:EAX
Remainder           : AH        : DX        : EDX
Quotient            : AL        : AX        : EAX
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.