• Welcome to Jose's Read Only Forum 2023.
 

Assembler Expressions - Short but Useful

Started by Charles Pegge, October 15, 2007, 04:08:43 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

MikeTrader

#15
Charles,

In graphing, there is often a need to clamp values so they do not exceed known bounds. These functions must be called for every iteration with the atendant loss of speed.
PB has functions for doing it, but my guess is this can be done better with ASM?


'¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'
FUNCTION ClampFloat( sVar AS SINGLE, Minimum AS SINGLE, Maximum AS SINGLE ) AS SINGLE 

    FUNCTION = MAX( Minimum, MIN( Maximum, sVar ) ) ' Clamp a FLOAT variable between Minimum & Maximum values

END FUNCTION           


'¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤'
FUNCTION ClampInt( sVar AS LONG, Minimum AS LONG, Maximum AS LONG ) AS LONG 

    FUNCTION = MAX&( Minimum, MIN&( Maximum, sVar ) ) ' Clamp an INTEGER variable between Minimum & Maximum values

END FUNCTION   


Used like this for example:
MinorColorIndex = CLNG( (maxIndexf * ClampFloat( (1.0 - 13*lineDensity), 0.0, 1.0) ) )

I could probably convert the code to utilize a MACRO function.

Charles Pegge

#16
The best way to speed things up would be to prepare the unclamped data set in an array, then clamp it in bulk using assembler. This would give you two kinds of efficiency: firstly traversing the array can be done without loading an indexing variable each time, secondly, the max and min values can be held in registers, further reducing RAM accesses.

PS: removed 2 colons in the wrong place.

A very literal example:


arrayptr=varptr(myarray)
! mov esi,arrayptr
! mov ecx,0
! mov edx,elements
! mov ebx,maxval
! mov eax,minval
do1:
! cmp ecx,edx
! jge edo1
! cmp [esi+ecx*4],eax
! jge eif1
   mov [esi+ecx*4],eax
eif1:
! cmp [esi+ecx*4],ebx
! jle eif2
! mov [esi+ecx*4],ebx
eif2:
! inc ecx
! jmp do1
edo1:



This can be further optimised by eliminating the SIBs which are are not hardcore silicon but microcoded.


arrayptr=varptr(myarray)
! mov esi,arrayptr
! mov edx,elements
! mov ebx,maxval
! mov eax,minval
do1:
! dec edx
! jl edo1
! cmp [esi],eax
! jge eif1
   mov [esi],eax
eif1:
! cmp [esi],ebx
! jle eif2
! mov [esi],ebx
eif2:
! add esi,4
! jmp do1
edo1:



MikeTrader

OK.
So just to be clear, MyArrary would be declared as:

DIM MyArray(n,3) as LONG/SINGLE

For i = 1 to n
  MyArray(i,1) = Value To Clamp
  MyArray(i,2) = Min Value
  MyArray(i,3) = Max Value
Next

arrayptr=varptr(myarray)

call Clamp( arrayptr ) ' Clamp all n values in one call.

As I look at the different ways in which this is function is used in my code, I realize I could take this approach in some instances, but in others the result is immediatly used to calculate something else.

But that is not a problem, as i could just call the function with a one element array right?

Charles Pegge

You just need a single element array, assuming the clamp values are the same for all the elements.
The function might look like this:

minmax( byref myarray(0) as long, byval elements as long, byval minval as long, byval maxval as long)
If this fits your program I can produce an equivalent floating point minmax. It is slightly more tricky.

MikeTrader

>assuming the clamp values are the same for all the elements.
yes I see, perfect!
Thanks Charles.
Why are floating point versions more tricky?

Charles Pegge

#20
Well there's a bit a shuffling to do on the FPU stack, then you'll find that PB inline assembler does not support the direct comparator instructions FCOMI and FCOMIP, so opcodes have to be used for those. (Without them the older FCOM and FCOMP requires a transfer of FPU flags into the CPU flag register). Another important point is that the FPU uses the overflow flag not the sign flag, so jae (jump above or equal) and jbe (jump below or equal) must be used instead of jge and jle.

Anyway here is how it works out: tested on an array of 2 elements.


#COMPILE EXE
#DIM ALL

SUB minmax(_
  BYREF aa AS SINGLE,_
  BYVAL els AS LONG,_
  BYVAL minval AS SINGLE,_
  BYVAL maxval AS SINGLE _
  )
DIM arrayptr AS SINGLE PTR
arrayptr=VARPTR(aa)
! mov esi,arrayptr
! mov edx,els
! fld dword ptr maxval
! fld dword ptr minval
do1:
  ! dec edx
  ! jl edo1
  ! fld dword ptr [esi]
  ' fcomip st(1),st(0)
  ! db &hdf,&hf1
  ! jae eif1
  ! fst dword ptr [esi]
eif1:
  ! fld dword ptr [esi]
  ' fcomip st(2)
  ! db &hdf,&hf2
  ! jbe eif2
  ! fxch
  ! fst dword ptr [esi]
  ! fxch
eif2:
  ! add esi,4
  ! jmp do1
edo1:
  ! fcomp st(0) ' dump
  ! fcomp st(0) ' dump
END SUB


FUNCTION PBMAIN () AS LONG
DIM a(1) AS SINGLE
a(0)=-1: a(1)=1
minmax(a(0), 2 , -.5, .5 )
MSGBOX STR$ (a(0))+$CR+STR$(a(1))

END FUNCTION