![]() |
![]() |
![]() |
![]() |
27 Strings (strings.hhf)
HLA provides a sophisticated string handling package. The string data type has been carefully designed for high performance operations and there are lots of routines that perform almost every imaginable standard operation on the string data.
The first place to start is with the discussion of the string data type itself. A string variable is nothing more than a four-byte pointer that points at the actual string data. So anytime you pass a string by value to a procedure or method, you're actually passing a pointer value. Note that taking the address of a string variable (with the LEA instruction) takes the address of the pointer, not the address of the actual character data. Therefore, if you are calling a routine that expects the address of some character data in a register, you would normally move the contents of a string variable into that register, not load the address of that string variable. For example, the atoi routine (see the section on conversions, earlier) expects a pointer to a string variable in the ESI register. If you wish to pass the address of the first character of a string in ESI, you would use the "mov( s, esi);" instruction, not "lea( esi, s );".
The HLA Standard Library makes a couple of important assumptions about where string variables are pointing. First, and most important, string variables must always point at a buffer that is an even multiple of four bytes long. Many string operations move double words, rather than bytes, around to improve performance. If the buffer is not an even multiple of four bytes long, some data transfers may inadvertently wipe out data adjacent to the string buffer or, worse still, cause a general protection fault.
The second assumption the HLA Standard Library makes is that the string data is prefaced by two dword objects. The first (at offset -8 from the beginning of the character data) contains the maximum number of characters that can be stored into this string (not counting a zero terminating byte). This value is fixed when storage is allocated for the string. The HLA string routines use this value to detect a string overflow condition.
The second dword object before the character data (at offset -4) is the current dynamic length of the string (that is, the actual number of characters currently in the string). Since the maximum and dynamic length fields are four bytes long, HLA supports (in theory) strings whose lengths are up to four gigabytes in length. In practice, of course, strings generally don't grow very large.
HLA strings always contain a zero terminating byte. Strictly speaking, this zero terminating byte is not absolutely necessary because the HLA string type includes a dynamic length field. As such, the HLA Standard Library routines tend to ignore the zero terminating byte other than for use as a delimiter in the conversion routines. However, having this zero terminating byte allows you to pass HLA strings as parameters to Windows and Linux API functions and other functions that expect C/C++ style zero terminated strings.
Although not necessary for correct operation, HLA always aligns strings on a double word boundary. This allows certain string operations to run nearly twice as fast as they would if they were not aligned on a double word boundary.
To simplify access to the fields of a string, the string.hhf header file contain a record template you may use to access those fields in a structured fashion. This structure has the following definition:
type strRec: record := -8; MaxStrLen: int32; length: int32; strData: char[12]; endrecord;(The index value after the char type is arbitrary.)
For example, suppose you have a string variable s and you wish to know the current length of this string. You could obtain the length as follows:
mov( s, esi ); mov( (type str.strRec [esi]).length, eax );(Note that the str.strRec type definition appears within the str namespace, hence the "str." prefix).
As a general rule, you should always use stralloc (or some routine that winds up calling stralloc) to allocate storage for string variables. If you must allocate the storage yourself, be sure the storage allocation follows all the rules specified earlier.
Consider what HLA does when you declare an initialized string object as follows:
static s :string := "SomeString";One might be tempted to think that HLA allocates the string data as part of the "s" variable. In fact, this is not the case. HLA places the actual string data (including the length values, terminating byte, and any necessary padding bytes) somewhere else and then initializes the s object with the address of data data (appearing elsewhere). There is no direct way by only referencing s at compile-time, to treat the address of this string object as a constant. This feature would be useful, for example, for initializing string fields of a record constant with the address of the actual string data.
The HLA Standard Library strings.hhf header file provides a macro that lets you declare string constants and attach a label to the first character of that string (which is the address you generally want to assign to a string variable or field). You use this macro almost like the string data type, except you also supply a literal string constant argument, e.g.,
static s :str.constant( "SomeString" );This creates a string object in memory with s's address corresponding to the first character of the string object. Note that s is not an HLA string; remember, an HLA string is a pointer to a string object. The address of s is what would normally appear in a string variable. Now consider the following code:
type r:record s:string; b:byte; endrecord; static somestr :str.constant( "SomeString" ); a :r := r:[ &somestr, 0 ];This initializes the s field of a with a pointer to the string containing the characters "SomeString". This is the proper way to initialize a string field of a record. Note that HLA will accept the following without complaint, but it is not correct:
static somestr :string := "SomeString"; a :r := r:[ &somestr, 0 ];This example initializes the s field of a with the address of somestr. But this is not string data, rather, it's the address of some string data. Therefore, this code initializes field s with a pointer to the pointer of some character data, rather than the pointer to the character data (which is what you probably want).
Here's the implementation of the str.constant macro, just in case you're wondering how it works:
// str.constant( literal_constant ) // // This macro creates a string constant object whose address // you may assign to a string variable. This is useful, for // example, when initializing fields of a record with string data. #macro constant( __strconst ):__strname,__padding; forward( __strname ); align(4); dword @length( __strconst ), @length( __strconst ); __strname:char; @nostorage; byte __strconst, 0; ?__padding := ((4 - ((@length( __strconst ) + 1) mod 4)) mod 4); #while( __padding > 0 ) byte 0; ?__padding -= 1; #endwhile #endmacro;27.1 String Allocation Macros and Functions
str.strvar( size )str.strvar is a macro that will statically allocate storage for a string in the STATIC variable declaration section (you cannot use str.strvar in any of the other variable declaration sections, including the other static sections: READONLY, and STORAGE; you can only use it in the STATIC section). This macro emits the appropriate code to initialize a string pointer variable with the address of appropriate string storage that has sufficient room to hold a string of at least size characters (size is the parameter passed to this macro).
static StaticString: str.strvar( 32 );Since the storage is statically allocated for StaticString, there is no need to call stralloc or any other string/memory allocation procedure to allocate storage for this variable.
The following are brief descriptions of the string routines provided in the HLA strings library. Note that most routines raise an exception if an index error or string overflow occurs. See the source listings for more details.
str.init( var b:var; numBytes:dword ); @returns( "eax" );This function initializes a block of memory for use as a string object. It takes the address of the buffer variable b and aligns this address on a dword boundary. Then it initializes the MaxLength, length, and zero terminating byte fields at the resulting address. Finally, it returns a pointer to the newly created string object in EAX. The numBytes field specifies the size of the entire buffer area, not the desired maximum length of the string. The numBytes field must be 16 or greater, else this routine will raise an ex.ValueOutOfRange exception. Note that string initialization may consume as many as 15 bytes (up to three bytes to align the address on a dword boundary, four bytes for the maxlength field, four bytes for the length field, and the string data area must be a multiple of four bytes long (including the zero terminating byte). This is why the numBytes field must be 16 or greater. Note that this function initializes the resulting string to the empty string. The maxlength field will contain the maxium number of character that you can store into the resulting string after subtracting the zero terminating byte, the sizes of the length fields, and any alignment bytes that were necessary.
27.2 String Length Calculation
str.length( src ); @returns( "eax" );The src parameter can either be a string variable or a 32-bit register that points at valid string data. This macro returns the length field of the specified string in the EAX register.
str.mLength( src ); // returns the value in EAXMacro implementation of the above procedure. Much faster, but you cannot use it buried inside another expression or instruction. (e.g., "mov( str.mLength(s), ecx);" is illegal).
27.3 String Assignment, Substring, and Concatenation Functions
str.cpy( src:string; dest:string );Copies the characters and dynamic length from the source string to the destination string.
str.a_cpy( src:string ); @returns( "eax" );This routine allocates sufficient storage on the heap to make a copy of the source string. After copying the string, this routine returns a pointer to the new string in EAX. When you are done with this string's data, call strfree to deallocate the storage.
str.setstr( src:char; dest:string; cnt:uns32 ); str.a_setstr( src:char; count:uns32 ); @returns( "eax" );These routines construct a string by concatenating cnt copies of the src character together. The str.strset routine stores these characters into the destination string. The str.a_strset routine allocates storage for the string. Both routines return a pointer to the newly created string in EAX. When you are finished with the data created by str.a_strset, you should call strfree to release the storage associated with the string.
str.cat( src:string; dest:string ) str.a_cat( src1:string; src2:string ); @returns( "eax" );These routines concatenate two strings. The str.cat routine concatenates the source string to the end of the destination string (the destination string's MaxLength must be large enough to hold both strings). The str.a_cat routine allocates storage on the heap, copies the src2 string to the new storage, and then concatenates the src1 string to the end of the src2 string. Both routines return a pointer to the string in EAX. When you are done with the data created by str.a_cat, you should call strfree to release the storage.
27.4 String Comparison, Search, and Scanning Functions
str.eq( left:string; right:string ); @returns( "al" );Compares the left string to the right string and returns true in AL/EAX1 if the two strings are equal.
str.ne( left:string; right:string ); @returns( "al" );Compares the left string to the right string and returns true in AL/EAX if the two strings are not equal.
str.lt left:string; right:string ); @returns( "al" );Compares the left string to the right string and returns true in AL/EAX if the left string is less than the right string.
str.le( left:string; right:string ); @returns( "al" );Compares the left string to the right string and returns true in AL/EAX if the left string is less than or equal to the right string.
str.gt( left:string; right:string ); @returns( "al" );Compares the left string to the right string and returns true in AL/EAX if the left string is greater than the right string.
str.ge( left:string; right:string ); @returns( "al" );Compares the left string to the right string and returns true in AL/EAX if the left string is greater than or equal to the right string.
str.ieq( left:string; right:string ); @returns( "al" );Compares the left string to the right string and returns true in AL/EAX2 if the two strings are equal using a case insenstive comparison.
str.ine( left:string; right:string ); @returns( "al" );Compares the left string to the right string and returns true in AL/EAX if the two strings are not equal using a case insenstive comparison.
str.ilt left:string; right:string ); @returns( "al" );Compares the left string to the right string and returns true in AL/EAX if the left string is less than the right string using a case insenstive comparison.
str.ile( left:string; right:string ); @returns( "al" );Compares the left string to the right string and returns true in AL/EAX if the left string is less than or equal to the right string using a case insenstive comparison.
str.igt( left:string; right:string ); @returns( "al" );Compares the left string to the right string and returns true in AL/EAX if the left string is greater than the right string using a case insenstive comparison.
str.ige( left:string; right:string ); @returns( "al" );Compares the left string to the right string and returns true in AL/EAX if the left string is greater than or equal to the right string using a case insenstive comparison.
str.index( src1:string; src2:string ); @returns( "eax" );This function returns the zero-based index of src2 within src1 (that is, it locates the first occurrence of the string src2 appearing inside src1). The index is returned in the EAX register. If src2 does not appear in src1, then this function returns -1 in the EAX register.
str.iindex( src1:string; src2:string ); @returns( "eax" );Just like str.index, except this one does a case insensitive comparison.
str.index2( src1:string; offs:uns32; src2:string ); @returns( "eax" );This function returns the zero-based index of src2 within src1 starting at character position offs in src1 (that is, it locates the first occurrence of the string src2 appearing inside src1 at or after src1[offs]). The index is returned in the EAX register. If src2 does not appear in src1, then this function returns -1 in the EAX register.
str.iindex2( src1:string; offs:uns32; src2:string ); @returns( "eax" );Just like str.index2 except this function does a case insensitive comparison.
str.rindex( src1:string; src2:string ); @returns( "eax" );This function returns the zero-based index of the last occurrence of src2 within src1 (that is, it searches backwards in src1). The index is returned in the EAX register. If src2 does not appear in src1, then this function returns -1 in the EAX register.
str.irindex( src1:string; src2:string ); @returns( "eax" );Just like str.rindex, except that this function does a case insensitive search.
str.rindex2( src1:string; offs:uns32; src2:string ); @returns( "eax" );This function returns the zero-based index of the last occurrence of src2 within src1 that occurs before character postion offs in the string (that is, it searches backwards in src1 starting at character position offs). The index is returned in the EAX register. If src2 does not appear in src1, then this function returns -1 in the EAX register.
str.irindex2( src1:string; offs:uns32; src2:string ); @returns( "eax" );Just like str.rindex2 except this function does a case insensitive search.
str.prefix( src1:string; src2:string ); @returns( "al" );This function returns true if src2 is a "prefix" of src1 (if src1 begins with the string src2). This function returns false in EAX if src2 is not a prefix of src1.
str.prefix2( src1:string; offs:uns32; src2:string ); @returns( "al" );This function returns true if src2 is a prefix of the substring of src1 starting at character position offs in src1. This function returns false in EAX if src2 is not a prefix of src1 starting at character position offs within src1.
str.iprefix( src1:string; src2:string ); @returns( "al" ); str.iprefix2( src1:string; offs:uns32; src2:string ); @returns( "al" );Just like str.prefix and str.prefix2 except these functions do a case insensitive string comparison.
str.chpos( src1:string; src2:char ); @returns( "eax" );This function returns the zero-based index of the first occurrence of the specified character (src2) within src1. This function returns -1 if src2 is not found within src1.
str.chpos2( src1:string; offs:uns32; src2:char ); @returns( "eax" );This function returns the zero-based index of the first occurrence of the specified character (src2) within src1, starting at character position offs within src1. This function returns -1 if src2 is not found within src1 at or after position offs in the string.
str.rchpos( src1:string; src2:char ); @returns( "eax" );This function returns the zero-based index of the last occurrence of the specified character (src2) within src1. This function returns -1 if src2 is not found within src1.
str.rchpos2( src1:string; offs:uns32; src2:char ); @returns( "eax" );This function returns the zero-based index of the last occurrence of the specified character (src2) before (or at) position offs within the src1. This function returns -1 if src2 is not found within src1.
str.span( src1: string; src2:cset ); @returns( "eax" );This function skips over all characters in src1 that are elements of the character set src2. It returns the index of the first character in src1 that is not a member of the src2 set. This function returns minus one if all characters in src1 are elements of src2.
str.span2( src1: string; start:int32; src2:cset ); @returns( "eax" );Similar to str.span above, except this routine starts at character position start rather than at character position zero when searching through the string.
str.rspan( src1: string; src2:cset ); @returns( "eax" ); str.rspan2( src1: string; start:int32; src2:cset ); @returns( "eax" );Similar to the previous two routines, except these search backwards through the string.
str.brk( src1: string; src2:cset ); @returns( "eax" );This function skips over all characters in src1 that are not elements of the character set src2. It returns the index of the first character in src1 that is a member of the src2 set. This function returns minus one if all characters in src1 are not elements of src2.
str.brk2( src1: string; start:int32; src2:cset ); @returns( "eax" );Similar to str.brk above, except this routine starts at character position start rather than at character position zero when searching through the string.
str.rbrk( src1: string; src2:cset ); @returns( "eax" ); str.rbrk2( src1: string; start:int32; src2:cset ); @returns( "eax" );Similar to the previous two routines, except these search backwards through the string.
str.tokenize( src: string; var dest:dword ); @returns( "eax" ); str.tokenize2( src: string; var dest:dword; delims:cset ); @returns( "eax" );These two routines lexically scan3 a string and break it up into "lexemes" (words), returning an array of pointers to each of the lexemes. The only difference between the two routines is that the tokenize routine uses the following default set of delimiter characters:
{' ', #9, ',', '<', '>', '|', '\', '/', '-'}This character set roughly corresponds to the delimiters used by the Windows Command Window interpreter or typical Linux shells. If you do not wish to use this particular set of delimiter characters, you may call str.tokenize2 and specify the characters you're interested in.
The tokenize routines begin by skipping over all delimiter characters at the beginning of the string. Once they locate a non-delimiter character, they skip forward until they find the end of the string or the next delimiter character. Then they allocate storage for a new string on the heap and copy the delimited text to this new string. A pointer to the new string is stored into the dword array passed as the first parameter to tokenize(2). This process is repeated for each lexeme found in the src string.
As these functions are intended for processing command lines, any quoted string (a sequence of characters surrounded by quotes or apostrophies) is treated as a single token/string by these functions. If this behavior is a problem for you, it's real easy to modify the str.tokenize source file to handle this issue.
Warning: the dest parameter should be an array of strings. This array must be large enough to hold pointers to each lexeme found in the string. In theory, there could be as many as str.length(src)/2 lexemes in the source string.
On return from these functions, the EAX register will contain the number of lexemes found and processed in the src string (i.e., EAX will contain the number of valid elements in the dest array).
When you are done with the strings allocated on the heap, you should free them by calling strfree. Note that you need to call strfree for each active pointer stored in the dest array.
Here is an example of a call to the str.tokenize routine:
program tokenizeDemo; #include( "stdio.hhf" ); #include( "string.hhf" ); #include( "memory.hhf" ); static strings: string[16]; ParseMe: string := "This string contains five words"; begin tokenizeDemo; str.tokenize( strings, ParseMe ); mov( 0, ebx ); while( ebx < eax ) do stdout.put ( "string[", (type uns32 ebx), "]=""", strings[ebx*4], """", nl ); strfree( strings[ebx*4] ); inc( ebx ); endwhile; end tokenizeDemo;Program 8 Demonstration of str.tokenize
This program produces the following output:
string[0]="This" string[1]="string" string[2]="contains" string[3]="five" string[4]="words"27.5 String Insertion, Deletion and Extraction Functions
str.substr( src:string; dest:string; start:dword; len:dword )This substring routine extracts characters src[start]..src[start+len-1] and copies them to the destination string.
str.a_substr( src:string; start:dword; len:dword ); @returns( "eax" );This routine is similar to the previous routine, except it allocates storage for the substring on the heap and returns a pointer to this storage in the EAX register. You should call strfree to free up the storage allocated by this routine when you are done with the substring.
str.insert( src:string; dest:string; start:dword )This routine inserts the string src into the destination string dest immediately before the character at index start.
str.a_insert( src1:string; src2:string; start:dword ); @returns( "eax" );This routine creates a new string on the heap and copies src2 to the new storage. Then it inserts the string src1 into the new string immediately before the character at index start. It returns a pointer to this new string in the EAX register. You should call the strfree procedure to free up the storage allocated by this routine when you are done with the new string.
str.delete( dest:string; start:dword; length:dword )This routine removes length characters from the string dest starting with the character at position start.
str.a_delete( src:string; start:dword; length:dword ); @returns( "eax" );This routine makes a copy of src on the heap and then deletes length characters from this new string beginning at index start. This routine returns a pointer to the newly allocated string in the EAX register. When you are done with this string data, you should call strfree to free up the storage.
str.truncate( dest:string; length:dword )This routine removes length characters from the end of the string dest .
str.a_truncate( src:string; start:dword; length:dword ); @returns( "eax" );This routine makes a copy of src on the heap and then deletes length characters from the end of this new string. This routine returns a pointer to the newly allocated string in the EAX register. When you are done with this string data, you should call strfree to free up the storage.
str.replace( dest: string; fromStr:string; toStr:string ) str.a_replace( src: string; fromStr:string; toStr:string ); @returns( "eax" );These two routines modify the destination string by replacing occurrences of characters in the fromStr with the corresponding character found in the toStr string. That is, while scanning through the dest or src strings, these routines compare each character in the string against the characters found in fromStr. If the current character in src/dest is found in fromStr, then these routines take the corresponding character in toStr (i.e., the character at the same offset in toStr as the match in fromStr) and they replace the character in the output string. If toStr is shorter than fromStr, then these routines will delete all characters found in fromStr that do not have a corresponding character in toStr. If toStr is longer than fromStr, these routines ignore the extra characters in toStr.
The str.replace routine directly replaces the characters in the dest string. The str.a_replace routine first makes a copy of the src string (by calling stralloc) and then operates on the copy of the string. str.a_replace returns a pointer to the newly allocated (and converted) string in the EAX register. You should call strfree when you are done with this string.
27.6 String Conversion Functions
str.upper( dest: string ) str.lower( dest: string ) str.a_upper( src: string ); @returns( "eax" ); str.a_lower( src: string ); @returns( "eax" );These routines convert all the alphabetic characters in a string to upper or lower case (as the name suggests). The str.upper and str.lower routines operate directly on the string passed as a parameter. The str.a_upper and str.a_lower routines make a copy of the string on the heap and process the characters in this copy; they return a pointer to the new string in EAX You should call the strfree routine to deallocate the storage created by str.a_lower and str.a_upper when you are done with the strings they've created.
str.delspace( dest: string ) str.a_delspace( src: string ); @returns( "eax" );These two routines delete leading spaces and tabs from a string. str.delspace operates directly on the string passed as a parameter, str.a_delspace makes a copy of the string on the heap and then deletes the spaces from that string. The str.a_delspace procedure returns a pointer to the new string in EAX. After you are done with the string created by str.a_delspace, you should call strfree to release the storage.
str.trim( dest: string ) str.a_trim( src: string ); @returns( "eax" );The str.trim and str.a_trim routines delete both leading and trailing spaces and tabs from a string. The str.trim routine operates directly on the string passed as a parameter, the str.a_trim routine makes a copy of the source string and operates on that copy. The str.a_trim procedure returns a pointer to the new string in EAX. When you are done with the string created by str.a_trim, you should call strfree to release its storage.
27.7 String Construction Functions
The string construction functions convert their parameter data to a string and append this string to the end of another string. These routines are very similar to the stdout procedures except that they write their output to a string instead of the standard output device.
27.7.1 Boolean Concatenation Function
str.catbool( b:boolean; dest:string );This function concatenates the string "true" or "false" to the end of the destination string you supply as a parameter. The destination string must be large enough to hold the concatenated result or this function will raise an exception.
27.7.2 Character and String Concatenation Functions
str.catc( c:char; dest:string );This function concatenates the character parameter to the end of the destination string. The destination string must be large enough to hold the concatenated result or this function will raise an exception.
str.catcSize( c:char; width:int32; fill:char; dest:string );This function builds a string whose length is the absolute value of width (or length one if width is zero) and concatenates this string to the end of dest. If width is negative, the string this function constructs consists of width-1 fill characters followed by the value of c. If width is positive, then the string this function creates consists of c's value followed by width-1 fill chars.
str.cats( s:string; dest:string );This function is functionally equivalent to str.cat. It concatentates s to the end of dest.
str.catsSize( s:string; width:int32; fill:char; dest:string );This function builds a string whose length is the absolute value of width (or length one if width is zero) or the length of s, whichever is greater, and concatenates this string to the end of dest. If width is negative, the string this function constructs consists of the characters in s followed by the fill character. If width is positive, then the string this function puts s' characters at the end of the string.
str.catcset( c:cset; dest:string );This function translates the character set to a string (holding each character in the set in some sequence) and appends this string to the end of the dest string.
27.7.3 Hexadecimal String Concatentation Functions
str.catb( b:byte; dest:string ); str.catbSize( b:byte; size:dword; fill:char )This function translates the value of b to a two-character string that is the hexadecimal representation of b and concatenates these two characters to the end of dest. The str.catbSize function lets you specify a minimum field width and a fill character. The str.catb routine uses a minimum size of two and a fill character of '0'.
str.catw( w:word; dest:string ); str.catwSize( w:word; size:dword; fill:char )This function translates the value of w to a four-character string that is the hexadecimal representation of w and concatenates these four characters to the end of dest. The str.catwSize function lets you specify a minimum field width and a fill character. The str.catw routine uses a minimum size of two and a fill character of '0'.
str.catd( d:dword; dest:string ) str.catdSize( d:dword; size:dword; fill:char )This function translates the value of d to an eight-character string that is the hexadecimal representation of d and concatenates these eight characters to the end of dest. The str.catdSize function lets you specify a minimum field width and a fill character. The str.catd routine uses a minimum size of two and a fill character of '0'.
str.catq( q:qword; dest:string ) str.catqSize( q:qword; size:dword; fill:char )This function translates the value of q to a 16-character string that is the hexadecimal representation of q and concatenates these 16 characters to the end of dest. The str.catqSize function lets you specify a minimum field width and a fill character. The str.catq routine uses a minimum size of two and a fill character of '0'.
27.7.4 Unsigned Integer String Concatenation Functions
These routines convert unsigned integer values to a string and concatenate that string to the end of the dest string you supply as a parameter. The str.catxxxSize functions contain width and fill parameters that let you specify the minimum field width when converting the value.
If the absolute value of width is greater than the number of print positions the value requires, then these functions output width characters to the output file. If width is non-negative, then these functions right-justify the value in the output field; if value is negative, then these functions left-justify the value in the output field.
These functions print the fill character as the padding value for the extra print positions.
![]()
Figure 21 str.catuxxxSize Output Format
str.catu8size ( u8:byte; width:int32; fill:char; dest:string );This function converts the unsigned 8-bit value u8 to a string using the width and fill values as specified above. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.
str.catu16size ( u16:word; width:int32; fill:char; dest:string );This function converts the unsigned 16-bit value u16 to a string using the width and fill values as specified above. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.
str.catu32size ( u32:dword; width:int32; fill:char; dest:string );This function converts the unsigned 32-bit value u32 to a string using the width and fill values as specified above. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.
str.catu64size ( u64:qword; width:int32; fill:char; dest:string );This function converts the unsigned 64-bit value u64 to a string using the width and fill values as specified above. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.
str.catu8( u8:byte; dest:string );This function converts the eight-bit unsigned integer you pass as a parameter to a string using the minimum number of print positions the number requires. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.
str.catu16( u16:word; dest:string );This function converts the 16-bit unsigned integer you pass as a parameter to a string using the minimum number of print positions the number requires. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.
str.catu32( u32:dword; dest:string );This function converts the 32-bit unsigned integer you pass as a parameter to a string using the minimum number of print positions the number requires. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.
str.catu64( u64:qword; dest:string );This function converts the 64-bit unsigned integer you pass as a parameter to a string using the minimum number of print positions the number requires. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.
27.7.5 Signed Integer String Concatenation Functions
These routines convert signed integer values to string and concatenate that string to the end of the string you specify via the dest parameter. The str.catxxxSize functions contain width and fill parameters that let you specify the minimum field width when outputting a value.
If the absolute value of width is greater than the number of print positions the value requires, then these functions output width characters to the output file. If width is non-negative, then these functions right-justify the value in the output field; if value is negative, then these functions left-justify the value in the output field.
These functions print the fill character as the padding value for the extra print positions.
Note that unlike floating point values, these functions do not print a space in front of the value if it is non-negative.
![]()
Figure 22 str.catixxSize Output Format
str.cati8size ( i8:byte; width:int32; fill:char; dest:string );This function converts the eight-bit signed integer you pass as a parameter to a string using the minimum number of print positions the number requires. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.
str.cati16size ( i16:word; width:int32; fill:char; dest:string );This function converts the 16-bit signed integer you pass as a parameter to a string using the minimum number of print positions the number requires. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.
str.cati32size ( i32:dword; width:int32; fill:char; dest:string );This function converts the 32-bit signed integer you pass as a parameter to a string using the minimum number of print positions the number requires. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.
str.cati64size ( i64:qword; width:int32; fill:char; dest:string );This function converts the 64-bit signed integer you pass as a parameter to a string using the minimum number of print positions the number requires. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.
str.cati8( i8:byte; dest:string );This function converts the eight-bit signed integer you pass as a parameter to a string using the minimum number of print positions the number requires. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.
str.cati16( i16:word; dest:string );This function converts the 16-bit signed integer you pass as a parameter to a string using the minimum number of print positions the number requires. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.
str.cati32( i32:dword; dest:string );This function converts the 32-bit signed integer you pass as a parameter to a string using the minimum number of print positions the number requires. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.
str.cati64( i64:qword; dest:string );This function converts the 64-bit signed integer you pass as a parameter to a string using the minimum number of print positions the number requires. It then concatenates this string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.
27.7.6 Floating Point String Concatenation Using Scientific Notation
The floating point numeric string conversion routines translate the three different binary floating point formats to their string representation and then concatenate this string the end of the string that the dest parameter specifies. There are two generic classes of these routines: those that convert their values to exponential/scientific notation and those that convert their string to a decimal form.
The str.cate80, str.cate64, and str.cate32 routines convert their values to a string using scientific notation. These three routines each have two parameters: the value to output and the field width of the result. These routines produce a string with the following format:
![]()
Figure 23 Floating Point Scientific Notation Conversion Format
str.cate80 ( r:real80; width:int32; dest:string );This function converts the 80-bit extended precision floating point value passed in r to a string using scientific/exponential notation. This procedure converts the value using width print positions. width should have a minimum value of five for real numbers in the range 1e-9..1e+9 and a minimum value of six for all other values. Note that 80-bit extended precision floating point values support about 18 significant digits. So a width value that yeilds more than 18 mantissa digits will produce garbage output in the low order digits of the number. After conversion, this function concatenates the string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.
str.cate64 ( r:real64; width:int32; dest:string );This function converts the 64-bit double precision floating point value passed in r to a string using scientific/exponential notation. This procedure converts the value using width print positions. width should have a minimum value of five for real numbers in the range 1e-9..1e+9 and a minimum value of six for all other values. Note that 64-bit extended precision floating point values support about 15 significant digits. So a width value that yeilds more than 15 mantissa digits will produce garbage output in the low order digits of the number. After conversion, this function concatenates the string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.
str.cate32 ( r:real32; width:int32; dest:string );This function converts the 32-bit single precision floating point value passed in r to a string using scientific/exponential notation. This procedure converts the value using width print positions. width should have a minimum value of five for real numbers in the range 1e-9..1e+9 and a minimum value of six for all other values. Note that 32-bit extended precision floating point values support about 6-7 significant digits. So a width value that yeilds more than seven mantissa digits will produce garbage output in the low order digits of the number. After conversion, this function concatenates the string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.
27.7.7 Floating Point String Concatentation Using Decimal Notation
Although scientific (exponential) notation is the most general display format for real numbers, real numbers you display in this format are very difficult to read. Therefore, the HLA strings module also provides a set of functions that convert real values to strings using the decimal representation. Although you cannot (practically) use these decimal output routines for all real values, they are applicable to a wide variety of common numbers you will use in your programs.
These functions come in two varieties. The first variety requires four parameters: the real value to convert, the width of the converted value, the number of digit positions to the right of the decimal point, and a padding character. The second variety only requires the first three parameters and assumes the padding character is a space. These functions write their values using the following string format:
![]()
Figure 24 str.catrxx Conversion Format
str.catr80 ( r:real80; width:int32; decpts:uns32; pad:char; dest:string );This procedure converts an 80-bit extended precision floating point value to a string. The string consumes exactly width characters in the output file. If the numeric output, using the specified number of positions to the right of the decimal point, is sufficiently small that the string representation would be less than width characters, then this procedure uses spaces as the padding character to fill the output with width characters. After conversion, this function concatenates the string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.
str.catr64 ( r:real64; width:int32; decpts:uns32; pad:char; dest:string );This procedure converts a 64-bit double precision floating point value to a string. The string consumes exactly width characters in the output file. If the numeric output, using the specified number of positions to the right of the decimal point, is sufficiently small that the string representation would be less than width characters, then this procedure uses spaces as the padding character to fill the output with width characters. After conversion, this function concatenates the string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.
str.catr32 ( r:real32; width:int32; decpts:uns32; pad:char; dest:string );This procedure converts a 32-bit single precision floating point value to a string. The string consumes exactly width characters in the output file. If the numeric output, using the specified number of positions to the right of the decimal point, is sufficiently small that the string representation would be less than width characters, then this procedure uses spaces as the padding character to fill the output with width characters. After conversion, this function concatenates the string to the end of the dest string. This function raises an exception if the resulting string's length is greater than dest's MaxStrLen value.
27.8 Generic String Formatting Function
str.put( dest:string, values_to_convert );
str.put is a macro that automatically invokes an appropriate str.catxxx output routine based on the type of the parameter(s) you pass it. This is a very convenient output routine and is probably the string conversion output call you will use most often in your programs. Keep in mind that this macro is not a single function call; instead, HLA translates this macro into a sequence of calls to procedures like str.cati32, str.cats, etc.
str.put is a macro that provides a flexible syntax for converting data to a string. This macro allows a variable number of parameters. For each parameter present in the list, str.put will call the appropriate routine to emit that data, according to the type of the parameter. Parameters may be constants, registers, or memory locations. You must separate each macro parameter with a comma.
Here is an example of a typical invocation of str.put:
str.put( dest, "I=", i, " j=", j );The above is roughly equivalent to
str.puts( "I=", dest ); str.puti32( i, dest ); str.puts( " j=", dest ); str.puti32( j, dest );This assumes, of course, that i and j are int32 variables.
The str.put macro also lets you specify the minimum field width for each parameter you specify. To print a value using a minimum field width, follow the object you wish to print with a colon and the value of the minimum field width. The previous example, using field widths, could look like the following:
str.put( dest, "I=", i:2, " j=", j:5 );Although this example used the literal decimal constants two and five for the field widths, keep in mind that register values and memory value (integers, anyway) are prefectly legal here.
For floating point numbers you wish to display in decimal form, you can specify both the minimum field width and the number of digits to print to the right of the decimal point by using the following syntax:
str.put( dest, "Real value is ", f:10:3, nl );The str.put macro can handle all the basic primitive types, including boolean, unsigned (8, 16, 32), signed (8, 16, 32), character, character set, real (32, 64, 80), string, and hexadecimal (byte, word, dword).
If you specify a class variable (object) and that class defines a "toString" method, the str.put macro will call the associated toString method and concatenate that string to the end of the destination string. Note that the toString method must dynamically allocate storage for the string by calling stralloc. This is because str.put will call strfree on the string once it outputs the string.
There is a known "design flaw" in the str.put macro. You cannot use it to print HLA intermediate variables (i.e., non-local VAR objects). The problem is that HLA's syntax for non-local accesses takes the form "reg32:varname" and str.put cannot determine if you want to print reg32 using varname print positions versus simply printing the non-local varname object. If you want to display non-local variables you must copy the non-local object into a register, a static variable, or a local variable prior to using stdout.put to print it. Of course, there is no problem using the other str.catXXXX functions to display non-local VAR objects, so you can use those as well.
27.9 Zero-Terminated String Support
Although HLA's string format is more efficient (with respect to speed) than the zero-terminated string format that languages like C, C++, and Java use, HLA programs must often interact with code that expects zero-terminated strings. Examples include HLA (assembly) code you like with C/C++/Java programs and calls you make to operating systems like Windows and Linux (that expect zero terminated strings). Therefore, the HLA Standard Library provides a limited amount of support for zero-terminated strings so it can efficiently interact with external code that requires such strings.
When passing read-only string data to some code that expects a zero-terminated string, HLA's string format is upwards compatible with zero-terminated strings. No conversion is necessary. An HLA string variable holds the address of a sequence of characters that end with a zero byte (the zero-terminated format). So as long as the code you're calling doesn't attempt to write any data to the string object, you can pass HLA string objects to functions and procedures that expect zero-terminated strings.
If the procedure or function you're calling stores data into a destination string variable, then you generally should not pass an HLA string to that function. There are two problems with this: first, the function does not check the HLA string's maximum length field to ensure that string overflow does not occur; second, the external function does not properly set the HLA string's length field before returning. Furthermore, the external code may create it's own string data in some buffer and does not even allocate space for HLA's maximum length and dynamic length fields. To workaround these limitations, HLA provides various procedures in the Standard Library that manipulate zero-terminated strings so your programs can effectively communicate with external code that operates on such strings.
Before describing the support functions that HLA provides for zero-terminated strings, it's probably worthwhile to first discuss how one writes code that comfortably co-exists with such strings. As noted above, there are three major problems one must deal with when external code processes zero-terminated strings. We'll deal with these issues one at a time.
The first problem is that the external code does not check the maximum string length field before writing character data to a string object. Therefore, the external code cannot determine if a buffer overflow will occur when that function extends the string's length. Algorithms that depend upon the string function raising an exception when a buffer overflow occurs will not work properly when calling external code that manipulates zero-terminated strings. The solution to this problem is the same as the solution in C and C++: the programmer must take the responsibility of ensuring that there is sufficient buffer space available to hold the string the external function produces. Exactly how much space you must allocate as a maximum varies on a call by call basis, but usually you can pick a sufficiently large value that is safe and preallocate storage for an HLA string whose maximum length satisifies the program's requirements. Note that most operating system API functions that return variable length strings will let you specify a maximum length parameter so the OS will not overflow your string buffer; well-written library routines and other code that create variable length zero-terminated strings and generally provide this same functionality.
The second problem, the fact that the external code that manipulates the string's data does not update HLA's string length field, is solvable by computing the length of the zero-terminated string upon return from the external code and updating the length field yourself. A convenient way to handle this operation is to write a wrapper function that you call from your code. The wrapper function calls the external code and then computes and updates the HLA string length field before returning to the original caller. This saves having to compute the length on each and every invocation of the external code. The HLA Standard Library provides a string length function that efficiently computes the length of a zero-terminated string. You can call this function upon return from the external code and then store the return result into the HLA dynamic length field.
Some external functions may create their own zero-terminated strings rather than store their string data in a buffer you supply. Such functions will probably not allocate storage for the dynamic and maximum length fields that the HLA string format requires. Therefore, you cannot directly use such string data as an HLA string in your assembly code. There are two ways to handle such string data: (1) copy the zero-terminated string to an HLA string and then manipulate the HLA string, or, (2) process the zero-terminated string using functions that directly manipulate such strings. The HLA strings module provides a set of zero-terminated string functions that let you choose either mechanism. The choice of method (1) or (2) depends entirely upon how you intend to use the string data upon return to your HLA code. If you're going to do considerable string manipulation on that string data within your HLA code (and you want to use the full set of HLA string and pattern matching functions on the string data), it makes a lot of sense to first convert the string to the HLA format. On the other hand, if you're going to do very little manipulation, or if the external function expects your code to update the string data in place (so it can refer to a modified version of the original string data at the original address the external code allocates), then it's probably best to manipulate the string data in-place using a set of zero-terminated string functions. If you need to do considerable string manipulation on some data, but the external code expects you to leave the manipulated string in the original buffer it allocates, you can convert the string to an HLA string, do the modification, and then copy the resulting string back into the original buffer; however, all this copying can be expensive, so you should be careful about using this approach.
The HLA Standard Library provides a small handful of important zero-terminated string functions. This set certainly isn't as extensive as the set of functions available for HLA strings, nor is it as extensive as the set of functions available, for example, in the C Standard Library. However, this small set of functions will probably cover 90-95% of the requirements you'll have for processing zero-terminated strings in HLA code. Generally, if you need other functionality, you can obtain it by calling C Standard Library functions from your HLA code or by first converting the string to an HLA string (and then copying the data back to the original buffer, if necessary). The following subsections describe the functions that the HLA Standard Library provides to support zero-terminated strings.
27.9.1 ZString Length
str.zlen( var zstring:var ); @returns( "eax" );The single parameter is the address of a zero-terminated string. This function returns the length of that string in the EAX register.
Note that the str.zlen function has a single untyped reference parameter. Generally, you'd pass the name of a buffer variable as the parameter to this function. If the address of the zero-terminated string appears in a register, you'll need to use one of the following three invocations to call this function:
// Manual invocation- assumes the string pointer is in EBX: push( ebx ); call str.zlen; << length is in EAX >> . . . str.zlen( [ebx] ); // zlen expects a memory operand . . . str.zlen( val ebx ); // Tell HLA to use value of ebx.The str.zlen function is especially useful for updating the length field of an HLA string you've passed to some external code that generates a zero-terminated string. Consider the following code that updates the length upon return from an external function:
// Allocate sufficient storage to hold the string result the external // code will produce. 1024 was chosen at random for this example, you'll // have to pick an appropriate value based on the size of the string // the external procedure in your code produces. stralloc( 1024 ); mov( eax, strVar ); . . . externalFunction( strVal ); // externalFunction overwrites strVal data. str.zlen( strVal ); // Compute the result string's length mov( strVar, ebx ); // Get pointer to string data. if( eax > (type str.strRec [ebx]).MaxStrLen )) then // If there was a string overflow, the overflow may // have wiped out some important data somewhere, so // it may be too late to raise this exception. However, // better late than not notifying the caller at all. // Because the buffer overflow may have corrupted the application's // data, the application should attempt to terminate as // gracefully as possible at this point. raise( ex.StringOverflow ); endif; // Okay, the string didn't overflow the buffer, update the // HLA string dynamic length field: mov( eax, (type str.strRec [ebx]).length );27.9.2 Converting a ZString to an HLA String
str.cpyz( var zsrc:var; dest:string ); str.a_cpyz( var zsrc:var ); @returns( "eax" );These two functions convert a zero-terminated string to an HLA string. The str.cpyz string copies the zero-terminated string the zsrc parameter specifies to the HLA string the dest parameter specifies. The dest parameter must point at a pre-allocated HLA string whose maximum length is large enough to hold a copy of the source string (HLA will raise an exception if this is not the case). The str.a_cpyz function allocates sufficient storage for the new string on the heap and then copies the zero-terminated string to the new string it creates (since str.a_cpyz always allocates sufficient storage, string overflow cannot occur; this function, however, can raise a memory allocation failure exception). The str.a_cpyz function returns a point to the new HLA string in the EAX register. It is the caller's responsibility to free this storage (via a call to strfree) when the application is done using this string.
Note that the conv.cStrToStr and conv.a_cStrToStr functions are alias of str.cpyz and str.a_cpyz (respectively). The prototypes for these conv.XXXX routines simply specify the external (linker) names of the corresponding str.XXXX procedures.
These two functions are especially useful when some external C/C++ or OS code returns a pointer to a zero-terminated string that you want to manipulate in your HLA code as an HLA string. However, keep in mind that there is a bit of overhead involved with the conversion (specially, these functions must first compute the length of the zero-terminated string and then copy the string data to the HLA string, so they make two passes over the string's data). While the code that does this conversion is fairly efficient, you may want to consider the overhead of this conversion if you're writing time-critical code and you don't use the resultant HLA string very much once the conversion is complete.
27.9.3 Concatenting a ZString to the End of an HLA String
str.catz( var zsrc:var; dest:string );
This function concatenates a zero-terminated string to the end of an existing HLA string. It raises an exception if the resulting string's length would be greater than the maximum length the HLA string allows.
At this time, the HLA Standard Library does not provide the equivalent of an str.a_catz function. If you need this functionality, you can easily achieve it with the following code:
procedure a_catz( hlaStr:string; var zsrc:var ); @nodisplay; @noalignstack; @returns( "eax" ); begin a_catz; push( ebx ); str.zlen( val zsrc ); mov( hlaStr, ebx ); add( (type str.strRec [ebx]).length, eax ); stralloc( eax ); str.cpy( hlaStr, (type string eax) ); str.catz( val zsrc, (type string eax) ); pop( ebx ); end a_catz;27.9.4 Copying a ZString
str.zcpy( var zsrc:var; var dest:var );
The str.zcpy function copies one zero-terminated string to another. The destination buffer must be large enough to hold the source string and it is the caller's responsbility to ensure this. The str.zcpy routine has no way to determine the maximum size of the destination buffer, so it cannot check for buffer overflow (this is typical for zero-terminated string functions).
Since HLA strings are zero-terminated, you can use this function to copy an HLA string to a zero-terminated string. Note, however, that the str.zcpy parameters are both untyped reference parameters, so if you pass an HLA string as the source parameter, you'll typically invoke str.zcopy as follows:
// Assumptions: hlaString is the name of an HLA String variable and // destZStr is the name of an array of characters or byte array. str.zcpy( val hlaString, destZStr ); // Use this call if destZStr is a pointer variable that points at // an array of bytes/characters: str.zcpy( val hlaString, val destZStr );Remember, untyped reference parameters always take the address of their actual parameter, even if the actual parameter is a pointer. You must use the VAL operator to tell HLA to use the value of the pointer, rather than the address of the pointer, when passing an HLA string (which is a pointer variable) to this function.
Of course, you can also use the str.zcpy function to copy one zero-terminated string to another. You'd typically use str.zcpy in this capacity to copy a string returned by one external function to a buffer for use by another external function that expects a zero-terminated string. Note that str.zcpy has approximately the same overhead as the str.cpyz function, so if you need to make a copy of a zero-terminated string that you're going to pass on to another function that uses zero-terminated strings and you might want to manipulate that string as an HLA string at some point, it's probably better to use the str.cpyz function.
27.9.5 Concatenating ZStrings
str.zcat( var zsrc:var; var dest:var );
This function concatenates one zero-terminated string to the end of another. The caller must ensure that the destination buffer is large enough to hold the resulting string; the str.zcat function has no way to verify the size of the destination buffer, so it cannot check for buffer overflow (this is typical for zero-terminated string functions).
This string is useful for manipulating zero-terminated strings some external code provides without the overhead of first converting the strings to HLA strings. If you call two external functions that return zero-terminated strings and you need to pass their concatenated result to some other external function that expects a zero-terminated string, and there is no string manipulation in your HLA code, then using str.zcat is more efficent than converting the strings to an HLA string and using the HLA string concation function.
When using this function, don't forget that it's parameters are untyped reference parameters. When passing the address of a buffer variable you may specify the name of the buffer directly. However, when passing a pointer to the buffer, you'll probably need to use the VAL operator to tell HLA to pass the pointer's value rather than the pointer's address. Here are some examples of str.zcat invocations:
static buffer1 :char[256]; buffer2 :char[254]; bufptr1 :pointer to char; bufptr2 :pointer to char; . . . str.zcat( buffer1, buffer2 ); str.zcat( val bufptr1, buffer1 ); str.zcat( buffer2, [edi] ); str.zcat( val bufptr2, val esi );You can also use the str.zcat procedure to copy data from an HLA string to a zero-terminated string. Don't forget that HLA string variables are actually pointers, so you have to use the VAL operator when passing an HLA string as the source operand to this function:
static hlaStr :string; zString :char[256]; zPtr :pointer to char; . . . str.zcat( val hlaStr, zString ); str.zcat( val hlaStr, val zptr ); str.zcat( val hlaStr, [edi] ); str.zcat( val hlaStr, val esi );It really does not make any sense to specify an HLA string variable as the destination operand. str.zcat does not update the HLA string's length field, so if you supply an HLA string as the destination operand, the str.zcat procedure may corrupt the HLA string, forcing you to manually compute the length yourself. If you need to copy a zero-terminated string to an HLA string, use the str.cpyz function instead.
27.9.6 Comparing ZStrings
str.zcmp( var zsrc1:var; var zsrc2:var ); @returns( "eax" );
The str.zcmp function compares two zero-terminated strings and returns the comparison results in the EAX register and in the x86 flags. This comparison function sets the condition code bits so you can use the standard unsigned condition instructions (jump and set instructions) immediately upon return to test for less than, less than or equal, equal, not equal, greater than, or greater than or equal. This function also returns -1 ($FFFF_FFFF), zero, or one in EAX to indicate less than, equal, or greater than (respectively). Note that this function compares zsrc1 to zsrc2. Therefore, this function returns -1 if zsrc1 < zrc2, zero if zsrc1 = zsrc2, and one if zsrc1 > zsrc2.
This function is especiially useful for comparing two zero-terminated strings that some external code returns to your HLA program if you don't need to do any further manipulation of the string data. This function is also useful for comparing an HLA string against a zero-terminated string (since HLA strings are zero terminated). Technically, you could use this function to compare two HLA strings (since they are zero-terminated), but the standard HLA string comparison functions are probably more efficient for this purpose.
Note that when supplying an HLA string variable as an operand to this function (or, in general, any pointer variable) you will need to use the VAL operator so that HLA passes the pointer to the string (rather than the address of the pointer to the string) as the parameter, e.g.:
static hlaString :string; strPtr :pointer to char; zstring :char[256]; . . . str.zcmp( val hlaString, zstring ); str.zcmp( zstring, val strPtr ); str.zcmp( zstring, val edi );1The "returns" values for these functions is the string "AL", however, they also zero out the H.O. three bytes of EAX.
2The "returns" values for these functions is the string "AL", however, they also zero out the H.O. three bytes of EAX.
3Many people mistakenly refer to this process as "parsing". Technically, parsing means "figuring out the meaning" rather than breaking up a string into certain components (like words).
![]() |
![]() |
![]() |
![]() |
![]() |