Absolute Value of | ASM/8086 |
This gem calculates the absolute value of the number you input to the routine. This gem can be optimized in two ways: size or speed. Both optimizations are presented.
Optimize for size
This small version of the algorithm is based on the fact that NEG
sets the sign flag to reflect the result of the operation.
To explain the algorithm we must look at the three possible
cases:
NEG
will set the sign flag to one (SF=1
) and negate the number. Then the jump will be executed and the computer jumps back to the NEG
instruction. Now the value will be negative. Negating a negative value makes it positive (and SF=0
), the computer will not execute the jump and the out value is positive.NEG
will set the sign flag to zero (SF=0
) and negate the number. Then the jump will not be executed as the sign flag is not set. The out value will therefore be positive.
If the input number is 8000h
NEG
will make the snippet go into an infinite loop.
;
; return absolute value of input
;
; input:
; ax = value
;
; output:
; ax = abs(value)
;
; destroys:
; flags
;
@locallabel:
neg ax
js short @locallabel
On older CPUs this is quite fast, on newer it is not - mainly because of the branchpredicting units. The jump in the algorithm is hard to predict and a mispredicted jump may cost as much as 20 cycles on a Pentium Pro / Pentium II (about 4 on Pentium / Pentium MMX). This is however the smallest available version of the gem.
This version of the gem is also easy to implement as a macro and can handle memory very easy:;
; abs(value) macro
;
MACRO invalue
LOCAL localloop
localloop:
neg invalue
js short localloop
ENDM
Just call the macro with any input you wish (memory offset or register).
Optimize for speed
This version of the gem is considerably faster than the previous presented version on newer CPUs. On older it is very likely that they are almost equally fast. The basic gem looks like this:;
; return absolute value of input
;
; input:
; ax = value
;
; output:
; ax = abs(value)
;
; destroys:
; dx
; flags
;
cwd
xor ax,dx
sub ax,dx
The gem uses the fact that:
xor ax,dx sub ax,dxis an exact replacement for (if
DX = FFFFh
)neg axThis is where the
CWD
comes in. If the number is negative DX
will be set to FFFFh
. If the number is positive DX
will be set to zero, and the following instructions will have no effect on AX
.AX/EAX
DX/EDX
)
CWD
instruction. To replace CWD
, you can use this combination:mov dx,ax sar dx,15(If 32-bit registers are used, shift with a value of
31
instead.) This has several advantages:CWD
on Pentium and above processors.
SAR DX,15
instruction. The 8086 can not shift with immideates greater than 1. The best option is to use the version optimized for size.; ; return absolute value of input ; ; input: ; ax = value ; ; output: ; ax = abs(value) ; ; destroys: ; dx ; flags ; mov dx,ax sar dx,15 xor ax,dx sub ax,dxNote: This version is not as easy to implement as a macro, mainly because it does destroy contents of one register (this can be fixed with a
PUSH+POP
, but that would slow down the gem). The above presented version is also not paired at all. (No pairing allowed, unless other instructions are inserted.)