CountChars function in assembler

Share your advanced PureBasic knowledge/code with the community.
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

CountChars function in assembler

Post by Psychophanta »

Code updated For 5.20+ (Same as CountString())

Code: Select all

Procedure.l CountChars(a.s,s.s)
  !mov edi,dword[esp]    ;pointer to the first character in string (first function parameter)
 ;firstly we must found the lenght of the string:
  !cld              ;clear DF (Direction Flag). Could be not necessary.
  !xor al,al       ;set NULL character in AL register
  !xor ebx,ebx  ;init counter
  !mov ecx,ebx    ;lets set 4294967295 ($FFFFFFFF) characters maximum
  !dec ecx
  !repnz scasb    ;repeat comparing AL CPU register content with [edi]
  !jecxz go    ;if NULL byte is not found within those 4294967295 characters then exit giving 0
  !not ecx     ;else, some adjusts. Now we have the lenght at ecx register
  !mov edi,dword[esp]     ;point again to the first character in string (first function parameter)
  !mov eax,dword[esp+4]
  !mov al,byte[eax]    ;al=character to find
  !@@:REPNZ scasb   ;repeat comparing AL CPU register content with [edi]
  !jecxz go     ;until ecx value is reached
  !inc ebx       ;or a match is found
  !jmp @r       ;continue comparing next character
  !go:MOV eax,ebx   ;output the matches counter
  ProcedureReturn
EndProcedure


st.s="asfhaisu f78a.wetr.q8 fsa su.f789 ay "
ch.s="."
MessageRequester("",Str(CountChars(st,ch)),0)


AL
LJ
Enthusiast
Enthusiast
Posts: 177
Joined: Wed Apr 30, 2003 4:00 pm

Alright

Post by LJ »

@Psycho

Awesome. I know you put lots of comments which is great. I'd like to post this message from you in the Inline Assembly sticky thread, but it might be too advanced. Do you think you can expand the explanation? Something that a beginner to Purebasic Inline Assembly can understand?
LarsG
Enthusiast
Enthusiast
Posts: 713
Joined: Mon Jun 02, 2003 1:06 pm
Location: Norway
Contact:

Post by LarsG »

Yes, I'm with LJ on this one.. I would love to learn this little snippet in detail... Perhaps explaining why you chose the registers you chose, and how you know where the string paramters go etc...

btw: has anyone compared this to the PBs Len() command?!?

-Lars

AMD Athlon XP2400, 512 MB RAM, Hercules 3D Prophet 9600 256MB RAM, WinXP
PIII 800MHz, 320 MB RAM, Nvidia Riva Tnt 2 Mach 64 (32MB), WinXP + Linux
17" iMac, 1.8 GHz G5, 512 MB DDR-RAM, 80 GB HD, 64 MB Geforce FX 5200, SuperDrive, OSX
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Post by PB »

> btw: has anyone compared this to the PBs Len() command?!?

It's not a version of the Len() command, it simply counts the occurrences
of a string inside another string, like my Occurences() procedure here:

viewtopic.php?t=3796
LarsG
Enthusiast
Enthusiast
Posts: 713
Joined: Mon Jun 02, 2003 1:06 pm
Location: Norway
Contact:

Post by LarsG »

ooops.. sorry.. my bad.. hehe
you see, that's the reason why a deeper explanation would be in place.. hehe
(that.. and I really should read the snippets more carefully..:p)

-Lars

AMD Athlon XP2400, 512 MB RAM, Hercules 3D Prophet 9600 256MB RAM, WinXP
PIII 800MHz, 320 MB RAM, Nvidia Riva Tnt 2 Mach 64 (32MB), WinXP + Linux
17" iMac, 1.8 GHz G5, 512 MB DDR-RAM, 80 GB HD, 64 MB Geforce FX 5200, SuperDrive, OSX
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

Hi.

I made that function some months ago, and i use it in one of my progs.

I come from Z80 and MC68000 assembling. I made some code, and a complete Z80A emulator with a 68000 (including interrupts, refresh register, etc.) years ago...

For me i386 assembling is something like Z80 "plus".

Oh, come on, i think it is difficult to explain better that staff. :wink:


AL
LJ
Enthusiast
Enthusiast
Posts: 177
Joined: Wed Apr 30, 2003 4:00 pm

Ok

Post by LJ »

@Psycho,

Do explain in more detail, maybe command by command, why your code does what it does. If you'd like I'm an education specialist and so I will rewrite your explanation/edit it to make it understandable.
User avatar
waffle
Enthusiast
Enthusiast
Posts: 129
Joined: Mon May 12, 2003 1:34 pm
Location: USA
Contact:

Post by waffle »

ok, line by line as i read it:

mov edi..... place the pointer to the first parameter in edi....

