Page 1 of 2
Assembler Routines for beginners...
Posted: Fri May 19, 2006 11:01 am
by Michael Vogel
I'm interested to find a small collection of tiny procedures, written in assembler to learn from it. I've searched around in the forum, but because of the differences of PB4 to older versions, it's not easy to find the "right" way for programming such snippets...
The most important target is to be able to write (reallly small) functions which could be usefull sometimes, like
* Bit operations: SetBit ClrBit CheckBit
* Math functions: Min(), Max(), Int(), Frac()
So if anyone can put here some nice code parts (working with PB4), that would be cool!
Posted: Fri May 19, 2006 1:17 pm
by Psychophanta
I have a good collection:
some small pieces:
Code: Select all
Procedure.f RoundASM(num.f)
!fld dword[p.v_num]
!frndint
ProcedureReturn
EndProcedure
Code: Select all
Procedure.l rol12(n.l,b.l); <- rotate left the bits in a 12bit value
!mov ecx,12
!xor edx,edx
!mov eax,dword[p.v_b]
!div ecx
!mov ecx,edx
!mov ax,word[p.v_n]
!mov bx,ax
!shl bx,4
!shld ax,bx,cl
!and ax,$0fff
ProcedureReturn
EndProcedure
Procedure.l ror12(n.l,b.l); <- rotate right the bits in a 12bit value
!mov ecx,12
!xor edx,edx
!mov eax,dword[p.v_b]
!div ecx
!mov ecx,edx
!add ecx,4
!mov ax,word[p.v_n]
!mov bx,ax
!shl ax,4
!shrd ax,bx,cl
!and ax,$0fff
ProcedureReturn
EndProcedure
Procedure.l rol24(n.l,b.l); <- rotate left the bits in a 24bit value
!mov ecx,24
!xor edx,edx
!mov eax,dword[p.v_b]
!div ecx
!mov ecx,edx
!mov eax,dword[p.v_n]
!mov ebx,eax
!shl ebx,8
!shld eax,ebx,cl
!and eax,$00ffffff
ProcedureReturn
EndProcedure
Procedure.l ror24(n.l,b.l); <- rotate right the bits in a 24bit value
!mov ecx,24
!xor edx,edx
!mov eax,dword[p.v_b]
!div ecx
!mov ecx,edx
!add ecx,8
!mov eax,dword[p.v_n]
!mov ebx,eax
!shl eax,8
!shrd eax,ebx,cl
!and eax,$00ffffff
ProcedureReturn
EndProcedure
Code: Select all
Procedure.d ATan2(y.d,x.d)
!fld qword[p.v_y]
!fld qword[p.v_x]
!fpatan
ProcedureReturn
EndProcedure
Code: Select all
Procedure.l CountChars(a.s,s.b)
!mov edi,dword[p.v_a] ;pointer to the first character in string (first function parameter)
;firstly we must find the lenght of the string:
!;cld ;clear DF (Direction Flag). (normally not necessary; cleared by default)
!xor ebx,ebx ;init counter to NULL
!mov al,bl ;set NULL character in AL register
!mov ecx,ebx ;lets set 4294967295 ($FFFFFFFF) characters maximum
!dec ecx
!repnz scasb ;repeat comparing AL CPU register content with byte[edi]
!jecxz near _CountCharsgo ;if NULL byte is not found within those 4294967295 characters then exit returning 0
!not ecx ;else, some adjusts. Now we have the lenght at ecx register
!mov edi,dword[p.v_a] ;point again to the first character in string (first function parameter)
!mov al,byte[p.v_s] ;al=character to find
!@@:REPNZ scasb ;repeat comparing AL CPU register content with byte[edi]
!jecxz near _CountCharsgo ;until ecx value is reached
!inc ebx ;or a match is found
!jmp near @r ;continue comparing next character
!_CountCharsgo:MOV eax,ebx ;output the matches counter
ProcedureReturn
EndProcedure
Posted: Fri May 19, 2006 1:52 pm
by Dare2
These appear to be a bit of a duplication of Pyschophanta's stuff, but may:
A: Be useful
B: Get some feedback from others that is useful to us both.
Code: Select all
Procedure LogicalShR(a,b)
MOV eax,a
MOV ecx,b
SHR eax,cl
ProcedureReturn
EndProcedure
Procedure LogicalShL(a,b)
MOV eax,a
MOV ecx,b
SHL eax,cl
ProcedureReturn
EndProcedure
Procedure LogicalRoR(a,b)
MOV eax,a
MOV ecx,b
ROR eax,cl
ProcedureReturn
EndProcedure
Procedure LogicalRoL(a,b)
MOV eax,a
MOV ecx,b
ROL eax,cl
ProcedureReturn
EndProcedure
v1=$01 : Debug Hex(v1) : v1 = LogicalRoR(v1,1) : Debug Hex(v1)
Requires inline ASM turned on.
Posted: Fri May 19, 2006 2:35 pm
by Dare2
Some more, again smarter minds will have better stuff:
Code: Select all
Procedure SetBit(a,b) ; Set bit 'b' of 'a' (bit 0 is rightmost bit)
MOV eax,1
MOV ecx,b
SHL eax,cl
OR eax,a
ProcedureReturn
EndProcedure
Procedure ClrBit(a,b) ; Clear bit 'b' of 'a' (bit 0 is rightmost bit)
MOV eax,a
MOV ecx,b
ROR eax,cl
AND al,$FE
ROL eax,cl
ProcedureReturn
EndProcedure
Procedure GetBit(a,b) ; Get bit 'b' of 'a' (bit 0 is rightmost bit)
MOV eax,1
MOV ecx,b
SHL eax,cl
AND eax,a
SHR eax,cl
ProcedureReturn
EndProcedure
For i = 7 To 0 Step -1
Debug RSet(Bin(SetBit($0,i)),8,"0")
Next
Debug "---"
For i = 7 To 0 Step -1
Debug RSet(Bin(ClrBit($0FF,i)),8,"0")
Next
Debug "---"
For i = 7 To 0 Step -1
w.s + Str(GetBit($55,i))
Next
Debug w
Posted: Fri May 19, 2006 3:50 pm
by Pupil
Code: Select all
Procedure.l Min(a.l, b.l)
MOV eax, a
MOV ebx, b
XOr edx, edx
CMP eax, ebx
SETLE dl
SUB edx, 1
And ebx, edx
NOT edx
And eax, edx
Or eax, ebx
ProcedureReturn
EndProcedure
Procedure.l Max(a.l, b.l)
MOV eax, a
MOV ebx, b
XOr edx, edx
CMP eax, ebx
SETLE dl
SUB edx, 1
And eax, edx
NOT edx
And ebx, edx
Or eax, ebx
ProcedureReturn
EndProcedure
Procedure.l SetBit(a.l, bit.l)
MOV eax, a
MOV edx, bit
BTS eax, edx
ProcedureReturn
EndProcedure
Procedure.l ClearBit(a.l, bit.l)
MOV eax, a
MOV edx, bit
BTC eax, edx
ProcedureReturn
EndProcedure
Procedure.l GetBit(a.l, bit.l)
XOr eax, eax
MOV edx, bit
BT a, edx
SETC al
ProcedureReturn
EndProcedure
Posted: Sat May 20, 2006 8:25 pm
by dagcrack
Those are some slow min/max routines you've posted

