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?
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?
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.
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
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
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.