One of the advantages of PowerBASIC is its flexibility in programming
style. PowerBASIC has a very powerful set of string manipulation functions
but the operating system still mainly uses zero terminated strings so in
some instances, it is more economical to work in zero terminated strings.

In other instances, some people who are more familiar with this format
still like to work this way and of course, sometimes there is an advantage
in using this format.

Below is a test application that has a set of zero terminated string
functions written in assembler. The are not as easy to use as the basic
dynamic strings but they are useful in many instances. Where possible,
they have used DWORD size instructions to get the speed up to an
acceptable level.

Zero terminated strings have the disadvantage that they must be scanned to
get their length so an algorithm written by Agner Fog has been used
because of its speed advantage over classic byte scanners.

There is also an example of how to copy a numeric array using a DWORD
memory copy routine written in assembler. Rather than some messy looped
assignment process, this routine uses the direct address of the first
member of each array and copies the correct number of bytes from the
source address to the destination address.

For having written two other test pieces, one using 4 32 bit registers and
the second using 4 64 bit MMX registers, the "rep movsd" method has not
been improved on so the memory copy routine is in the smart end of the
market in terms of speed.

Note that you cannot use this technique on a dynamic string array as they
are not stored sequentially in memory.

A small piece of pure PowerBASIC for your pleasure.

Regards,

[email protected]

