Assembly MASM Dealing with Negative Integers

2019-02-26 11:29发布

问题:

I was instructed to write a program in assembly that will carry out the following arithmetic:

((A + B) / C) * ((D - A) + E)

I've succeeded in doing this when no negative values come into to play, but suppose A = 5, B = 4, C = 3, D = 2, and E = 1. This gives us ((5 + 4) / 3) * ((2 - 5) + 1) or -6.

this is where I need help. I've done some research, and have found 2's compliment to be a solution, but I'm not sure to implement it into my code.

If someone could help me, I'd be very grateful!

INCLUDE Irvine32.inc
; ((A + B) / C) * ((D - A) + E)
.data
valA dword 1
valB dword 2
valC dword 3
valD dword 4
valE dword 5

.code
main PROC

    mov ecx, valA
    add ecx, valB
    mov edx, valC
    call Divide
    mov ecx, eax
    mov edx, valD
    sub edx, valA
    add edx, valE
    call Multiply

    exit

main ENDP

*Divide and Multiply Procedures divide and multiply respectively.

回答1:

Irvines's WriteDec should be replaced by WriteInt which handles the argument EAX as signed number.

Inside the CPU, the negative "-2" and the positive "4294967294" are transformed to the same value: 0xFFFFFFFE. DIV performs the division 6/-2 positively (6/4294967294) and gets the result 0 = 0x00000000, with IDIV the result is correct: -3 = 0xFFFFFFFD.

MUL and IMUL differ in the high part of the result (EDX). Since the high part is not needed in this case, it is not mandatory to use IMUL.

There are no different versions of ADD and SUB for signed and unsigned numbers. This was the main reason for introducing the 2's complement coding. It's just a thing of interpretation: if the programmer decides that this should be a signed number, then it is a signed number. If he/she/it decides that this is an unsigned number, then it is an unsigned number. The CPU doesn't care about such things - the results will be always the same.

Here's an example with WriteInt, IDIV and IMUL:

; ((A + B) / C) * ((D - A) + E)
INCLUDE Irvine32.inc

.DATA
valA dword 5
valB dword 4
valC dword 3
valD dword 2
valE dword 1

.CODE
main PROC
    mov ecx, valA
    add ecx, valB
    mov edx, valC
    call Divide

    mov ecx, eax
    mov edx, valD
    sub edx, valA
    add edx, valE
    call Multiply

    call WriteInt           ; Write a positive or negative number

    exit
main ENDP

Divide PROC USES ECX EDX    ; EAX = ECX / EDX
    mov eax, ecx
    mov ecx, edx
    xor edx, edx
    idiv ecx                ; Signed division, e.g 6/-3 = -2
    ret
Divide ENDP

Multiply PROC USES ECX EDX  ; EAX = ECX * EDX
    mov eax, edx
    imul ecx                ; Signed multiplication
    ret
Multiply ENDP

END main

A 2's complement calculation is needed to get the absolute value of the number. E.g. the representation of -2 has two parts: a sign ('-') and an absolute value ('2'). A simple way to get the absolute value is to look at the sign bit, the leftmost bit of the number, and to jump appropriate. The calculation itself is performed just by NEG.

Example with WriteDec, IDIV and IMUL:

; ((A + B) / C) * ((D - A) + E)
INCLUDE Irvine32.inc

.DATA
valA dword 5
valB dword 4
valC dword 3
valD dword 2
valE dword 1

.CODE
main PROC
    mov ecx, valA
    add ecx, valB
    mov edx, valC
    call Divide

    mov ecx, eax
    mov edx, valD
    sub edx, valA
    add edx, valE
    call Multiply

    test eax, eax           ; Set the flags according to (EAX AND EAX)
    jns J1                  ; Skip the next block if EAX is positive (no sign)

        ; EAX is negative
        push eax            ; Preserve EAX
        mov al, '-'         ; Write the letter '-'
        call WriteChar      ; http://programming.msjc.edu/asm/help/index.html?page=source%2Firvinelib%2Fwritechar.htm
        pop eax             ; Restore EAX
        neg eax             ; 2's complement

    J1:
    call WriteDec           ; Write EAX as positive number

    exit
main ENDP

Divide PROC USES ECX EDX    ; EAX = ECX / EDX
    mov eax, ecx
    mov ecx, edx
    xor edx, edx
    idiv ecx                ; signed division, e.g 6/-3 = -2
    ret
Divide ENDP

Multiply PROC USES ECX EDX  ; EAX = ECX * EDX
    mov eax, edx
    imul ecx                ; signed multiplication
    ret
Multiply ENDP

END main

Here is an algorithm to get the absolute value of EAX without jump:

cdq
xor eax, edx
sub eax, edx


回答2:

On a twos complement machine, add and sub operations are actually the same for signed and unsigned quantities, so those parts of your program don't need to change. There are specific instructions for signed division and multiplication, so make sure the functions use those (or just use them directly).