Absolute Value ofASM/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:

  1. Input number is positive
  2. Input number is negative
  3. Input is 8000h
If the input number is positive the 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.
If the input number is negative the 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,dx
is an exact replacement for (if DX = FFFFh)
        neg     ax
This 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.
You can implement and use this gem on any of the CPU's ranging from 8086 to the new Pentium II. There are however problems with it: The problem is the 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: There are some disadvantages, mainly that we can not use it on a 8086 anymore, the problem beeing the 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.
The Pentium+ "optimized" algorithm will look like this:
;
; 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,dx
Note: 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.)
Gem writers: Tylisha C. Andersen (code)
John Eckerdal (text and code)
Contributors:Bertram Felgenhauer
Norbert Juffa
last updated: 1999-08-26