This technique is an alternative when converting assembler algorithms from MASM or using the assembler output from a C compiler where there is no stack frame for the procedure.
Converting bare mnemonic code to PB notation is relatively simple but manually editing a no stack frame procedure is not for the faint of heart and is very error prone.
For the extra work of putting the code inside a normal basic SUB and having to make a call to the sub to obtain the procedure start addresses you get completely uncompromised assembler code performance and can routinely use code built in an entirely different compiler, in this case VC/VS C compiler output.
The new #ALIGN directive removes the last advantage that a C compiler ever had over PB.
Note that if you need to write mixed API/high level code and assembler you will produce far more reliable code if you use the normal stack frame SUB or FUNCTION but if you have pure mnemonic code from another source this technique allows you to use it with nothing more than a notation conversion.
Converting bare mnemonic code to PB notation is relatively simple but manually editing a no stack frame procedure is not for the faint of heart and is very error prone.
For the extra work of putting the code inside a normal basic SUB and having to make a call to the sub to obtain the procedure start addresses you get completely uncompromised assembler code performance and can routinely use code built in an entirely different compiler, in this case VC/VS C compiler output.
The new #ALIGN directive removes the last advantage that a C compiler ever had over PB.
Note that if you need to write mixed API/high level code and assembler you will produce far more reliable code if you use the normal stack frame SUB or FUNCTION but if you have pure mnemonic code from another source this technique allows you to use it with nothing more than a notation conversion.
Code:
#IF 0 ' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ 8:02:22 PM Wednesday, November 18, 2009 Build this test piece with PBWIN90 #ENDIF ' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ FUNCTION PBmain as LONG #REGISTER NONE LOCAL text as ASCIIZ * 64 LOCAL ptxt as DWORD LOCAL var as DWORD text = "This is a test" ptxt = VarPtr(text) init_asm_len_procs ' get the asm procedure addresses szLen(ptxt) ' call the szLen function ! mov var, eax ' write EAX to DWORD variable msgbox str$(var) ! mov eax, ptxt ' copy ptxt variable to EAX szStrlen(eax) ' call szStrlen using EAX register as argument ! mov var, eax msgbox str$(var) FUNCTION = 0 End FUNCTION ' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ #IF NOT %DEF(%proc_init_asm_len) %proc_init_asm_len = 1 GLOBAL pszLen as DWORD GLOBAL pstrlen as DWORD MACRO szLen(pstr) ! push pstr ! call pszLen END MACRO MACRO szStrlen(pstr) ! push pstr ! call pstrlen END MACRO ' --------------------------- SUB init_asm_len_procs() #REGISTER NONE pszLen = CodePtr(asm_szlen) pstrlen = CodePtr(asm_StrLen) Exit SUB ' ***************************************************************** ' simple string length algo unrolled by 4 ' ***************************************************************** #align 16 asm_szlen: ! mov eax, [esp+4] ! sub eax, 4 lb0: ! add eax, 4 ! cmp BYTE PTR [eax], 0 ! je lb1 ! cmp BYTE PTR [eax+1], 0 ! je lb2 ! cmp BYTE PTR [eax+2], 0 ! je lb3 ! cmp BYTE PTR [eax+3], 0 ! jne lb0 ! sub eax, [esp+4] ! add eax, 3 ! ret 4 lb3: ! sub eax, [esp+4] ! add eax, 2 ! ret 4 lb2: ! sub eax, [esp+4] ! add eax, 1 ! ret 4 lb1: ! sub eax, [esp+4] ! ret 4 ' ***************************************************************** ' DWORD string length algo unrolled by 4 ' ***************************************************************** #align 16 asm_StrLen: ! mov eax, [esp+4] ; get pointer to string ! lea edx, [eax+3] ; pointer+3 used in the end ! push ebp ! push edi ! mov ebp, &H80808080 slbl0: ! mov edi, [eax] ; read first 4 bytes ! add eax, 4 ; increment pointer ! lea ecx, [edi-&H01010101] ; subtract 1 from each byte ! not edi ; invert all bytes ! and ecx, edi ; and these two ! and ecx, ebp ! jnz slbl1 ! mov edi, [eax] ; read first 4 bytes ! add eax, 4 ; increment pointer ! lea ecx, [edi-&H01010101] ; subtract 1 from each byte ! not edi ; invert all bytes ! and ecx, edi ; and these two ! and ecx, ebp ! jnz slbl1 ! mov edi, [eax] ; read first 4 bytes ! add eax, 4 ; increment pointer ! lea ecx, [edi-&H01010101] ; subtract 1 from each byte ! not edi ; invert all bytes ! and ecx, edi ; and these two ! and ecx, ebp ! jnz slbl1 ! mov edi, [eax] ; read first 4 bytes ! add eax, 4 ; 4 increment DWORD pointer ! lea ecx, [edi-&H01010101] ; subtract 1 from each byte ! not edi ; invert all bytes ! and ecx, edi ; and these two ! and ecx, ebp ! jz slbl0 ; no zero bytes, continue loop slbl1: ! test ecx, &H00008080 ; test first two bytes ! jnz slbl2 ! shr ecx, 16 ; not in the first 2 bytes ! add eax, 2 slbl2: ! shl cl, 1 ; use carry flag to avoid branch ! sbb eax, edx ; compute length ! pop edi ! pop ebp ! ret 4 ' ***************************************************************** End SUB #ENDIF ' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