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