CLD ... this is a precurser to a string command.
by clearing the direction flag (DF) we tell the CPU to scan forward in the string.

XOR ..... when XORing a reg to itself, this sets it to 0 or null. this is faster
than MOV AL,0 because of access requirements (clock cycles)

MOV ecx,ebx ... copies ebx to ecx .... as this is a large register instead of 16 bit, this is the fastest way (no memory access) to set an address to "0"

DEC ecx ... two's compliment of -1 or .... all bits set.

REPNZ SCASB this is an assemble string command....
this will compare Al reg (remember, set to 0 with the XOR) with edi.
remember, edi was the pointer to the first byte of the first parameter?
If, this byte is not 0 (NULL) than edi is incremented (remember the DF ?),
so is ecx. This process stops when a null is found... Thats when the next
instuction occurs.

NOT ecx (remember, ecx was in two's compliment) this will convert to normal numbering, -1. This is needed because REPNZ will point at the null
char, and we want the last real char. This will give us the actual length.

MOV edi .... point edi at first string again
MOV eax ... point eax at second string that we test
MOV al, byte ... we only care about the first char in the second string

Note: in my book, it recommends the use of REPNE which will
return control to your program when a match is found. But this is an old 8088 book :) not sure what the @@ represents... possibly identifies
this mark as a lable and a command ... see GO @R

JECXZ check for overflow and then exit
INC EBX count a match
JMP @R continue to scan from last point

GO: MOV eax... this is the return value...



its been a long time since i did anything like that. Brimgs me back to my first program... making the LEDs blink sequentialy on my imsai 8080. Say fred, does that make you feel less old?Does reveal how old my librairy is .... should concider burning some of these books i think. I still have the spec sheets :), also for the RCA 1802... the ELF system.
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

Thank you waffle.
@@: is a label

My english is bad.

Ix86 assembler is not a secret. There are lots of docs, and if you get x86.hlp file you can understand all code, whatever.
Moreover, if your native language is english, what is the problem?
The .pdf document included in fasm package is optimal, and i advise it.

In the past i was looking for info about certain electronics componentes, about MSX hardware, after that Atari ST hardware, and after that Amiga hardware, and all it was almost not possible to find. It was like a secret.

Now, we have internet, that's veeeeery great for getting info without open the door of our home.
However, I have been searching info about AI and neuronal networks in the web, and all sites sell it, sell AI related courses, or give book titles. So then, secret continues for people who don't want to waste money for knowledge. Any help will be grateful!

I will put this in General Discussion. May be someone could help me.

LJ, what do you think about?



AL
LJ
Enthusiast
Enthusiast
Posts: 177
Joined: Wed Apr 30, 2003 4:00 pm

Waffle

Post by LJ »

@Waffle,

You are skilled with your ability to analyze that code and explain it in a clear way. Thank you. I think it's a bit too advanced to be in the beginners thread for Purebasic Inline Assembly, but not by much. I'm going to think on this one a bit and see if I can come up with a similar, but easier tutorial for beginners based on Psycho code and your explanation. Thanks again.
LJ
Enthusiast
Enthusiast
Posts: 177
Joined: Wed Apr 30, 2003 4:00 pm

OK

Post by LJ »

Ok guys, a new lesson has been added in the PureBasic Inline Assembly tutorial inspired and based on this message thread. Great team work on this minature project and thanks again for your time and effort.

Lj
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

Well, LJ.
I've made another function.
This one locates the number of iterations of a string into another.

Code: Select all

Procedure.l CountStrings(a.s,s.s)
  !cld          ;clear DF (Direction Flag)
  !mov edi,dword[esp]   ;load edi register with pointer to first string
  !mov esi,dword[esp+4] ;load edi register with pointer to second string (the string to search)
  !xor eax,eax    ;set eax register to NULL
  !mov bl,al      ;set bl register to NULL
  !mov edx,esi  ;save this value in edx to avoid, as much as possible, to have to read data from memory in the main loop.
  !;If any of two strings is empty then end program and matches found is 0;
  !or byte[edi],al  ;test if first string is empty
  !jz fin           ;if so, then end
  !or byte[esi],al  ;test if second string is empty
  !jz fin           ;if so, then end
  !;Mainloop:
  !mainloop:
  !cmpsb  ;what this instruction do is just compare byte[edi] with byte[esi] and increment edi and esi values by 1
  !jz match ;if byte[edi]=byte[esi] then goto match label, because a match byte was found...
  !mov esi,edx  ;restore this
  !or byte[edi],bl  ;check if end of first string is reached
  !jnz mainloop   ;if not reached then compare next bytes
  !fin:
  ProcedureReturn
  !match: ;here we have got inside a second treatment: We are in a possible total match, lets see if it is a complete match or it is a fake:
  !or byte[esi],bl  ;check if end of second string is reached
  !jnz @f   ;if so, here was a complete match
  !;complete match was found:
  !mov esi,edx  ;restore this
  !inc eax    ;increment complete matches counter
  !jmp mainloop   ;lets search for another possible complete match!
  !@@:cmpsb   ;compare one more byte
  !jz match   ;if equal, lets see if the deceit continues, or rather it could be a real complete match.
  !dec edi  ;ohhh! it was a deceit! It wasn't a complete match. So return back the first string counter by 1.
  !mov esi,edx  ;and restore this
  !jmp mainloop ;What a patient! lets continue searching for another possible match and why not, a possible complete match...