(no sarcasm, just saying this after a few benchmarks I did with my own routines).
Posted: Sat May 20, 2006 8:47 pm
by dioxin
Code: Select all
'MIN
!mov eax,a
!mov ecx,b
!cmp eax,ecx
!cmovg eax,ecx
'min value is now in eax
'MAX
!mov eax,a
!mov ecx,b
!cmp ecx,eax
!cmovg eax,ecx
'max value is now in eax
but note that the CMOV instruction only came in with the PentiumPro so not all Pentiums have it.
Posted: Sun May 21, 2006 1:25 am
by rsts
dagcrack wrote:Those are some slow min/max routines you've posted

(no sarcasm, just saying this after a few benchmarks I did with my own routines).
Care to share yours so we can learn from them?
cheers
Posted: Sun May 21, 2006 4:04 am
by Dare2
Come on dagcrack, stop being a voyeur at a nudist beach.

Show us what you've got!
Or keep everything zipped up.

(no sarcasm <-
and just saying that because it seems to mean that put-downs suddenly become acceptable behaviour)
Posted: Sun May 21, 2006 11:55 am
by Pupil
Actually i had not benchmarked the min max routines. My intention with the routines was to skip conditional jumps and hopefully avoid having the cpu flush the pipeline because of jump prediction misses. However this seemed a futile effort as even the following code will execute faster
Code: Select all
Procedure.l Min(a.l, b.l)
If a < b
Procedurereturn a
Else
Procedurereturn b
EndProcedure
Posted: Sun May 21, 2006 12:46 pm
by dioxin
Pupil,
avoiding jumps is a useful technique but not when replacing one jump takes up so much extra code.
The simple way to do a MIN or MAX is:
Code: Select all
!mov eax,a
!mov ecx,b
!cmp eax,ecx
!jg skip
!mov eax,ecx
skip:
eax now contains the result
Comparing it to your code:
Code: Select all
you've replaced this:
!jg skip
!mov eax,ecx
with this:
XOr edx, edx
SETLE dl
SUB edx, 1
And ebx, edx
NOT edx
And eax, edx
Or eax, ebx
So you replaced a short 2 opcode/4 byte sequence with a 7 opcode/16 byte sequence.
Also, if you look at the code, almost every line is directly dependant on the result of the previous line (edx is used in 6 of the 7 lines) so the CPU is forced to execute every line in sequence, it has no opportunity to do things in parallel.
If you need to avoid the CMOV instruction (as not all CPUs have it) then here is a clip from the Athlon Optimisation manual on how to do MIN:
Code: Select all
Example 4 Unsigned integer min function (z = x < y ? x : y):
MOV EAX, [x] ;load x
MOV EBX, [y] ;load y
SUB EAX, EBX ;x < y ? CF : NC ; x - y
SBB ECX, ECX ;x < y ? 0xffffffff : 0
AND ECX, EAX ;x < y ? x - y : 0
ADD ECX, EBX ;x < y ? x - y + y : y
MOV [z], ECX ;x < y ? x : y
This one replaces a 2 opcode/4 byte sequence with a 4 opcode/8 bytes sequence to avoid the jump. Still not as good as using CMOV but in cases where the data is random (and therefore the CPU's jump prediction is only 50% effective) it can beat the version with a jump.
Paul.
Posted: Sun May 21, 2006 12:49 pm
by thefool
a real pb procedure of Dioxin's code:
Code: Select all
Procedure.l Dioxi_Min(a.l, b.l)
!mov eax,dword[p.v_a]
!mov ecx,dword[p.v_b]
!cmp eax,ecx
!cmovg eax,ecx
ProcedureReturn
EndProcedure
and this is the fastest method i've seen. Also one of the simplest

Posted: Sun May 21, 2006 1:18 pm
by dioxin
TheFool,
ONE of the simplest? You mean there's another, equally simple method??
I do often wonder about the usefulness of placing such short code snippets in a procedure. The procedure overheads will dwarf the time taken by the code.
Paul.
Posted: Sun May 21, 2006 1:43 pm
by thefool
dioxin wrote:TheFool,
ONE of the simplest? You mean there's another, equally simple method??
I do often wonder about the usefulness of placing such short code snippets in a procedure. The procedure overheads will dwarf the time taken by the code.
Paul.
Perhaps it IS the simplest
True, dioxin. You can always make a macro of it

Posted: Sun May 21, 2006 1:51 pm
by dioxin
As it happens, depending on circumstances, MMX/SSE stuff can make it simpler and quicker but unless you're already using MMX/SSE then the overhead outweighs the gain.