I'm trying to optimize my code (so getting rid of apis). I have a drop down menu and when I press a button it detects what has been selected.
This works perfectly fine with lstrcmp, offset lpDropDown, offset string
.if eax==0
code
.endif
code
I saw some other code lying around
mov esi, offset string1
mov edi, offset string2
@@:
mov al,[esi]
cmpsb
jne strings_not_equal
test al, al
jne @b
strings_are_equal:
.....
strings_not_equal:
.....
however this will not work because one of the strings, lpDropdown is not null terminated (well I don't think it is because I don't actually define it myself).
I then saw this for strings that aren't null terminated:
mov esi, offset string1
mov edi, offset string2
mov ecx,string_length
repz cmpsb
jne strings_not_equal
strings_are_equal:
.....
strings_not_equal:
.....
however this doesn't work either, I think it is because one of the strings actually is null terminated. Though now looking at it in detail I think it is because only one string is a dword. I cannot change this though as one of them has to be declared as a byte otherwise I get an error compiling.
Thanks. I'm still learning I know but I want to stop relying on apis so much and optimize my code.
Hi,
AFAIK, you don't need to do mov al,[esi] if you are using cmpsb. I think
mov al,[esi]
cmp al,[edi]
jne @blah
is faster than cmpsb. Then don't do test al,al. Instead, put length of one string in ecx and do
dec ecx
jnz @B ; if more than 0 jump
Thomas
:U
mov eax, offset String21
mov edi, offset lpDropDown
mov ecx,7
@@:
cmp al, [edi]
dec ecx
jnz @B ; if more than 0 jump
jne @f
This isn't right obviously because it only compares the first byte. Could I use cmp al, [edi+ecx] or something to that effect?
Oops,
My mistake :red . You either do cmp al,[edi+ecx] or do an inc edi before the dec ecx
Thomas :U
Doesn't work.
mov eax, offset String21
mov edi, offset lpDropDown
mov ecx,7
@@:
cmp byte ptr al, [edi]
jne @f
inc edi ; byte position
inc eax ; byte position
dec ecx ; counter
jnz @B ; if more than 0 jump
For some reason when comparing the first byte al is 2A (which is an *) and edi is 4A which is J (what it should be). However both of the strings are the same. I did this in olly command bar
? al
and it showed this *J
did the same in edi and it had something like %\J
I'm not sure why this is happening or how to prevent it?
Nilrem,
You only have 2 choices with astring compares with ANSI characters, either use TWO zero terminated strings to compare or set a length for the comparison and pass it to the procedure that does the comparison. Let us know what you have in mind and it can probably be fixed up easily enough.
String21 db 'JongBot',0
that is one of the strings
However the other string is not declared it is just whatever is selected in the dropdownmenu (lpDropDown).
So I think that isn't zero terminated.
Thanks Hutch and Thomas for giving help. So that is what, the second option you said Hutch. 8-)
Hi,
If you get the string from a dropdown menu, it will be a zero terminated string
Thomas :U
Ok I wonder why the code is not working then if they are both zero terminated (and of course it is since don't all windows api results end with a zero terminated string?).
Why not use the szCmpi or szCmp functions in the MASM32 library?
Thomas :U
Here is an algo to do sring compares based on a preset length.
LATER : Tweaked :toothy
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
strcmp proc str1:DWORD,str2:DWORD,cnt:DWORD
push ebx
push esi
mov esi, str1 ; load 1st byte data in ESI
mov edx, str2 ; load 2nd byte data in EDI
mov eax, cnt ; load byte count in EAX
add esi, eax ; add byte count to ESI
add edx, eax ; add byte count to EDI
neg eax ; reverse sign in EAX
xor ebx, ebx ; clear EBX to prevent stall with BL
@@:
mov bl, [esi+eax] ; load byte at ESI into BL
cmp bl, [edx+eax] ; compare BL to byte in EDI
jne @F ; exit if not equal
add eax, 1 ; add 1 to test next pair of bytes
jnz @B ; jump back if count is not zero
mov eax, -1 ; non zero is matching strings
jmp quit
@@:
xor eax, eax ; zero is non matching strings
quit:
pop esi
pop ebx
ret
strcmp endp
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
Haven't tested it yet. Some questions however. Why would bl stall? Why add eax,1 isn't inc eax quicker, or is that just preference?
Secondly since this post is about trying to optimize my code, I have several strings to compare, therefore how could I manipulate this easier, apart from putting strings into strings (for example string1 is always compared so move the next string, string2 into string1 using repnz or whatever it was I forgot) I'm asking this because I'm contemplating about a swicth statement but am unsure how to go about it. Thanks a lot in helping I really do appreciate it, maybe I am annoying but I really want to be a more efficient programmer. 8-)
Edit
Nevermind I see it's a procedure ready to use easily. However the questions still remain concerning efficieny and switch statements.
Basically I want to compare two strings (obviously) and then do something. At the moment I have the rather inefficient:
invoke lstrcmp, str1, str2 //str1 been dropdownmenu
.if eax==0
code
.endif
invoke lstrcmp, str1, str2
.if eax==0
code
.endif
invoke lstrcmp, str1, str2
.if eax==0
code
.endif
You get the idea...
Edit again
I know you can't do this in asm, but I just remembered a project in Excel where using a V-lookup was incredibly useful, if that could be used that would be great. Just trying to prove I don't ask reams of questions without putting the effort in. 8-)
inc and dec are slower on the P4 compared to add and sub. (I think)
the same but shorter :lol
strcmp proc str1:DWORD,str2:DWORD,cnt:DWORD
push esi
mov esi, str1 ; load 1st byte data in ESI
mov edx, str2 ; load 2nd byte data in EDI
mov eax, cnt ; load byte count in EAX
LoopA:
add eax,-1
jl quit
movzx ecx, byte ptr [esi+eax] ; load byte at ESI into BL
cmp cl, [edx+eax] ; compare BL to byte in EDI
je LoopA
xor eax, eax ; zero is non matching strings
quit:
pop esi
ret
strcmp endp
Regards,
Lingo
Thankyou. 8-) Had problems getting it to work. Hutch yours worked perfectly.
Here is the same algo with the stack frame removed. It probably does not matter in this context but its easy enough to do and it appears to be working OK.
The algo is correctly binary compare rather than string compare and can be used for any memory comparison, not just string data.
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
align 4
OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE
bytecmp proc txt1:DWORD,txt2:DWORD,cnt:DWORD
push esi
mov esi, [esp+8] ; load 1st byte data in ESI
mov edx, [esp+12] ; load 2nd byte data in EDX
mov eax, [esp+16] ; load byte count in EAX
add esi, eax ; add byte count to ESI
add edx, eax ; add byte count to EDX
neg eax ; reverse sign in EAX
xor ecx, ecx ; clear ECX to prevent stall with CL
align 4
@@:
mov cl, [esi+eax] ; load byte at ESI into BL
cmp cl, [edx+eax] ; compare BL to byte in EDX
jne @F ; exit if not equal
add eax, 1 ; add 1 to test next pair of bytes
jnz @B ; jump back if count is not zero
sub eax, 1 ; non zero is matching strings
jmp quit
@@:
xor eax, eax ; zero is non matching strings
quit:
pop esi
ret 12
bytecmp endp
OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
"Thankyou. Had problems getting it to work..."
Now is OK
Try again pls :bdg
Regards,
Lingo
hey hutch:
If you want to negate a number, don't you have to use neg eax / add eax,1
instead of just neg eax?
Aero,
Just write two examples and test it.
Thats weird. It worked in practice, but in thoery it does not work.
Consider half-byte signed numbers:
1111 = -1
1110 = -2
1101 = -3
1100 = -4
1011 = -5
1010 = -6
1001 = -7
1000 = -8
0111 = 7
0110 = 6
0101 = 5
0100 = 4
0011 = 3
0010 = 2
0001 = 1
0000 = 0
Take 5 = 0101
neg eax gives 1010 = -6
therefore neg eax \ add eax,1 gives -6 + 1 = -5
What theory?
Where did you get the idea that NEG of 0101 produces 1010 ?
AeroASM,
I think you are confusing not and neg.
neg eax
would be functionally equivalent to
not eax
inc eax
I think he forgot to do the twos compliment.
Paul
MichaelW: spot on
pbrennick: wtf is twos complement?
Quote from: AeroASM on April 23, 2005, 05:33:10 PM
MichaelW: spot on
pbrennick: wtf is twos complement?
Yeah,
I was thinking of asking that. I have seen both one's complement and two's complement and they have complemented my mind :wink
Thomas :U
I posted this on the old forum, but it appears to have been lost.
Two's Complement
----------------
Processors of the x86 family use a two's complement representation
for signed integers. With this representation, the most significant
bit functions as a sign bit. A value of 0 for the sign bit indicates
a positive integer and a value of 1 indicates a negative integer.
For simplicity, the following examples use bytes, but the concept
is applicable to any size integer.
The value of a positive integer is the value of the bits:
0000 0000b = 0
0000 0001b = +1
0000 0010b = +2
...
0111 1111b = +127
The value of a negative integer is the value of the bits
minus 2^n, where n is the number of bits.
1111 1111b (255) minus 2 ^ 8 (256) = -1
1111 1110b (254) minus 2 ^ 8 (256) = -2
1111 1101b (253) minus 2 ^ 8 (256) = -3
...
1000 0000b (128) minus 2 ^ 8 (256) = -128
The next two examples require an understanding of the rules
for binary addition:
0 + 0 = 0
0 + 1 = 1
1 + 0 = 1
1 + 1 = 0 carry 1
To convert a value from positive to negative, or from negative
to positive, you invert (complement) all the bits and add 1:
Start with +1: 0000 0001
Invert all bits: 1111 1110
Add 1: 0000 0001
Now have -1: 1111 1111
Invert all bits: 0000 0000
Add 1: 0000 0001
Now have +1: 0000 0001
You can verify that 1111 1111b represents -1 by adding 1:
Start with -1: 1111 1111
Add 1: 0000 0001
Result is 0: 1 0000 0000 (carry ignored)
Quote from: pbrennick on April 23, 2005, 05:21:41 PM
I think he forgot to do the twos compliment.
Paul
Thats what confused me. I knew that twos complement was a system of negative numbers, but I didn't know how to
do a system.
Two's complement is also the name of an operation that creates the arithmetic negative in a two's complement system.
OK, got it. When Pbrennick said I forgot to do the twos complement, he meant that I did not know that there is an opcode to do it.
Hutch the problem arises if I have one string called Thunder and another Thunder2.
Nilrem,
Quote
Hutch the problem arises if I have one string called Thunder and another Thunder2.
All tis means is the algo you are using is not doing the comparison properly for the string length. You only need to pass the length of one string, usually the first one and then do the comparison and you should pick if one is different in its last character than another.
What I'm saying is I have the following
invoke strcmp, offset lpDropDown, offset ThunderPopper, 13
so if ThunderPopperII is selected from the dropdownmenu then when the above happens it will think ThunderPopper is selected when in fact ThunderPopperII is, but I cannot fix this problem because changing 13 to 15 results in if ThunderPopper is selected then it doesn't come up when it should.
I just either had to put strcmp ThunderPopperII before strcmp ThunderPopper
but I like it to be in order (my code) so instead I used szCmp.
Implement Hutch's idea. It would be great practice, plus be very fast.
I have implemented his, but it doesn't work if I have this:
invoke strcmp, offset lpdropdown, offset thunderpopper, 13
jnz @f
ret
@@:
invoke strcmp, offset lpdropdown, offset thunderpopperII, 15
jnz @f
ret
That doesn't work if the string ThunderpopperII is selected because it stops at the first strcmp because the first 13 bytes of thunderpopperII matches the compare.
You need to pass the lengths of both strings. You need to know if the strings differ in length or not.
There is something fundamentally wrong with the premises here, if the strings are zero terminated, it does not mater if they are the same up near the end or not, the difference is measured with the zero terminator included.
"string",0
"string1",0
A single coparison algo will exit when the "1" is compared with the terminating zero as a mismatch.
Check out this little snippet I made while up coding late. Very late. :)
SzComp proc sz1:DWORD, sz2:DWORD
mov esi, sz1 ; load string offsets
mov edi, sz2
cld ; clear direction flag
@@:
cmpsb ; compare bytes esi,edi
jne err ; different?
cmp byte ptr [esi-1], 00 ; is one a null?
jz eos ; yes, strings match
jmp @B
err:
mov eax,1 ; compare failed
ret
eos:
mov eax,0 ; compare succeeded
ret
SzComp endp
Hutch what is wrong is ThunderPopper gets compared before ThunderPopperII
So if I have this
invoke strcmp, offset lpdropdown, offset thunderpopper, 13
jnz @f
code
@@:
invoke strcmp, offset lpdropdown, offset thunderpopperII, 15
jnz @f
code
As you can see if ThunderPopperII is in lpdropdown then at this point:
invoke strcmp, offset lpdropdown, offset thunderpopper, 13
only the first 13 bytes get compared and hence therefore it thinks ThunderPopper is in lpdropdown. What I should have done is tried and edited your algorithim you wrote Hutch so that when the comparison has finished, and it thinks it is successful I should then check to see if there is any remaining bytes, however whilst writing this I have realised that wouldn't work because I already specified 13 bytes. I think I'll look at szCmp and see the differences.
Mark Jones thanks for the late night coding effort, I'll try it later today hopefully.
Whoa nellie, my string compare is tons faster than lstrcmp, see the attached project:
SzCmp/lstrcmp speed comparison by Mark Jones (gzscuqn02ATsneakemailD0Tcom)
Timing routines by MichaelW from MASM32 forums: http://www.masmforum.com/
Please terminate any high-priority tasks and press ENTER to begin.
These are the three 64-byte strings being tested:
"Hello, this is an example of a MASM32 64-byte string comparison."
"Hello, this is an example of a MASM32 64-byte string comparison!"
"hello, this is an example of a MASM32 64-byte string comparison."
Timing results:
SzCmp, 64 bytes identical string: 416 clocks
SzCmp, 64 bytes different string (last character): 342 clocks
SzCmp, 64 bytes different string (first character): 11 clocks
lstrcmp, 64 bytes identical string: 1568 clocks
lstrcmp, 64 bytes different string (last character): 1619 clocks
lstrcmp, 64 bytes different string (first character): 2531 clocks
Press ENTER to exit...
[attachment deleted by admin]
Quote from: hutch-- on May 06, 2005, 05:24:48 AM
There is something fundamentally wrong with the premises here, if the strings are zero terminated, it does not mater if they are the same up near the end or not, the difference is measured with the zero terminator included.
"string",0
"string1",0
A single coparison algo will exit when the "1" is compared with the terminating zero as a mismatch.
Well, if indeed all the strings are 0-terminated, then the length to compare should include the terminator character. In other words, one more than the number of significant (or "interesting") characters.
Well, if indeed all the strings are 0-terminates, then you don't need a length.
That's true, but the length is being used for optimizing speed.
Hi Mark,
Interesting! What were you running on? These are my results running on a P3:
SzCmp/lstrcmp speed comparison by Mark Jones (gzscuqn02ATsneakemailD0Tcom)
Timing routines by MichaelW from MASM32 forums: http://www.masmforum.com/
Please terminate any high-priority tasks and press ENTER to begin.
These are the three 64-byte strings being tested:
"Hello, this is an example of a MASM32 64-byte string comparison."
"Hello, this is an example of a MASM32 64-byte string comparison!"
"hello, this is an example of a MASM32 64-byte string comparison."
Timing results:
SzCmp, 64 bytes identical string: 402 clocks
SzCmp, 64 bytes different string (last character): 391 clocks
SzCmp, 64 bytes different string (first character): 18 clocks
lstrcmp, 64 bytes identical string: 372 clocks
lstrcmp, 64 bytes different string (last character): 967 clocks
lstrcmp, 64 bytes different string (first character): 3296 clocks
Michael, my results were with an AMD XP 1800+. Those are some pretty big differences. :)
Your P3 seem to be faster than my Celeron 2.4ghz :toothy
Timing results:
SzCmp, 64 bytes identical string: 903 clocks
SzCmp, 64 bytes different string (last character): 888 clocks
SzCmp, 64 bytes different string (first character): 49 clocks
lstrcmp, 64 bytes identical string: 2209 clocks
lstrcmp, 64 bytes different string (last character): 2297 clocks
lstrcmp, 64 bytes different string (first character): 3097 clocks
I think string opcodes are worse on newer computers.
No, just Celeron is slower. My P3 850Mhz loads games faster then a Celeron 2GHz. 8-)
Hi Nilrem, Seems like we have the same processor.
Thomas :U
Hehe, well I really need an upgrade for the new games, anyways let's not take the topic off-topic. Please check your pm Thomas.