Tutorial 01: StrLib Basics

0
#
37
27.12.25 15:32

Tutorial 01: StrLib Basics

Overview

This tutorial covers the fundamental operations of FreshLib's string library (StrLib). You'll learn how to create, manipulate, compare, and search strings using FreshLib's handle-based string system.

Topics Covered

  1. String Structure - Understanding how FreshLib stores strings

  2. Handles vs Pointers - Why FreshLib uses handles

  3. String Creation - Creating new strings

  4. String Duplication - Copying strings

  5. String Pointers - Converting handles to pointers

  6. String Deletion - Proper cleanup

  7. String Concatenation - Joining strings together

  8. String Length - Getting string size

  9. String Comparison - Comparing strings (case-sensitive/insensitive)

  10. String Searching - Finding characters and substrings

Prerequisites

  • Basic x86 assembly knowledge

  • Understanding of registers (EAX, EBX, ECX, EDX, ESI, EDI)

  • Stack frame concepts (push, pop, call, ret)

The FreshLib String Structure

FreshLib strings are more than just character arrays. Each string has:

struc string {
  .capacity dd ?     ; Total allocated memory (offset -8)
  .len      dd ?     ; Current string length in bytes (offset -4)
  label .data byte   ; The actual string data (offset 0)
}

Key points:

  • Strings are null-terminated (like C strings)

  • Strings are dword-aligned for efficient processing

  • Capacity can be greater than length (room for growth)

Handles vs Pointers

FreshLib uses a handle system for strings:

Handles (Integer Identifiers)

  • Stored in the string table

  • Remain constant during string's lifetime

  • Used with most StrLib functions

  • Example: stdcall StrCat, hString, source

Pointers (Memory Addresses)

  • Direct memory addresses

  • Can change during string operations

  • Required for C library functions

  • Obtained via stdcall StrPtr, hString

Why Handles?

; WITHOUT handles (dangerous):
mov     eax, string_pointer
stdcall StrCat, eax, " more text"   ; eax may be invalid now!

; WITH handles (safe):
stdcall StrNew                       ; Returns handle in EAX
stdcall StrCat, eax, "text"          ; Handle remains valid
stdcall StrCat, eax, " more text"    ; Still valid!

Functions in This Tutorial

Initialization and Cleanup

Function Purpose
InitStrings Initialize string library (automatic in FreshLib)
FreeStrings Free all strings (automatic in FreshLib)

String Creation

Function Purpose
StrNew Create new empty string
StrDup Duplicate a string (copy)
StrDupMem Duplicate from memory pointer

String Access

Function Purpose
StrPtr Get pointer from handle
StrLen Get string length
StrDel Delete/free a string

String Manipulation

Function Purpose
StrCat Append string to destination
StrCharCat Append up to 4 characters
StrCopy Copy source to destination

String Comparison

Function Purpose Return
StrCompCase Case-sensitive compare CF=1 if equal
StrCompNoCase Case-insensitive compare CF=1 if equal

String Search

Function Purpose
StrCharPos Find character in string
StrPos Find substring in string

Demo Programs

Demo 01: String Creation

File: demo01_string_create.asm

Demonstrates:

  • Creating new empty strings with StrNew

  • Duplicating strings with StrDup

  • Duplicating from memory with StrDupMem

  • Getting pointers with StrPtr

  • Deleting strings with StrDel

    Key Concepts:

  • Always pair StrNew/StrDup with StrDel

  • Use StrPtr to get pointers for C functions

  • Check return values in EAX

Demo 02: String Concatenation

File: demo02_string_concat.asm

Demonstrates:

  • Concatenating strings with StrCat

  • Appending characters with StrCharCat

  • Copying strings with StrCopy

  • Getting string length with StrLen

    Key Concepts:

  • StrCat modifies destination in place

  • Destination MUST be a handle (not pointer)

  • Use txt macro for string literals

Demo 03: String Comparison

File: demo03_string_compare.asm

