This is probably the most peculiar problem I have seen when it comes to programming under PB. The reason is that my inline assembly code doesn't work - but it does. Contradiction? I think so, but none the less, it is the accurate but confusing description :hmmm:.
The current situation is that my [assembly]code works sometimes, but not every time. The behaviour differ quite a lot - when it fails. Some of it crashes to the extent that even Dr. Watson cannot do its job properly. There seems to be a system to it, since for the same setup/set of parametres, the response is repeatable, although I am unable to predict it. I cannot find any corresponding problems with my pure BASIC code. Not to wonder - it's bad code, you would think. I tend to agree, but I am unable to isolate the problem.
The thing is, my code work 100% correct when I debug it! When run from inside the PB debugger, all is well - using the very same data set that fails when run outside of the debugger!
I first detected this when reading a forum discussion the other day, and failed to make a local comparison using my own code. I left that for a while, until I stumbled over a variation of the problem. After a follow up on that, I detected that the problem is present for more (all?) of my assembly functions. Since then I have fiddled around with this and tried all the possibilities I can think of with no success. I have tested it under XP SP3 and W2K SP4. With and without firewalls and anti-everything SW. My usual settings in such SW is at "paranoia level" which have previously hampered PC usage. But I think that possibility has been eliminated this time.
This is NOT related to a particular PB compiler, as several versions reveal the same problem. The behaviour is consistent over PBCC versions 4.00, 4.01, 4.03, 4.04, 5.0 and 5.01, and PBWin version 8.04 and 9.01. Old and fresh installations (the 4.01 through 4.04 was by upgrade).
Normally, I would have poured debugging-code to log status all over the place in an attempt to isolate the problem, but I do not (yet) have any code to do that reliably from within pure assembly code.
------
In order to avoid the "code not shown" comment
, here is compilable code to demonstrate my problem:
The code shown consistently crash on me when run from outside of the debugger, but it works fine when run from inside the debugger. (The CC-compilers seems to struggle the most, Dr. Watson is having difficulties handling it, but the WIN-compilers simply return with a "sorry, need to close"-message). The code should work with compiler versions older than the ones I have used for testing without modification of any sort.
I would appreciate it if anyone could spare the time to confirm that it does or does not work with their compiler / computer.
Is this a situation known to anyone?
Is this a violation of "owned" code or data space rules?
What am I missing here?
My gut feeling tells me that there is some basic stuff (no pun intended), but I clearly fail to see it.
------
Code description:
This is an assembly piece that will search a string for a substring, primarily being case insensitive (but it's your choice), without using temporary storage. As such it is very well suited for looong string searches. At the time of writing, it was faster than the then current PB versions' INSTR, but I have not timed it against the PB5/9 INSTR.
I have previously published this code in the source forum (http://www.powerbasic.com/support/pb...ad.php?t=25128), so it may already be known to some of you. The link doubly demonstrate my point, as the samples published works both inside and outside of the debugger (As you can see, Colin Schmidt complained about the code crashing on his computer, but I didn't hear anything more after I released version 2).
The code supplied below has a modification as compared to the code already published: To make the code even more solid, I have put a (to my knowledge, unnecessary) PUSHAD at the start of the assembly code and a corresponding POPAD at the exit point. No change in behaviour is noticed after the added "security", so these two commands can safely be commented out.
ViH
----------------------------------------------------------------------------------------------------------
"If debugging is the process of removing bugs, then programming must be the process of putting them in." - Unknown
"You can fool some of the people all of the time, and all of the people some of the time, but you can make
a fool of yourself anytime." - Unknown
The current situation is that my [assembly]code works sometimes, but not every time. The behaviour differ quite a lot - when it fails. Some of it crashes to the extent that even Dr. Watson cannot do its job properly. There seems to be a system to it, since for the same setup/set of parametres, the response is repeatable, although I am unable to predict it. I cannot find any corresponding problems with my pure BASIC code. Not to wonder - it's bad code, you would think. I tend to agree, but I am unable to isolate the problem.
The thing is, my code work 100% correct when I debug it! When run from inside the PB debugger, all is well - using the very same data set that fails when run outside of the debugger!
I first detected this when reading a forum discussion the other day, and failed to make a local comparison using my own code. I left that for a while, until I stumbled over a variation of the problem. After a follow up on that, I detected that the problem is present for more (all?) of my assembly functions. Since then I have fiddled around with this and tried all the possibilities I can think of with no success. I have tested it under XP SP3 and W2K SP4. With and without firewalls and anti-everything SW. My usual settings in such SW is at "paranoia level" which have previously hampered PC usage. But I think that possibility has been eliminated this time.
This is NOT related to a particular PB compiler, as several versions reveal the same problem. The behaviour is consistent over PBCC versions 4.00, 4.01, 4.03, 4.04, 5.0 and 5.01, and PBWin version 8.04 and 9.01. Old and fresh installations (the 4.01 through 4.04 was by upgrade).
Normally, I would have poured debugging-code to log status all over the place in an attempt to isolate the problem, but I do not (yet) have any code to do that reliably from within pure assembly code.
------
In order to avoid the "code not shown" comment

The code shown consistently crash on me when run from outside of the debugger, but it works fine when run from inside the debugger. (The CC-compilers seems to struggle the most, Dr. Watson is having difficulties handling it, but the WIN-compilers simply return with a "sorry, need to close"-message). The code should work with compiler versions older than the ones I have used for testing without modification of any sort.
I would appreciate it if anyone could spare the time to confirm that it does or does not work with their compiler / computer.
Is this a situation known to anyone?
Is this a violation of "owned" code or data space rules?
What am I missing here?
My gut feeling tells me that there is some basic stuff (no pun intended), but I clearly fail to see it.
------
Code description:
This is an assembly piece that will search a string for a substring, primarily being case insensitive (but it's your choice), without using temporary storage. As such it is very well suited for looong string searches. At the time of writing, it was faster than the then current PB versions' INSTR, but I have not timed it against the PB5/9 INSTR.
I have previously published this code in the source forum (http://www.powerbasic.com/support/pb...ad.php?t=25128), so it may already be known to some of you. The link doubly demonstrate my point, as the samples published works both inside and outside of the debugger (As you can see, Colin Schmidt complained about the code crashing on his computer, but I didn't hear anything more after I released version 2).
The code supplied below has a modification as compared to the code already published: To make the code even more solid, I have put a (to my knowledge, unnecessary) PUSHAD at the start of the assembly code and a corresponding POPAD at the exit point. No change in behaviour is noticed after the added "security", so these two commands can safely be commented out.
Code:
' Code shown is tested to compile and run with ' PB CC4, PBCC5, PB WIN8, PB WIN9. It will probably ' work unchanged with many older versions as well. #COMPILE EXE #DIM ALL %CC_INSTR_SEARCHFORWARD = 0& %CC_INSTR_SEARCHBACKWARDS = -1& DECLARE FUNCTION CC_Instr( lStartPos AS LONG, sBuffer AS STRING, sWantedString AS STRING, sCollateString AS STRING, lDirection AS LONG ) AS LONG ' ============================================================================================================== FUNCTION CC_Instr( lStartPos AS LONG, sBuffer AS STRING, sWantedString AS STRING, sCollateString AS STRING, lDirection AS LONG ) AS LONG ' CC_Instr (=CustomCase INSTR or CustomCharacterset INSTR) will search sBuffer for sWantedString starting at position lStartPos and return an integer informing of ' the location of the string while using sCollateString for character translation when comparing. The result is indicated by the returnvalue. ' sBuffer will be searched from the beginning to the end (left to right) or from the end to the beginning (right to left) according to the lDirection flag. ' None of the parametres are altered during execution, and only 16 bytes are allocated for temporary data storage. ' ' Also worth noting, is that - due to the (mandatory) custom-characterset feature, this routine does not have ANY speed- nor resource penalty when handling case ' INSENSITIVE searches or any odd needs for character substitution. ' As for speed in general, the author's measurements suggests that this function completes in around 50% to 60% of the time used by PowerBasic's INSTR() for ' short-to-medium length strings (<90 chars used when testing), and approx. 20% faster than INSTR() for long strings (750-900KB strings used when testing). ' Note, though, that such timings are highly need-, hit- and environment dependant so your timings will most probably differ if you try for yourself using ' your own strings. ' ' Input: lStartPos = Where in sBuffer to start your search. 1 = start of sBuffer. Which end to start searching from is decided by lDirection. ' sBuffer = The buffer to search when looking for the string. sBuffer is NOT altered during the search. ' sWantedString = The string we want to find in sBuffer. sWantedString is NOT altered during the search. ' sCollateString = A 256-character string containing the characters to use when translating chars in sBuffer and sWantedString. ' This string may also be built according to PowerBasic's COLLATE STRING rules, and may ' be - if used in a search-algorithm within sorted arrays - identical to the COLLATE-string ' used when sorting the same array, so your custom sorting-tables will get extended mileage. ' lDirection = 0 or not 0 (or, if you prefer: %CC_INSTR_SEARCHFORWARD or %CC_INSTR_SEARCHBACKWARDS) ' 0 --> lStartPos = Relative to the beginning of sBuffer, and the search will start from ' beginning-of-sBuffer and continue to end-of-sBuffer ("normal" search left to right). ' Not 0 --> lStartPos = Relative to the end-of-sBuffer, and the search will start from the ' end-of-sBuffer and continue towards the beginning-of-sBuffer. ("reverse"/"backwards" search right to left) ' ' Output: <nothing> No alterations are done to the function's parametres. ' ' Returnvalues: 0 = sWantedString was not found in sBuffer, or an illegal parameter was detected: ' - lStartPos < 1 ' - LEN(sCollateString) <> 256 ' >0 = sWantedString is found at that position. 1 = always the beginning of (the leftmost position in ) sBuffer. LOCAL lRetVal AS LONG LOCAL lBufSize AS LONG LOCAL lWantLen AS LONG LOCAL psWantString AS STRING PTR ' Here and there one or more NOP instructions are inserted. This is in order to align the code to ' WORD-, DWORD- or PARAGRAPH-addresses. It has been attempted to collect all NOPs into places where ' they only fill the alignment function and are never executed, but where that has proved to be ' impossible or impractical, the NOPs are scattered around in the preceding code where they can be of ' some use; aligning variable accessing instructions (and others) and to fill in where some register- or ' execution-channel congestion can be thought to be eased. Although the effects of this has not been ' measured, timings more than suggests that the alignment work pays off big time (up to 20% has been measured) ' for long strings, while it doesn't really matter when it comes to short strings. However, I have not experimented ' with more or less alignment code, so other ways may prove to be faster still (e.g. only go for DWORD alignment all over ' as opposed to try to go for PARAGRAPH alignment as I have done here). One has to stop development at some point and say ' "That's it". There will always be something that can be improved upon or experimented with to see if it is better or not. ' Make sure that sCollateString is exactly 256 characters long ! PUSHAD ! mov EAX, sCollateString ' EAX = pointer to sCollateString handle ! mov EBX, [EAX] ' EBX = pointer to start of sCollateString ! cmp EBX, 0 ' EBX = 0 --> Null pointer: sCollateString is empty ! je RevEndFindString ! mov ECX, [EBX-4] ' ECX = LEN(sCollateString) ! cmp ECX, 256 ' Check that sCollateString is exactly 256 characters long ! jne RevEndFindString ' Exit if LEN(sCollateString)<>256 ' Store pointer to sWantedString in memory ' Store LEN(sWantedString) in memory ' Check that lWantLen > 0 ! mov EAX, sWantedString ' EAX = handle to sWantString ! mov ESI, [EAX] ' ESI = Pointer to sWantString ! cmp ESI, 0 ' ESI = 0 --> Null pointer: LEN(sWantedString) = 0 ! je RevEndFindString ! mov EDX, [ESI-4] ' move length of sWant into ECX ! mov lWantLen, EDX ' save LEN(sWantString) ' Check that lWantLen <= LEN(sBuffer) ! mov EAX, sBuffer ' EAX = pointer to string handle ! mov EDI, [EAX] ' EBX = pointer to string data ! cmp EDI, 0 ' EDI = 0 --> Null pointer: sBuffer is empty ! je RevEndFindString ! mov ECX, [EDI-4] ' move length of string into ECX ! mov lBufSize, ECX ' Set the lBufSize parameter also ! cmp ECX, EDX ' Is LEN(sBuffer) < LEN(sWantedString)? ! jc RevEndFindString ' If CY, then sWantedString is longer than sBuffer - exit ' Current situation: ' EAX = No interest at this point ' EBX = pointer to sCollateString (does not change until ending calculations when sWantString has been located) ' ECX = number of Chars in sBuffer (=Chars to search) ' EDX = lWantLen, and variable lWantLen initialized ' ESI = pointer to start (left side) of sWantString, variable psWantString (pointer to sWantString) initialized ' EDI = pointer to start (left side) of sBuffer - no need to save this one in memory ' ' Also, we have made sure that LEN(sWantString) <= LEN(sBuffer), lWantLen > 0 and LEN(sBuffer) >= lWantLen ' ' The above is in anticipation that Left-To-Right search is most frequently used ... ' for Right-To-Left calculations, we need to readjust ECX prior to continuing ' Check the search direction we want ! mov EAX, lDirection ' EAX = pointer to lDirection handle ! mov EAX, [EAX] ' EAX = value of lDirection ! cmp EAX, 0 ' See if lDirection indicates a Left-To-Right search or not ! jne RightToLeftSearchStart ' If lDirection <> 0 then this is a RightToLeft search ' ----- LEFT-TO-RIGHT-SEARCH starts here ------- ' Adjust ECX so that we do not search more of sBuffer than can fit sWantLen ! nop ' WORD alignment ! sub ECX, EDX ' ECX = LEN(sBuffer), EDX = LEN(sWantString) --> ECX = adjusted number of chars to search ! jc RevEndFindString ' If CY, then lWantLen > LEN(sBuffer) ! inc ECX ' ECX = LEN(sBuffer) - LEN(sWantString) + 1 ! mov psWantString, ESI ' Initialize pointer to sWantString ! nop ' WORD alignment ' Check that lStartPos is not less than 1 ! mov EAX, lStartPos ' EAX = pointer to lStartPos ! nop ' PARAGRAPH alignment ! mov EDX, [EAX] ' EDX = Value of lStartPos ! cmp ECX, EDX ' Compare LEN(sBuffer) to lStartPos ! jc RevEndFindString ' If lStartPos > LEN(sBuffer) - exit ! dec EDX ! nop ' DWORD alignment ! cmp EDX, 0 ' Check lStartPos > 0 upon input ! nop ' PARAGRAPH alignment ! jl RevEndFindString ' Part of PARAGRAPH alignment for the loops below ... DWORD aligning the following instruction ! nop ! nop ' DWORD alignment ' Adjust EDI to point to the real starting position and decrease ECX with the corresponding number of chars ! add EDI, EDX ' Make sure EDI points to the real starting position ! sub ECX, EDX ' Subtract the corresponding number of chars from ECX (= number of chars to search) ' PARAGRAPH alignment done - the top of the instruction-loop is aligned ! nop ! nop ! nop ! nop ' PARAGRAPH alignment ' Look for the first character to match FindFirstCharLoopTopInit: ! mov ESI, psWantString ! nop ! nop ' DWORD alignment ! movzx EAX, BYTE PTR [ESI] ' Get sWantedString's first character into AL (we're using only AL) ! nop ' DWORD alignment ! movzx EDX, BYTE PTR [EBX+EAX] ' Get sWantedString's first charcter's value (or SORT-WEIGHT if you're using an ARRAY SORT COLLATE string as sCollateString) into DL ' The FindFirstCharLoopTop label is now PARAGRAPH aligned (relative address 0) FindFirstCharLoopTop: ! movzx EAX, BYTE PTR [EDI] ' Get sBufferString's next charcter into AL ! movzx EAX, BYTE PTR [EBX+EAX] ' Get sBufferString's next character's value (or SORT-WEIGHT if you're using an ARRAY SORT COLLATE string as sCollateString) into AL ! inc EDI ! cmp EAX, EDX ' Compare AL with DL ! je FindRestCharsInit ! dec ECX ! jnz FindFirstCharLoopTop ! jmp EndCC_Instr ' ECX = 0 --> No strings found. To test: Use 1-letter strings as well! ' PARAGRAPH align the top of the FindRestCharsInit (relative address 0) ' This code is never executed - its sole purpose is to fill up to the next paragraph ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ' PARAGRAPH alignment FindRestCharsInit: ' Here: Check if the rest of the string matches also ! dec ECX ! push EDI ' Save the current position in sBuffer ! push ECX ' ECX = Remaining chars of sBuffer ! mov ECX, lWantLen ' Number of characters in sWantedString to ECX ! inc ESI ' Move the pointer to the next Char of sWantString before comparison commences ... ! dec ECX ! jz StringEqual ' Is the Wanted String empty now (could have been only 1 char)? ' PARAGRAPH align the top of the FirstCharsLoopTop (to relative address 0) ! nop ! nop ! nop ' PARAGRAPH alignment FindRestCharsLoopTop: ! movzx EAX, BYTE PTR [EDI] ' Get sBufferString's next charcter into AL (we're still only using AL) ! movzx EDX, BYTE PTR [EBX+EAX] ' Get sBufferString's next character's value (or SORT-WEIGHT if you're using an ARRAY SORT COLLATE string as sCollateString) into DL ! movzx EAX, BYTE PTR [ESI] ' Get sWantedString's next character into AL ! movzx EAX, BYTE PTR [EBX+EAX] ' Get sWantedString's next charcter's value (or SORT-WEIGHT if you're using an ARRAY SORT COLLATE string as sCollateString) into AL ! inc ESI ! inc EDI ! dec ECX ' Check if ECX=0 ! jz FindRestCharsLoopEnd ' If ECX = 0, no more characters in sWantedString ! cmp EAX, EDX ' Compare AL with DL ! je FindRestCharsLoopTop ' If equal - check next char as well ! pop ECX ' ECX = Remaining chars of sBuffer ! pop EDI ' EDI = Save the current position in sBuffer ! cmp ECX, 0 ' ECX = 0? ! je EndCC_Instr ' If ECX is zero, we have searched the entire buffer with no sWantedString ! jmp FindFirstCharLoopTopInit ' The FindRestCharsLoopEnd label is PARAGRAPH aligned ' This code is never executed - its sole purpose is to fill up to the next paragraph ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ' PARAGRAPH alignment FindRestCharsLoopEnd: ! cmp EAX, EDX ' ECX = 0, but the last characters aren't compared yet ! je StringEqual ' If comparison was successful - and the last character of sWantedString matched sBuffer ! pop ECX ' ECX = Remaining chars of sBuffer ! pop EDI ' EDI = Save the current position in sBuffer ! cmp ECX, 0 ' ECX = 0? ! je EndCC_Instr ' If ECX is zero, we have searched the entire buffer with no sWantedString ! jmp FindFirstCharLoopTopInit ' This code is never executed - its sole purpose is to fill up to the next paragraph ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ' PARAGRAPH alignment EndFindString: ' We did not locate sWantedString or sWantedString was in part found at the very end of sBuffer, so nothing more to look for! ! xor EAX, EAX ! mov lRetVal, EAX ! jmp EndCC_Instr ' This code is never executed - its sole purpose is to fill up to the next paragraph ! nop ! nop ! nop ! nop ' PARAGRAPH alignment StringEqual: ! mov EDX, lWantLen ! mov EBX, lBufSize ' EBX = SizeOf(sBuffer) ! dec EDX ! pop ECX ' ECX = Number of Chars left of sBuffer from the second Char of sWantedString - this is OK since lRetVal returned is offset 1, not 0 ! sub EBX, EDX ! pop EDI ' Get back previous positions - here: It only serves as a Stack-balance ! sub EBX, ECX ' EBX = sBuffer Offset to the first character of sWantedString ! mov lRetVal, EBX ' This offset-pointer (the Char following sWantedString) is stored in lRetVal ! jmp EndCC_Instr ' -------- RIGHT-TO-LEFT search starts here ------------ ' PARAGRAPH align the top of the RightToLeftSearchStart (to relative pos. 0) ' This code is never executed - its sole function is to fill up to the next paragraph ! nop ! nop ' PARAGRAPH alignment RightToLeftSearchStart: ' This is the Search-Backwards-part. The logic is identical, but we decrease string pointers instead. ' Make ESI and EDI to point to the end of their strings ! add EDI, ECX ' EDI = pointer to the rightmost position of sBuffer + 1 ! nop ! dec EDI ' EDI = pointer to the rightmost position of sBuffer ! add ESI, EDX ' ESI = pointer to the rightmost position of sWantLen + 1 ! nop ! dec ESI ' ESI = pointer to the rightmost position of sWantLen ! mov psWantString, ESI ' Adjust ECX so that we do not search more of sBuffer than can fit sWantLen ! sub ECX, EDX ' ECX = LEN(sBuffer), EDX = LEN(sWantString) --> ECX = adjusted number of chars to search ! jc RevEndFindString ' If CY, then lWantLen > LEN(sBuffer) ! inc ECX ' ECX = LEN(sBuffer) - LEN(sWantString) + 1 ' Check that lStartPos is not less than 1 ! nop ! mov EAX, lStartPos ' EAX = pointer to lStartPos ! nop ! mov EDX, [EAX] ' EDX = Value of lStartPos ! cmp ECX, EDX ' Compare LEN(sBuffer) to lStartPos ! jc RevEndFindString ' If lStartPos > LEN(sBuffer) - exit ! dec EDX ! nop ! cmp EDX, 0 ' Check lStartPos > 0 upon input ! nop ! jl RevEndFindString ! sub EDI, EDX ' EDI = pointer to the start-to-search position of sBuffer, offset by lStartPos as appropriate ! nop ! nop ! sub ECX, EDX ' ECX = ECX - lStartPos ! nop ! nop ! nop ! nop ! nop ! nop ' PARAGRAPH alignment ' Look for the first character to match RevFindFirstCharLoopTopInit: ! mov ESI, psWantString ! nop ! nop ' DWORD alignment ! movzx EAX, BYTE PTR [ESI] ' Get sWantedString's first character into AL ! nop ! movzx EDX, BYTE PTR [EBX+EAX] ' Get sWantedString's first charcter's value (or SORT-WEIGHT if you're using an ARRAY SORT COLLATE string as sCollateString) into DL ' The RevFindFirstCharLoopTop label is now PARAGRAPH aligned (relative address 0) RevFindFirstCharLoopTop: ! movzx EAX, BYTE PTR [EDI] ' Get sBufferString's first charcter into AL ! movzx EAX, BYTE PTR [EBX+EAX] ' Get sBufferString's first character's value (or SORT-WEIGHT if you're using an ARRAY SORT COLLATE string as sCollateString) into AL ! dec EDI ! cmp EAX, EDX ' Compare DL with AL ! je RevFindRestCharsInit ! dec ECX ! jnz RevFindFirstCharLoopTop ! jmp EndCC_Instr ' ECX = 0 --> No strings found. To test: Use 1-letter strings as well! ' This code is never executed - its sole purpose is to fill up to the next paragraph ' .... the seemingly longer-than-needed string of NOPs are necessary due to jumps becoming ' long or short in ways not fit for paragraph alignment. No harm done though - this is never ' executed. ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ! nop ' PARAGRAPH alignment RevFindRestCharsInit: ' Here: Check if the rest of the string matches also ! dec ECX ! push EDI ' Save the current position in sBuffer ! push ECX ' ECX = Remaining chars of sBuffer ! nop ' DWORD alignment ! mov ECX, lWantLen ' Number of characters in sWantedString to ECX ! dec ESI ' Move the pointer to the next Char of sWantString before comparison commences ... ! dec ECX ! jz RevStringEqual ' Is the Wanted String empty now (could have been only 1 char)? ! nop ! nop ' PARAGRAPH alignment RevFindRestCharsLoopTop: ! movzx EAX, BYTE PTR [EDI] ' Get sBufferString's next charcter into AL (we're still only using AL) ! movzx EDX, BYTE PTR [EBX+EAX] ' Get sBufferString's next character's value (or SORT-WEIGHT if you're using an ARRAY SORT COLLATE string as sCollateString) into DL ! movzx EAX, BYTE PTR [ESI] ' Get sWantedString's next character into AL ! movzx EAX, BYTE PTR [EBX+EAX] ' Get sWantedString's next charcter's value (or SORT-WEIGHT if you're using an ARRAY SORT COLLATE string as sCollateString) into AL ! dec ESI ! dec EDI ! dec ECX ' Check if ECX=0 ! jz RevFindRestCharsLoopEnd ' If ECX = 0, no more characters in sWantedString ! cmp EAX, EDX ' Compare AL with DL ! je RevFindRestCharsLoopTop ' If equal - check next char as well ! pop ECX ' ECX = Remaining chars of sBuffer ! pop EDI ' EDI = Save the current position in sBuffer ! cmp ECX, 0 ' ECX = 0? ! je EndCC_Instr ' If ECX is zero, we have searched the entire buffer with no sWantedString ! jmp RevFindFirstCharLoopTopInit ' Label RevFindRestCharsLoopEnd is PARAGRAPH aligned RevFindRestCharsLoopEnd: ! cmp EAX, EDX ' ECX = 0, but the last characters aren't compared yet ! je RevStringEqual ' If comparison was successful - and the last character of sWantedString matched sBuffer ! pop ECX ' ECX = Remaining chars of sBuffer ! pop EDI ' EDI = Save the current position in sBuffer ! cmp ECX, 0 ' ECX = 0? ! je EndCC_Instr ' If ECX is zero, we have searched the entire buffer with no sWantedString ! jmp RevFindFirstCharLoopTopInit ' This code is never executed - its sole purpose is to fill up to the next paragraph ! nop ! nop ! nop ' PARAGRAPH alignment RevEndFindString: ' We did not locate sWantedString or sWantedString was in part found at the very end of sBuffer, so nothing more to look for! ' Or ... and illegal condition was identified during initialization. Either way, we're through. ! xor EAX, EAX ! mov lRetVal, EAX ! jmp short EndCC_Instr ' This code is never executed - its sole purpose is to fill up to the next paragraph ! nop ! nop ! nop ! nop ! nop ! nop ! nop ' PARAGRAPH alignment RevStringEqual: ! pop ECX ! inc ECX ! mov lRetVal, ECX ' This position is DWORD aligned, so no adjustments made ... EndCC_Instr: ! POPAD FUNCTION = lRetVal END FUNCTION ' ============================================================================================================================================= FUNCTION PBMAIN () AS LONG LOCAL sXlat, sString, sSubString AS STRING LOCAL i, lPos AS LONG ' FUNCTION CC_Instr( lStartPos AS LONG, sBuffer AS STRING, sWantedString AS STRING, sCollateString AS STRING, lDirection AS LONG ) AS LONG sXlat = CHR$(0 TO 255) ' The ANSI part of UCASE$() is not defined in PB versions below CC4.03 or WIN8.03, ' except maybe the .02 versions which I have never seen - have they been generally available? #IF %DEF(%PB_DLL32) #IF %PB_REVISION > &H802 sXlat = UCASE$(sXlat, ANSI) #ELSE sXlat = UCASE$(sXlat) #ENDIF #ELSE #IF %PB_REVISION > &H402 sXlat = UCASE$(sXlat, ANSI) #ELSE sXlat = UCASE$(sXlat) #ENDIF #ENDIF sString = "This is my string to be searched" sSubString = "hIs" lPos = CC_Instr(1, sString, sSubString, sXlat, %CC_INSTR_SEARCHFORWARD) ' When successfully run, lPos = 2 and one of the messages below will display it. #IF %DEF(%PB_DLL32) ' Code to display results for the Windows GUI compilers. MSGBOX "sSubString = " + $DQ + sSubString + $DQ + " was found in pos." + STR$(lPos) + " of " + $CRLF + $DQ + sString + $DQ + "." #ELSE ' Code to display results for the Console compilers. PRINT "sSubString = " + $DQ + sSubString + $DQ + " was found in pos." + STR$(lPos) + " of " + $DQ + sString + $DQ + "." WAITKEY$ #ENDIF END FUNCTION ' ===============================================================================================================================================================================
----------------------------------------------------------------------------------------------------------
"If debugging is the process of removing bugs, then programming must be the process of putting them in." - Unknown
"You can fool some of the people all of the time, and all of the people some of the time, but you can make
a fool of yourself anytime." - Unknown
Comment