Assembler Routines for beginners...
- Michael Vogel
- Addict
- Posts: 2797
- Joined: Thu Feb 09, 2006 11:27 pm
- Contact:
Assembler Routines for beginners...
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!
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!
- Psychophanta
- Always Here
- Posts: 5153
- Joined: Wed Jun 11, 2003 9:33 pm
- Location: Anare
- Contact:
I have a good collection:
some small pieces:
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
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.
Requires inline ASM turned on.
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)
@}--`--,-- A rose by any other name ..
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
@}--`--,-- A rose by any other name ..
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
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
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
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:
Comparing it to your code:
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:
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.
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
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
Paul.
a real pb procedure of Dioxin's code:
and this is the fastest method i've seen. Also one of the simplest 
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

Perhaps it IS the simplestdioxin 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.

True, dioxin. You can always make a macro of it
