it's my first time posting a question here so I'm not sure if this is the right way to do. Please let me know if I should add more information to it later.
I'm trying to write some code in emu8086, I'm new to assembly so it's still a bit confusing to me. In my assignment, for one of the exercises I have to make a code that solves: r=(x-y*z)/t. The values are: x = 2000; y = -500; z = 200; t = 300; r = ?.
I checked the solution given in our course:
x dw 2000
y dw -500
z dw 200
t dw 300
r dw ?
____________________
mov ax, y
imul z
mov cx, dx
mov bx, ax
mov ax, x
cwd
sub ax, bx
sbb dx, cx
idiv t
mov r, ax
and I don't understand why using the commands cwd and sbb is needed, and also the lines
mov cx, dx
mov bx, ax
sbb dx, cx
This is the code I wrote:
mov AX, y
imul z
mov BX, x
sub BX, AX
idiv t
mov r, BX
But these are the results I get:
x = 07D0h
y = 0FE0Ch
z = 00C8h
t = 012Ch
r = 8E70h
(https://i.stack.imgur.com/9CU9Q.png)
The value for r should be 0154h, which is 340 in decimal.
I guess the problem might be related to the multiplication resulting in a negative number, but I don't understand how to fix it.
Could someone explain the purpose of those instructions in this code, how they affect the whole process and why is my own code wrong? Besides that, are there other ways of solving this without having to use those commands?
Thank you in advance.
CodePudding user response:
What you are seeing is implementing 32 bit arithmetic using 16 bit registers. This is needed because the intermediate values of your calculation do not fit into 16 bits, and partially also because idiv
and imul
operate on 32 bit register pair dx
and ax
.
The imul z
result is -100000
but that does not fit into 16 bits and is already split into dx=0xFFFE=-2
and ax=0x7960=31072
automatically. To do the subtraction x-y*z
x
is also extended to 32 bits, using the cwd
instruction. Before that the y*z
temporary is moved away to the cx:bx
pair. The subtraction is done by first doing the low 16 bits using plain sub
and the top 16 bits using sbb
to propagate the carry (borrow). The result is left in dx:ax
which is the implicit input to the idiv
. The quotient is then produced in ax
.
Your code is wrong because the sub BX, AX
leaves the result in bx
which is not used by the idiv
at all since that operates on dx:ax
implicitly. You then store that bx
which isn't even the output of the idiv
. You also only calculated using the low 16 bits, so the result is 2000-31072=-29072=0x8E70
.
Yes, you can simulate the cwd
and the sbb
using other instructions, for example using explicit conditional jumps. cwd
just sign extends ax
into dx
meaning if ax
is negative then dx
will be set to 0xffff
and to zero otherwise. You can simply do this by hand, or fill dx
with the sign bit using arithmetic shift or if you are permitted to know that x
is always positive then just zero dx
. Instead of sbb
you can just check if there was a carry (borrow) and subtract 1 from the result if so before handling the rest with a plain sub
.