Code:
  ' #########################################################################
  
  #COMPILE EXE
  
  ' ----------------------------
  ' Function / Sub declarations
  ' ----------------------------
  
  DECLARE FUNCTION MessageBox LIB "user32.dll" ALIAS "MessageBoxA" _
                  (ByVal DWORD,ByVal DWORD,ByVal DWORD,ByVal DWORD) as LONG
  
  DECLARE FUNCTION szLen(ByVal lpszString as LONG) as LONG
  
  DECLARE SUB MemCopyD(ByVal src as LONG, _
                       ByVal dst as LONG, _
                       ByVal ln  as LONG)
  
  DECLARE SUB szCatStr(ByVal lpszSource as LONG, _
                       ByVal lpszAdd    as LONG)
  
  DECLARE SUB szLeft(ByVal lpszSource  as LONG, _
                     ByVal lpszTarget  as LONG, _
                     ByVal ln as LONG)
  
  DECLARE SUB szMid(ByVal lpszSource   as LONG, _
                    ByVal lpszTarget   as LONG, _
                    ByVal stPos        as LONG, _
                    ByVal ln           as LONG)
  
  DECLARE SUB szRight(ByVal lpszSource as LONG, _
                      ByVal lpszTarget  as LONG, _
                      ByVal ln as LONG)
  
  DECLARE SUB szRevStr(ByVal lpszSource as LONG,ByVal lpszTarget as LONG)
  
  ' #########################################################################
  
  FUNCTION WinMain(ByVal Instance      as LONG, _
                   ByVal hPrevInstance as LONG, _
                   lpszCmdLine         as ASCIIZ PTR, _
                   ByVal nCmdShow      as LONG) AS LONG
  
      LOCAL lpAr1      as LONG
      LOCAL lpAr2      as LONG
  
      LOCAL szText     as ASCIIZ * 64
      LOCAL szAdd      as ASCIIZ * 20
      LOCAL sztTl      as ASCIIZ * 16
      LOCAL szBuffer   as ASCIIZ * 64
      LOCAL szMidStr   as ASCIIZ * 8
      LOCAL szRightStr as ASCIIZ * 8
      LOCAL szExtra    as ASCIIZ * 128
  
      szText = "Hi there folks ! "
      szAdd  = "Message Box Test"
      sztTl  = "Message Box"
  
    ' --------------------
    ' sz string functions
    ' --------------------
      szLeft VarPtr(szAdd),VarPtr(szBuffer),11
      szCatStr VarPtr(szText),VarPtr(szBuffer)
  
      szMid VarPtr(szText),VarPtr(szMidStr),8,6
      szCatStr VarPtr(szText),VarPtr(szMidStr)
  
      szRight VarPtr(szAdd),VarPtr(szRightStr),5
      szCatStr VarPtr(szText),VarPtr(szRightStr)
  
      szRevStr VarPtr(szText),VarPtr(szExtra)
  
      MessageBox 0,VarPtr(szExtra),VarPtr(sztTl),0
  
    ' -----------
    ' Array copy
    ' -----------
      redim aLong&(1 to 10000)
      redim aNext&(1 to 10000)
  
      lpAr1 = VarPtr(aLong&(1))
      lpAr2 = VarPtr(aNext&(1))
  
      aLong&(5678) = 1234
  
      MemCopyD lpAr1,lpAr2,40000
  
      Result$ = str$(aNext&(5678))
  
      MessageBox 0,StrPtr(Result$),VarPtr(sztTl),0
  
      FUNCTION = 0
  
  END FUNCTION
  
  ' #########################################################################
  
  SUB szCatStr(ByVal lpszSource as LONG, ByVal lpszAdd as LONG)
  
      #REGISTER NONE
  
      LOCAL ln1 as DWORD
  
      ln1 = szLen(lpszSource)
  
      ! mov esi, lpszAdd
      ! mov edi, lpszSource
      ! add edi, ln1
  
    csSt:
      ! mov al, [esi]
      ! inc esi
      ! mov [edi], al
      ! inc edi
      ! test al, al       ; test for zero
      ! jne csSt
  
  END SUB
  
  ' ###########################################################################
  
  FUNCTION szLen(ByVal lpszString as LONG) as LONG
  
      #REGISTER NONE
  
      LOCAL retval as LONG
  
      ! mov     eax,lpszString         ; get pointer to string
      ! lea     edx,[eax+3]            ; pointer+3 used in the end
    lnSt:     
      ! mov     ebx,[eax]              ; read first 4 bytes
      ! add     eax,4                  ; increment pointer
      ! lea     ecx,[ebx-&H01010101]   ; subtract 1 from each byte
      ! not     ebx                    ; invert all bytes
      ! and     ecx,ebx                ; and these two
      ! and     ecx,&H80808080
      ! jz      lnSt                   ; no zero bytes, continue loop
      ! test    ecx,&H00008080         ; test first two bytes
      ! jnz     lnOut
      ! shr     ecx,16                 ; not in the first 2 bytes
      ! add     eax,2
    lnOut:     
      ! shl     cl,1                   ; use carry flag to avoid branch
      ! sbb     eax,edx                ; compute length
  
      ! mov FUNCTION, eax
      
  END FUNCTION
  
  ' ###########################################################################
  
  SUB MemCopyD(ByVal src as LONG, _
               ByVal dst as LONG, _
               ByVal ln as LONG)
  
      #REGISTER NONE
  
        ! cld
  
        ! mov esi, src
        ! mov edi, dst
        ! mov ecx, ln
  
        ! shr ecx, 2
        ! rep movsd
  
        ! mov ecx, ln
        ! and ecx, 3
        ! rep movsb
  
  END SUB
  
  ' ###########################################################################
  
  SUB szLeft(ByVal lpszSource  as LONG, _
             ByVal lpszTarget  as LONG, _
             ByVal ln as LONG)
  
      #REGISTER NONE
  
      ! cld
  
      ! mov esi, lpszSource
      ! mov edi, lpszTarget
      ! mov ecx, ln
  
      ! shr ecx, 2
      ! rep movsd
  
      ! mov ecx, ln
      ! and ecx, 3
      ! rep movsb
  
      ! mov al, 0
      ! mov [edi], al
  
  END SUB
  
  ' ###########################################################################
  
  SUB szMid(ByVal lpszSource  as LONG, _
            ByVal lpszTarget  as LONG, _
            ByVal stPos       as LONG, _
            ByVal ln          as LONG)
  
      #REGISTER NONE
  
      ! cld
  
      ! mov esi, lpszSource
      ! mov edi, lpszTarget
      ! add esi, stPos
      ! mov ecx, ln
  
      ! shr ecx, 2
      ! rep movsd
  
      ! mov ecx, ln
      ! and ecx, 3
      ! rep movsb
  
      ! mov al, 0
      ! mov [edi], al
  
  END SUB
  
  ' ###########################################################################
  
  SUB szRight(ByVal lpszSource  as LONG, _
              ByVal lpszTarget  as LONG, _
              ByVal ln as LONG)
  
      #REGISTER NONE
  
      LOCAL ln1 as DWORD
  
      ln1 = szLen(lpszSource)
  
      ! cld
  
      ! mov esi, lpszSource
      ! add esi, ln1
      ! sub esi, ln
      ! mov edi, lpszTarget
      ! mov ecx, ln
  
      ! shr ecx, 2
      ! rep movsd
  
      ! mov ecx, ln
      ! and ecx, 3
      ! rep movsb
  
      ! mov al, 0
      ! mov [edi], al
  
  END SUB
  
  ' ###########################################################################
  
  SUB szRevStr(ByVal lpszSource as LONG,ByVal lpszTarget as LONG)
  
      #REGISTER NONE
  
      LOCAL ln as LONG
  
      ln = szLen(lpszSource)
  
      ! mov esi, lpszSource
      ! mov edx, esi        ; put in edx for comparison
      ! dec edx
      ! add esi, ln         ; add len to source address
      ! dec esi
      ! mov edi, lpszTarget
  
    rsSt:
      ! mov al, [esi]
      ! dec esi
      ! mov [edi], al
      ! inc edi
      ! cmp esi, edx
      ! jne rsSt
  
      ! mov al, 0
      ! mov [edi], al       ; write terminator
  
  END SUB
  
  ' ###########################################################################