Demonstrates:

  • Case-sensitive comparison with StrCompCase

  • Case-insensitive comparison with StrCompNoCase

  • Using Carry Flag (CF) for comparison results

  • Proper error handling with jc/jnc

    Key Concepts:

  • CF=1 means equal/TRUE for comparisons

  • CF=0 means not equal/FALSE for comparisons

  • Use jc (jump if carry) to check for equal

  • Use jnc (jump if no carry) to check for not equal

Demo 04: String Search

File: demo04_string_search.asm

Demonstrates:

  • Finding characters with StrCharPos

  • Finding substrings with StrPos

  • Handling NULL results (not found)

    Key Concepts:

  • Return NULL (0) if not found

  • Check EAX before using result

  • Pointers vs handles in search results

Common Patterns

Creating and Using a String

stdcall StrNew                 ; Create new string
mov     ebx, eax               ; Save handle

stdcall StrCat, ebx, txt "Hello"   ; Append text
stdcall StrPtr, ebx           ; Get pointer for C functions

; ... use string ...

stdcall StrDel, ebx            ; Clean up

Error Handling

stdcall SomeFunction, param
jc      .error_handler         ; Jump if CF=1 (error)
; Continue on success (CF=0)

String Comparison

stdcall StrCompCase, str1, str2
jc      .strings_equal         ; CF=1 means equal

; Strings are different

Saving NumToStr Results (FileWriteString Clobbers EAX)

IMPORTANT: FileWriteString clobbers the EAX register! When using NumToStr, you must save the result before calling FileWriteString with other strings.

; WRONG - memory leak!
stdcall NumToStr, eax, ntsDec or ntsUnsigned
stdcall FileWriteString, [STDOUT], "Length: "
stdcall FileWriteString, [STDOUT], eax    ; eax is clobbered!
; Memory leaked!

; CORRECT - save and restore
stdcall NumToStr, eax, ntsDec or ntsUnsigned
push    eax                              ; Save NumToStr result
stdcall FileWriteString, [STDOUT], "Length: "
pop     eax                              ; Restore NumToStr result
stdcall FileWriteString, [STDOUT], eax
stdcall StrDel, eax                      ; Clean up NumToStr result

Why this matters:

  • NumToStr creates a new string handle that must be freed

  • FileWriteString overwrites EAX with its own return value

  • Forgetting to delete the handle causes a memory leak

    Alternative (using a variable):

    stdcall NumToStr, eax, ntsDec or ntsUnsigned
    mov     [hResult], eax                   ; Save in variable
    stdcall FileWriteString, [STDOUT], "Length: "
    mov     eax, [hResult]                   ; Restore from variable
    stdcall FileWriteString, [STDOUT], eax
    stdcall StrDel, [hResult]                ; Clean up
    

Important Discoveries During Implementation

1. FileWriteString Clobbers EAX

Problem: When using NumToStr followed by FileWriteString, the EAX register (containing the string handle) gets overwritten, causing memory leaks and segmentation faults.

Discovery: FileWriteString modifies the EAX register as part of its normal operation. This means any value stored in EAX before calling FileWriteString will be lost.

Solution: Always save NumToStr results before calling FileWriteString with other strings:

  • Use push eax / pop eax for temporary saves

  • Use mov [variable], eax for longer-term storage

  • Always call StrDel on the saved handle to prevent memory leaks

    Impact: This pattern affects ALL demos that display numbers. Failing to follow this pattern causes:

  • Memory leaks (string handles never freed)

  • Segmentation faults (trying to use garbage data as string handles)

2. Memory Management is Manual

Discovery: Every string created by NumToStr, StrNew, StrDup, StrDupMem, or StrExtract must be explicitly freed with StrDel.

