' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
'                                        Build with PBCC 6.0
' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
 
    #include "\basic\include\win32api.inc"
 
    ' %clockit = 0  ' uncomment to run timing.
 
' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
 
FUNCTION PBmain as LONG
 
    #REGISTER NONE
 
    LOCAL src  as DWORD
    LOCAL lcnt as DWORD
    LOCAL pMem as DWORD
    LOCAL tcnt as DWORD
 
    a$ = _
        "Friends, Romans, countrymen, lend me your ears;"+chr$(13,10)+_
        "I come to bury Caesar, not to praise him."+chr$(13,10)+_
        "The evil that men do lives after them;"+chr$(13,10)+_
        "The good is oft interred with their bones;"+chr$(13,10)
 
  #IF %DEF(%clockit)
    Open "lt.txt" for Binary as #1  ' VERY BIG file of your choice
      Get$ #1, lof(1), a$
    Close #1
    tcnt = GetTickCount
  #ENDIF
 
    src = StrPtr(a$)
 
    PREFIX "! "
    push src
    call lines_to_array             ; call the tokenizer
    mov pMem, eax                   ; returned array pointer address in variable
    mov lcnt, ecx                   ; line count in variable
    END PREFIX
 
  #IF %DEF(%clockit)
    tcnt = GetTickCount - tcnt
    StdOut "Timing = "+format$(tcnt)+" milliseconds"
    ! jmp bye
  #ENDIF
 
    PREFIX "! "
    mov esi, pMem                   ; load pointer array into ESI
    mov edi, lcnt                   ; load the line count into EDI
  lbl1:
    push DWORD PTR [esi]            ; write the line of text to the console
    call puts                       ; make MSVCRT useful
    add esi, 4                      ; increment to next pointer
    sub edi, 1                      ; decrement the loop counter
    jnz lbl1
 
    END PREFIX
 
  bye:
    GlobalFree pMem                 ' release pointer memory here
    waitkey$
 
End FUNCTION
 
' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
 
FUNCTION lines_to_array(ByVal src as DWORD) as DWORD
 
  ' --------------------------------------------------------------------------
  ' in place tokenize a text file and return an array of pointers to the start
  ' of each line which is terminated at the ascii 13 location with an ascii 0
 
  ' IN     address of source text
  ' OUT 1. array of pointers address in EAX
  '     2. line count in ECX
 
  ' address of pointer array must be released in the scope of the caller
  ' using GlobalFree()
  ' --------------------------------------------------------------------------
 
    #REGISTER NONE
 
    LOCAL lcnt as DWORD
    LOCAL pMem as DWORD
    LOCAL alen as DWORD
 
    PREFIX "! "
 
    push src
    call get_lcnt           ; get the line count
    mov lcnt, eax           ; store line count in variable
    lea eax, [eax*4]        ; set pointer array length
    mov alen, eax           ; store the array size in alen
 
  ; --------------------------
  ; allocate the pointer array
  ; --------------------------
    END PREFIX
    pMem = GlobalAlloc(%GMEM_FIXED or %GMEM_ZEROINIT,alen)
    PREFIX "! "
 
    mov esi, src            ; source address in ESI
    mov ebx, pMem           ; pointer array address in EBX
 
    mov [ebx], esi          ; load array address into 1st member of array
    add ebx, 4
    sub esi, 1
 
  lbl1:
    add esi, 1
    movzx eax, BYTE PTR [esi]
    test eax, eax           ; test for zero
    jz lbl2                 ; exit loop on zero
    cmp eax, 13             ; test for ascii 13
    jne lbl1                ; short loop back if not 13
 
    mov BYTE PTR [esi], 0   ; write terminator at ascii 13 location
    add esi, 2              ; step over ascii 13 and 10
    mov [ebx], esi          ; write the next line start to pointer
    add ebx, 4              ; increment to next pointer
    jmp lbl1                ; long loop after writing pointer
 
  lbl2:
    mov ecx, lcnt           ; return the line count in ECX
    mov eax, pMem           ; the array pointer in EAX
    mov FUNCTION, eax
 
    END PREFIX
 
End FUNCTION
 
' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
 
FASTPROC get_lcnt
 
    PREFIX "! "
 
  ; --------------------------------------
  ; count ascii 13 to determine line count
  ; --------------------------------------
    mov edx, [esp+4]                    ; the source address
    sub edx, 1
    xor eax, eax
    jmp lbl1
 
  pre:
    add eax, 1                          ; increment the counter
  lbl1:
  ; -----------
  ; unroll by 4
  ; -----------
    add edx, 1
    movzx ecx, BYTE PTR [edx]
    cmp ecx, 13
    je pre
    test ecx, ecx
    jz lbl2
 
    add edx, 1
    movzx ecx, BYTE PTR [edx]
    cmp ecx, 13
    je pre
    test ecx, ecx
    jz lbl2
 
    add edx, 1
    movzx ecx, BYTE PTR [edx]
    cmp ecx, 13
    je pre
    test ecx, ecx
    jz lbl2
 
    add edx, 1
    movzx ecx, BYTE PTR [edx]
    cmp ecx, 13
    je pre
    test ecx, ecx
    jnz lbl1
 
  lbl2:
    ret 4
 
    END PREFIX
 
END FASTPROC
 
' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
 
    DECLARE FUNCTION vcputs CDECL LIB "MSVCRT.DLL" ALIAS "puts" (BYVAL ptxt AS DWORD) AS DWORD
 
' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
 
SUB puts(BYVAL ptxt AS DWORD)
 
  vcputs ptxt
 
End SUB
 
' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