Reputation: 45
I need to write in asm 8086 a program like b=a/6 but without the DIV instruction. I know how to do it with SAR but only 2,4,8,16...
mov ax,a
sar ax,1 ;//div a by 2
mov b,ax
my question is how can I do it to div by 6?
Upvotes: 3
Views: 5452
Reputation: 6297
Since you've used the sar
instruction in your example, I assume you need signed division by 6.
The following code uses multiplication by the reciprocal constant. It rounds the result down to −∞:
mov ax,0x2AAB ; round(0x10000/6)
imul A ; DX:AX contains the signed 32-bit result of the multiplication
mov B,dx ; take only the upper 16 bits
If you need the result rounded towards 0, increase negative results by one:
mov ax,0x2AAB
imul A
test dx,dx ; set FLAGS according to DX
jns skip
inc dx ; increase DX if it was negative
skip: mov B,dx
Finally, if you need unsigned division by 6, you need a more precise constant and a shift:
mov ax,0xAAAB ; round(0x40000/6)
mul A ; DX:AX contains the unsigned 32-bit result of the multiplication
shr dx,1
shr dx,1 ; shift DX right by 2 (8086 can't do "shr dx,2")
mov B,dx
Upvotes: 1
Reputation: 71506
From grade school
x/6 = x * 1/6;
Look at what happens when you let the C compiler do it
unsigned short fun ( unsigned short x )
{
return(x/6);
}
32 bit x86
0000000000000000 <fun>:
0: 0f b7 c7 movzwl %di,%eax
3: 69 c0 ab aa 00 00 imul $0xaaab,%eax,%eax
9: c1 e8 12 shr $0x12,%eax
c: c3 retq
32 bit arm
00000000 <fun>:
0: e59f3008 ldr r3, [pc, #8] ; 10 <fun+0x10>
4: e0802093 umull r2, r0, r3, r0
8: e1a00120 lsr r0, r0, #2
c: e12fff1e bx lr
10: aaaaaaab
same story. Translate that to 8086.
so 6 = 3 * 2, so we really need to divide by 3. then adjust
unsigned short fun ( unsigned short x )
{
return(x/3);
}
00000000 <fun>:
0: e59f3008 ldr r3, [pc, #8] ; 10 <fun+0x10>
4: e0802093 umull r2, r0, r3, r0
8: e1a000a0 lsr r0, r0, #1
c: e12fff1e bx lr
10: aaaaaaab
one less bit of shift. One of the shifts is to increase precision the other is because there is a divide by 2 in there.
You can do the subtraction loop of course. Otherwise it is long division which is actually pretty easy to code.
Upvotes: 0
Reputation: 32732
The approach given an another answer is simple brute force loop, and can take a while for large values of a
. This is a version that uses larger chunks (working it like a long division problem) specifically coded to divide a signed number by 6:
; signed divide by 6
mov ax,a
mov cx,1000h ; initial count of how many divisors into ax to check for
mov bx,6000h ; value of "divisor * cx"
xor dx,dx ; result
top:
cmp ax,bx
jl skip
; we can fit "cx" copies of the divisor into ax, so tally them
add dx,cx
sub ax,bx
; optionally can have a "jz done" here to break out of the loop
skip:
shr bx,1
shr cx,1
jnz top
; copy result into ax
mov ax,dx
If you need to divide something other than 6, the initial cx
and bx
values need to be adjusted. cx
is the power-of-two multiple of the divisor that leaves bit 14 set (since bit 15 is the sign bit; for an unsigned divide you'd want to have bit 15 set instead). bx
is that power of 2. If there are limits on the initial value for a
you can adjust the initial cx
and bx
values, but have to be careful because you'll get an incorrect answer if you make them too small.
Upvotes: 3
Reputation: 20693
you can use subtraction and count how many times it take to get to zero, eg. 30/6=5 and 30-6-6-6-6-6=0 so for 30 you must 5 times subtract 6 to get to zero
Something like that:
mov cx,0
mov ax, dividend
divloop:
cmp ax, 0
jle done
sub ax, divisor
inc cx
jmp divloop
done:
;result is in cx
Upvotes: 1