Common Mistakes:

  • Forgetting to delete NumToStr results (especially when they're temporary)

  • Creating strings in error paths and not cleaning them up

  • Overwriting handle variables before deleting the old handle

    Best Practice: Track every string creation and ensure there's a matching StrDel call in all code paths (including error handlers).

3. Carry Flag Usage is Inverted for Comparisons

Discovery: String comparison functions use CF=1 for "equal" (TRUE), which is opposite to most error-checking functions.

Two Different Conventions:

  • Error checking: CF=1 means error, CF=0 means success

    • Example: stdcall StrNew followed by jc .error

  • Comparisons: CF=1 means equal/TRUE, CF=0 means not equal/FALSE

    • Example: stdcall StrCompCase, s1, s2 followed by jc .equal

      Impact: Using the wrong jump instruction leads to inverted logic.

4. Angle Bracket Syntax is for Constants Only

Discovery: The < > syntax for inline strings only works with compile-time constants, not runtime register values.

Valid:

stdcall FileWriteString, [STDOUT], <"Error!", 13, 10>

Invalid:

stdcall FileWriteString, [STDOUT], <"Position: ", eax, 13, 10>  ; WRONG!

Reason: The < > syntax is a preprocessor macro that expands at compile time. Register values only exist at runtime.

5. String Constants Require the text Macro

Discovery: String constants must use the text macro in the iglobal section to ensure proper null termination and alignment.

Correct:

iglobal
  cHello text "Hello World"
endg

Wrong (missing null terminator):

iglobal
  cHello db "Hello World"
endg

6. Build Order Matters for Dependencies

Discovery: When building demos, the runtime loader ld-musl-i386.so must be present in the same directory as the executable.

Solution: The build script copies the runtime from a reference location before building demos.

Building and Running

cd 01-strlib-basics
./build.sh

This will compile and test all 4 demos.

Next Steps

After completing this tutorial, continue to:

  • Tutorial 02: String Manipulation (insert, extract, case conversion)

  • Tutorial 03: Advanced Features (numbers, encoding, patterns)

Reference

  • FreshLib Source: ~/Documents/fossil/FreshIDE/freshlib/data/strlib.asm

  • AsmBB Usage: ~/Documents/fossil/asmbb/source/*.asm

35
27.12.25 15:35
; _______________________________________________________________________________________
;| FreshLib String Processing Tutorial: Demo 01 - String Creation and Deletion
;|_______________________________________________________________________________________|
;  Description: Demonstrates creating, duplicating, and deleting strings in FreshLib
;  Topics: String creation, duplication, pointer access, cleanup
;  Prerequisites: Basic x86 assembly, register knowledge
;_________________________________________________________________________________________

include "%lib%/freshlib.inc"

LINUX_INTERPRETER equ './ld-musl-i386.so'

@BinaryType console, compact
LIB_MODE equ NOGUI

options.DebugMode = 1

include "%lib%/freshlib.asm"

; ========================================
; FUNCTION REFERENCE
; ========================================
;
; InitializeAll
;   Description: Initialize FreshLib framework (called at program start)
;   Arguments:   None
;   Returns:     None
;   Note:        Must be paired with FinalizeAll at program end
;
; StrNew
;   Description: Creates a new empty string handle
;   Arguments:   None
;   Returns:     EAX = string handle (0 on error)
;   CF:          0 = success, 1 = error
;
; StrDup
;   Description: Creates a duplicate of an existing string
;   Arguments:   .hString = source string handle
;   Returns:     EAX = new string handle (0 on error)
;   CF:          0 = success, 1 = error
;
; StrDupMem
;   Description: Creates a string from a memory pointer
;   Arguments:   .pointer = memory address of string data
;   Returns:     EAX = string handle (0 on error)
;   CF:          0 = success, 1 = error
;
; StrCat
;   Description: Appends source string to destination string
;   Arguments:   .dest    = destination string handle (MUST be a handle)
;               .source  = source string handle or memory pointer
;   Returns:     EAX = destination handle (unchanged)
;   CF:          0 = success, 1 = error
;   Note:        Destination string is modified in place
;
; StrPtr
;   Description: Converts string handle to memory pointer
;   Arguments:   .hString = string handle
;   Returns:     EAX = pointer to string data (NULL if not found)
;   Note:        Use this for C library functions that need pointers
;
; StrLen
;   Description: Gets string length in bytes
;   Arguments:   .hString = string handle or pointer
;   Returns:     EAX = length in bytes
;
; StrDel
;   Description: Frees a string handle and its memory
;   Arguments:   .hString = string handle to delete
;   Returns:     None
;   Side Effect: Preserves FLAGS register (unusual!)
;   Note:        Always pair with StrNew/StrDup/StrDupMem
;
; NumToStr
;   Description: Converts number to decimal string
;   Arguments:   .number  = number to convert
;               .flags   = conversion flags (ntsDec, ntsUnsigned, etc.)
;   Returns:     EAX = string handle containing number
;   Note:        Caller must free the result with StrDel
;
; FileWriteString
;   Description: Writes string to standard output
;   Arguments:   .file    = file handle (STDOUT for console)
;               .string  = string handle or pointer
;   Returns:     None
;   WARNING:     Clobbers EAX register! Save important values first.
;
; FinalizeAll
;   Description: Cleanup FreshLib framework before exit
;   Arguments:   None
;   Returns:     None
;
; TerminateAll
;   Description: Exit program with return code
;   Arguments:   .exitcode = program exit code (0 = success)
;   Returns:     Does not return
;
; ========================================

; ========================================
; Global Variables
; ========================================
uglobal
  hString1       dd ?
  hString2       dd ?
  hString3       dd ?
  hString4       dd ?
endg

; ========================================
; Constants
; ========================================
iglobal
  cCRLF       text 13, 10

  cLabel1     text "Demo 1: StrNew - Create empty string", 13, 10
  cLabel2     text "Demo 2: StrDup - Duplicate string", 13, 10
  cLabel3     text "Demo 3: StrDupMem - Duplicate from memory", 13, 10
  cLabel4     text "Demo 4: StrPtr and StrLen", 13, 10

  cOriginal   text "Hello, FreshLib!"
  cEmpty      text "(empty string)", 13, 10
  cError      text "Error occurred!", 13, 10
  cDone       text 13, 10, "Demo 01 complete!", 13, 10
endg

; ========================================
; Entry Point
; ========================================
start:
        InitializeAll

        stdcall FileWriteString, [STDOUT], cLabel1

        stdcall StrNew
        jc      .error
        mov     [hString1], eax

        stdcall StrLen, eax
        test    eax, eax
        jnz      .not_empty

        stdcall StrCat, [hString1], cOriginal
        stdcall StrPtr, [hString1], eax
        stdcall FileWriteString, [STDOUT], eax

        jmp      .demo2

.not_empty:
        stdcall FileWriteString, [STDOUT], cEmpty

.demo2:
        stdcall FileWriteString, [STDOUT], cLabel2

        stdcall StrDup, [hString1]
        jc      .error
        mov     [hString2], eax

        stdcall StrCat, [hString1], ' (modified)'
        stdcall StrCat, [hString2], ' (copy)'

        stdcall StrPtr, [hString1], eax
        stdcall FileWriteString, [STDOUT], eax

        stdcall StrPtr, [hString2], eax
        stdcall FileWriteString, [STDOUT], eax

        stdcall FileWriteString, [STDOUT], cCRLF

.demo3:
        stdcall StrNew
        jc      .error
        mov     [hString3], eax

        stdcall StrDupMem, cOriginal
        jc      .error
        mov     [hString4], eax

        stdcall FileWriteString, [STDOUT], cLabel3

        stdcall StrPtr, [hString4], eax
        stdcall FileWriteString, [STDOUT], eax

        stdcall FileWriteString, [STDOUT], cCRLF

.demo4:
        stdcall FileWriteString, [STDOUT], cLabel4

        stdcall StrLen, [hString4]           ; Get string length
        push    eax                          ; Save length for later

        stdcall StrPtr, [hString4], eax     ; Get pointer to string data
        push    eax                          ; Save pointer for later

        stdcall FileWriteString, [STDOUT], 'String: '
        pop     eax                          ; Restore string pointer
        stdcall FileWriteString, [STDOUT], eax
        stdcall FileWriteString, [STDOUT], ' Length: '
        pop     eax                          ; Restore length
        push    eax                          ; Save length again
        stdcall NumToStr, eax, ntsDec or ntsUnsigned
        push    eax                          ; Save NumToStr result
        stdcall FileWriteString, [STDOUT], eax
        pop     eax                          ; Get NumToStr result
        stdcall StrDel, eax                  ; Clean up NumToStr result

.cleanup:
        stdcall StrDel, [hString1]
        stdcall StrDel, [hString2]
        stdcall StrDel, [hString3]
        stdcall StrDel, [hString4]

        stdcall FileWriteString, [STDOUT], cDone

.finish:
        FinalizeAll
        stdcall TerminateAll, 0

.error:
        stdcall FileWriteString, [STDOUT], cError
        jmp     .cleanup
33
27.12.25 15:36
; _______________________________________________________________________________________
;| FreshLib String Processing Tutorial: Demo 02 - String Concatenation
;|_______________________________________________________________________________________|
;  Description: Demonstrates string concatenation, copying, and length operations
;  Topics: StrCat, StrCharCat, StrCopy, StrLen
;  Prerequisites: Demo 01
;_________________________________________________________________________________________

include "%lib%/freshlib.inc"

LINUX_INTERPRETER equ './ld-musl-i386.so'

@BinaryType console, compact
LIB_MODE equ NOGUI

options.DebugMode = 0

include "%lib%/freshlib.asm"

; ========================================
; FUNCTION REFERENCE
; ========================================
;
; StrCharCat
;   Description: Appends 1-4 characters to a string
;   Arguments:   .hString = destination string handle
;               .char1   = first character (or $0A0D for CRLF)
;               .char2   = second character (optional)
;               .char3   = third character (optional)
;               .char4   = fourth character (optional)
;   Returns:     EAX = destination handle (unchanged)
;   CF:          0 = success, 1 = error
;   Note:        Use $0A0D (hex) for CRLF (13, 10)
;
; StrCopy
;   Description: Copies source string to destination (replaces content)
;   Arguments:   .dest    = destination string handle
;               .source  = source string handle
;   Returns:     EAX = destination handle (unchanged)
;   CF:          0 = success, 1 = error
;   Note:        Destination is modified, source is unchanged
;
; See demo01 for: InitializeAll, FinalizeAll, StrNew, StrDup, StrDupMem,
;               StrCat, StrPtr, StrLen, StrDel, NumToStr, FileWriteString
;
; ========================================

; ========================================
; Global Variables
; ========================================
uglobal
  hHello         dd ?
  hWorld         dd ?
  hResult        dd ?
endg

; ========================================
; Constants
; ========================================
iglobal
  cTitle     text "=== Demo 02: String Concatenation ===", 13, 10
  cHello     text "Hello"
  cWorld     text " World"
  cCRLF      text 13, 10

  cLabel1    text "1. StrCat: "
  cLabel2    text "2. StrCharCat: "
  cLabel3    text "3. StrCopy: "
  cLabel4    text "4. StrLen:", 13, 10
  cError     text "Error!", 13, 10
  cDone      text 13, 10, "Demo 02 complete!", 13, 10
endg

; ========================================
; Entry Point
; ========================================
start:
        InitializeAll

        stdcall FileWriteString, [STDOUT], cTitle

        stdcall StrDupMem, cHello
        jc      .error
        mov     [hHello], eax

        stdcall StrDupMem, cWorld
        jc      .error
        mov     [hWorld], eax

        stdcall FileWriteString, [STDOUT], cLabel1
        stdcall StrCat, [hHello], [hWorld]
        jc      .error

        stdcall StrPtr, [hHello], eax
        stdcall FileWriteString, [STDOUT], eax
        stdcall FileWriteString, [STDOUT], cCRLF

        stdcall FileWriteString, [STDOUT], cLabel2

        stdcall StrCharCat, [hHello], '!'
        jc      .error

        stdcall StrCharCat, [hHello], $0A0D
        jc      .error

        stdcall StrPtr, [hHello], eax
        stdcall FileWriteString, [STDOUT], eax

        stdcall FileWriteString, [STDOUT], cLabel3

        stdcall StrNew
        jc      .error
        mov     [hResult], eax

        stdcall StrCopy, [hResult], [hHello]
        jc      .error

        stdcall StrPtr, [hResult], eax
        stdcall FileWriteString, [STDOUT], eax
        stdcall FileWriteString, [STDOUT], cCRLF

        stdcall FileWriteString, [STDOUT], cLabel4

        stdcall StrLen, [hHello]
        push    eax                     ; Save length value

        stdcall FileWriteString, [STDOUT], '  hHello length: '
        pop     eax                     ; Restore length value
        stdcall NumToStr, eax, ntsDec or ntsUnsigned
        push    eax                     ; Save NumToStr result handle
        stdcall FileWriteString, [STDOUT], eax
        pop     eax                     ; Restore NumToStr result handle
        stdcall FileWriteString, [STDOUT], cCRLF
        stdcall StrDel, eax             ; Clean up NumToStr result

.cleanup:
        stdcall StrDel, [hHello]
        stdcall StrDel, [hWorld]
        stdcall StrDel, [hResult]

        stdcall FileWriteString, [STDOUT], cDone

.finish:
        FinalizeAll
        stdcall TerminateAll, 0

.error:
        stdcall FileWriteString, [STDOUT], cError
        jmp     .cleanup
32
27.12.25 15:37
; _______________________________________________________________________________________
;| FreshLib String Processing Tutorial: Demo 03 - String Comparison
;|_______________________________________________________________________________________|
;  Description: Demonstrates case-sensitive and case-insensitive string comparison
;  Topics: StrCompCase, StrCompNoCase, Carry Flag usage
;  Prerequisites: Demo 01
;_________________________________________________________________________________________

include "%lib%/freshlib.inc"

LINUX_INTERPRETER equ './ld-musl-i386.so'

@BinaryType console, compact
LIB_MODE equ NOGUI

options.DebugMode = 0

include "%lib%/freshlib.asm"

; ========================================
; FUNCTION REFERENCE
; ========================================
;
; StrCompCase
;   Description: Compares two strings with case sensitivity
;   Arguments:   .str1    = first string handle or pointer
;               .str2    = second string handle or pointer
;   Returns:     None (uses Carry Flag)
;   CF:          1 = strings are EQUAL, 0 = strings are NOT EQUAL
;   Note:        Comparison is case-sensitive ('A' != 'a')
;
; StrCompNoCase
;   Description: Compares two strings without case sensitivity
;   Arguments:   .str1    = first string handle or pointer
;               .str2    = second string handle or pointer
;   Returns:     None (uses Carry Flag)
;   CF:          1 = strings are EQUAL, 0 = strings are NOT EQUAL
;   Note:        Comparison is case-insensitive ('A' == 'a')
;
; See demo01 for: InitializeAll, FinalizeAll, StrNew, StrDup, StrDupMem,
;               StrCat, StrPtr, StrLen, StrDel, NumToStr, FileWriteString
;
; ========================================

; ========================================
; Global Variables
; ========================================
uglobal
  hString1       dd ?
  hString2       dd ?
  hString3       dd ?
endg

; ========================================
; Constants
; ========================================
iglobal
  cCRLF      text 13, 10

  cTitle     text "=== Demo 03: String Comparison ===", 13, 10, 13, 10
  cString1   text "Hello"
  cString2   text "hello"

  cLabel1    text "Test 1: StrCompCase('Hello', 'Hello') = "
  cLabel2    text "Test 2: StrCompCase('Hello', 'hello') = "
  cLabel3    text "Test 3: StrCompNoCase('Hello', 'hello') = "

  cEqual     text "EQUAL", 13, 10
  cNotEqual  text "NOT EQUAL", 13, 10
  cError     text "Error!", 13, 10
  cDone      text "Demo 03 complete!", 13, 10
endg

; ========================================
; Entry Point
; ========================================
start:
        InitializeAll

        stdcall FileWriteString, [STDOUT], cTitle

        stdcall StrDupMem, cString1
        jc      .error
        mov     [hString1], eax

        stdcall StrDupMem, cString1
        jc      .error
        mov     [hString2], eax

        stdcall StrDupMem, cString2
        jc      .error
        mov     [hString3], eax

        stdcall FileWriteString, [STDOUT], cLabel1

        stdcall StrCompCase, [hString1], [hString2]
        jc      .test1_equal

        stdcall FileWriteString, [STDOUT], cNotEqual
        jmp      .test2

.test1_equal:
        stdcall FileWriteString, [STDOUT], cEqual

.test2:
        stdcall FileWriteString, [STDOUT], cLabel2

        stdcall StrCompCase, [hString1], [hString3]
        jc      .test2_equal

        stdcall FileWriteString, [STDOUT], cNotEqual
        jmp      .test3

.test2_equal:
        stdcall FileWriteString, [STDOUT], cEqual

.test3:
        stdcall FileWriteString, [STDOUT], cLabel3

        stdcall StrCompNoCase, [hString1], [hString3]
        jc      .test3_equal

        stdcall FileWriteString, [STDOUT], cNotEqual
        jmp      .cleanup

.test3_equal:
        stdcall FileWriteString, [STDOUT], cEqual

.cleanup:
        stdcall StrDel, [hString1]
        stdcall StrDel, [hString2]
        stdcall StrDel, [hString3]

        stdcall FileWriteString, [STDOUT], cCRLF
        stdcall FileWriteString, [STDOUT], cDone

.finish:
        FinalizeAll
        stdcall TerminateAll, 0

.error:
        stdcall FileWriteString, [STDOUT], cError
        jmp     .cleanup
31
27.12.25 15:38
; _______________________________________________________________________________________
;| FreshLib String Processing Tutorial: Demo 04 - String Searching
;|_______________________________________________________________________________________|
;  Description: Demonstrates finding characters and substrings within strings
;  Topics: StrCharPos, StrPos, NULL handling, angle bracket syntax
;  Prerequisites: Demo 01
;_________________________________________________________________________________________

include "%lib%/freshlib.inc"

LINUX_INTERPRETER equ './ld-musl-i386.so'

@BinaryType console, compact
LIB_MODE equ NOGUI

options.DebugMode = 1

include "%lib%/freshlib.asm"

; ========================================
; FUNCTION REFERENCE
; ========================================
; StrCharPos - Find character in string, returns pointer to char or NULL
; StrPos - Find substring in string, returns pointer to match or NULL
; StrPtr - Get data pointer from string handle
; StrDupMem - Create string from memory buffer
; ========================================

; ========================================
; Global Variables
; ========================================
uglobal
  hHaystack      dd ?
  hNeedle        dd ?
  hPosStr        dd ?           ; String handle for NumToStr result
  pFound         dd ?
  pBase          dd ?
endg

; ========================================
; Constants
; ========================================
iglobal
  cCRLF      text 13, 10

  cTitle     text "=== Demo 04: String Searching ===", 13, 10, 13, 10
  cHaystack  text "The quick brown fox jumps over the lazy dog"
  cFox       text "fox"
  cCat       text "cat"

  cLabel1    text "1. StrCharPos - Find 'o': "
  cLabel2    text "2. StrCharPos - Find 'z': "
  cLabel3    text "3. StrPos - Find 'fox': "
  cLabel4    text "4. StrPos - Find 'cat': "

  cPosition  text "Position: "
  cFound     text "[FOUND]", 13, 10
  cNotFound  text "[NOT FOUND]", 13, 10
  cDone      text 13, 10, "Demo 04 complete!", 13, 10
endg

; ========================================
; Entry Point
; ========================================
start:
        InitializeAll

        stdcall FileWriteString, [STDOUT], cTitle

        stdcall StrDupMem, cHaystack
        jc      .error
        mov     [hHaystack], eax

; Get base pointer for offset calculations
        stdcall StrPtr, [hHaystack]
        mov     [pBase], eax

; Demo 1: Find 'o' in haystack
        stdcall FileWriteString, [STDOUT], cLabel1

        stdcall StrCharPos, [hHaystack], 'o'
        mov     ecx, eax              ; Save found pointer in ECX
        test    eax, eax
        jz      .not_found_1

; Calculate position: (found_ptr - base_ptr) + 1
        sub     ecx, [pBase]          ; position in bytes (0-based)
        mov     eax, ecx
        inc     eax                    ; Convert to 1-based position
        stdcall NumToStr, eax, ntsDec or ntsUnsigned
        mov     [hPosStr], eax        ; Save NumToStr result
        stdcall FileWriteString, [STDOUT], cPosition
        mov     eax, [hPosStr]        ; Restore NumToStr result
        stdcall FileWriteString, [STDOUT], eax
        stdcall FileWriteString, [STDOUT], cCRLF
        stdcall StrDel, [hPosStr]     ; Clean up NumToStr result
        jmp      .demo2

.not_found_1:
        stdcall FileWriteString, [STDOUT], cNotFound

.demo2:
; Demo 2: Find 'z' in haystack
        stdcall FileWriteString, [STDOUT], cLabel2

        stdcall StrCharPos, [hHaystack], 'z'
        mov     [pFound], eax          ; Save result
        test    eax, eax
        jz      .not_found_2

; Calculate position
        mov     ecx, [pFound]
        sub     ecx, [pBase]
        mov     eax, ecx
        inc     eax
        stdcall NumToStr, eax, ntsDec or ntsUnsigned
        mov     [hPosStr], eax        ; Save NumToStr result
        stdcall FileWriteString, [STDOUT], cPosition
        mov     eax, [hPosStr]        ; Restore NumToStr result
        stdcall FileWriteString, [STDOUT], eax
        stdcall FileWriteString, [STDOUT], cCRLF
        stdcall StrDel, [hPosStr]     ; Clean up NumToStr result
        jmp      .demo3

.not_found_2:
        stdcall FileWriteString, [STDOUT], cNotFound

.demo3:
; Demo 3: Find "fox" substring
        stdcall FileWriteString, [STDOUT], cLabel3

        stdcall StrDupMem, cFox
        jc      .error
        mov     [hNeedle], eax

        stdcall StrPos, [hHaystack], [hNeedle]
        mov     [pFound], eax          ; Save result before any clobber
        test    eax, eax
        jz      .not_found_3

; Calculate position
        mov     ecx, [pFound]
        sub     ecx, [pBase]
        mov     eax, ecx
        inc     eax
        stdcall NumToStr, eax, ntsDec or ntsUnsigned
        mov     [hPosStr], eax        ; Save NumToStr result
        stdcall FileWriteString, [STDOUT], cPosition
        mov     eax, [hPosStr]        ; Restore NumToStr result
        stdcall FileWriteString, [STDOUT], eax
        stdcall FileWriteString, [STDOUT], cCRLF
        stdcall StrDel, [hPosStr]     ; Clean up NumToStr result
        jmp      .cleanup_needle

.not_found_3:
        stdcall FileWriteString, [STDOUT], cNotFound

.cleanup_needle:
        stdcall StrDel, [hNeedle]

; Demo 4: Find "cat" substring (not found)
        stdcall FileWriteString, [STDOUT], cLabel4

        stdcall StrDupMem, cCat
        jc      .error
        mov     [hNeedle], eax

        stdcall StrPos, [hHaystack], [hNeedle]
        test    eax, eax
        jz      .not_found_4

; Should not reach here
        stdcall FileWriteString, [STDOUT], <"Found unexpectedly!", 13, 10>
        jmp      .cleanup

.not_found_4:
        stdcall FileWriteString, [STDOUT], cNotFound

.cleanup:
        stdcall StrDel, [hHaystack]
        stdcall StrDel, [hNeedle]

        stdcall FileWriteString, [STDOUT], cDone

.finish:
        FinalizeAll
        stdcall TerminateAll, 0

.error:
        stdcall FileWriteString, [STDOUT], <"Error!", 13, 10>
        jmp     .cleanup

Tutorial 01: StrLib Basics

0
#