EndProcedure
;Grrr!: Z-80 had conditional CALL, Ix86 doesn't !?
a$="ureruPururePe ruPurePurPuPure uresdfhg ureeurPeruPuruee sPuuredil hPur eurePuree urePuret"

MessageRequester("",Str(CountStrings(a$,"Pure")),0)

;Can use function input parameters as normal pointers:
;Procedure.l CountStrings(*a,*s)
;but then you have to call function using pointers too:
;CountStrings(@a$,@"Pure")

Perhaps it could be possible to do a faster routine, i don't know. Who dares?

I hope this time it is enought explained. At least for you LJ :wink:



AL
LJ
Enthusiast
Enthusiast
Posts: 177
Joined: Wed Apr 30, 2003 4:00 pm

Very nice...

Post by LJ »

Very nice Psycho. I will create another lesson for beginners and have it up soon. Thanks again.
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

Oh! you go so fast!

Here is the final and more beautyful version, and a little bit faster:

Code: Select all

Procedure.l CountStrings(a.s,s.s)
  !cld          ;clear DF (Direction Flag)
  !mov edi,dword[esp]   ;load edi register with pointer to first string
  !mov esi,dword[esp+4] ;load esi register with pointer to second string (the string to search)
  !xor eax,eax    ;set eax register to NULL
  !mov bl,al      ;set bl register to NULL
  !mov edx,esi  ;save this value in edx to avoid, as much as possible, to have to read data from memory in the main loop.
  !;If any of two strings is empty then end program and matches found is 0;
  !cmp byte[edi],bl  ;test if first string is empty
  !jz fin           ;if so, then end
  !cmp byte[esi],bl  ;test if second string is empty
  !jz fin           ;if so, then end
  !;Mainloop:
  !mainloop:
  !cmpsb  ;what this instruction do is just compare byte[edi] with byte[esi] and increment edi and esi values by 1
  !jz match ;if byte[edi]=byte[esi] then goto match label, because a match byte was found...
  !mov esi,edx  ;restore this
  !cmp byte[edi],bl  ;check if end of first string is reached
  !jnz mainloop   ;if not reached then compare next bytes
  !fin:
  ProcedureReturn
  !match: ;here we have got inside a second treatment: We are in a possible total match, lets see if it is a complete match or it is a fake:
  !cmp byte[esi],bl  ;check if end of second string is reached
  !jz @f   ;if so, here was a complete match
  !cmpsb   ;compare one more byte
  !jz match   ;if equal, lets see if the deceit continues, or rather it could be a real complete match.
  !dec edi  ;ohhh! it was a deceit! It wasn't a complete match. So return back the first string counter by 1.
  !mov esi,edx  ;and restore this
  !jmp mainloop ;What a patient! lets continue searching for another possible match and why not, a possible complete match...
  !;complete match was found:
  !@@:inc eax    ;increment complete matches counter
  !mov esi,edx  ;restore this
  !jmp mainloop   ;lets search for another possible complete match!
EndProcedure
;Grrr!: Z-80 had conditional CALL, Ix86 doesn't !?
a$="ureruPururePe ruPurePurPuPure uresdfhg ureeurPeruPuruee sPuuredil hPur eurePuree urePuret"

MessageRequester("",Str(CountStrings(a$,"Pure")),0)

;Can use function input parameters as normal pointers:
;Procedure.l CountStrings(*a,*s)
;but then you have to call function using pointers too:
;CountStrings(@a$,@"Pure")


AL
LJ
Enthusiast
Enthusiast
Posts: 177
Joined: Wed Apr 30, 2003 4:00 pm

OK

Post by LJ »

OK Psycho, got the next Inline Assembly tutorial posted (see Inline Assembly sticky thread). Modified your code by removing some redundant instructions, expanded the REM explanations in the code, and set it all in the context of the tutorial and where our students are currently at. Didn't get a chance to add the more beautiful code you posted because the tutorial was already 50% done. That's okay. It's still a great tutorial. Good work my friend, you da man!

Lj
Post Reply