News:

;) This forum is the property of Proton software developers

Main Menu

How to pass a string variable to procedure as parameter?

Started by trastikata, Jul 26, 2026, 05:56 PM

Previous topic - Next topic

trastikata

Hello,

I have a particular case that I can't resolve. I want to pass a String variable to procedure as parameter, something like:

Dim myString2 As String * 15
Dim myString1 As String * 24

WorkWithString(myString1)
End

Proc WorkWithString(--> here the String Variable comes As parameter <--)

EndProc

I was thinking of using "ByRef" and then work with the String as a byte array, but then I can't estimate the input strings' size by using "Bound(sString)", the final code should be the equivalent of:

Dim myString1 As String * 24
Dim myString2 As String * 15

    WorkWithString(myString1)
End


Proc WorkWithString(--> here the String Variable comes As parameter, name it "sString" <--)
    Dim bCounter As Byte
   
    bCounter = 5
    Do
        'For illustration purposes
        sString[bCounter] = sString[bCounter + 1]
        Inc bCounter
    Loop While bCounter < Bound(sString)   
EndProc


JonW

Hi Trasikata

I have come across this before and Bound() cannot survive the call, and you do not need it.

Bound() is fixed at compile time from the declaration. Once you are inside the proc, the parameter holds an address, so Bound() would return the size of that address variable rather than the string. There is no way to recover it through a reference.

Use Len() instead. It accepts a Word variable holding a pointer to the string, so it works on a ByRef parameter directly:


Proc WorkWithString(ByRef pAddr As Word)
    Dim wSrc As Word

    If Len(pAddr) < 6 Then
        ExitProc
    EndIf

    pAddr = pAddr + 5              ' Destination = position 5
    wSrc  = pAddr + 1              ' Source      = position 6

    While Ptr8(wSrc) <> 0
        Ptr8(pAddr++) = Ptr8(wSrc++)
    Wend
    Ptr8(pAddr) = 0                ' Re-terminate
EndProc

Called exactly as you wanted:


WorkWithString(myString1)
WorkWithString(myString2)
-
This is similar to what Les uses throughout Strings.inc in the Includes folder. On the 8-bit branch, Str_TrimLeft, Str_Reverse and Str_InStr all take their string arguments as ByRef ... As Word and work through Ptr8(). Str_Reverse and Str_InStr call Len() on the parameter to get the length; Str_TrimLeft just walks to the null terminator instead. Either way, none of them needs Bound().

Output below is from a PIC18F27Q43 at 64MHz. The same proc handles both string sizes without knowing or caring which it was given.

A few things the run confirmed, in case they save anyone time:

  • Bound() on a String returns the declared character count minus 1. String * 24 gives 23.
  • The variable actually occupies 25 bytes for String * 24 — the 24 characters plus the null terminator.
  • Character data starts at AddressOf(). There is no length byte in front of it.
  • Direct indexing myString1 is 0-based and returns the first character.

If you would rather pass the address explicitly, drop the ByRef and use a plain Word:


wAddr = AddressOf(myString1)
WorkWithString(wAddr)

Both forms work. I assign to an intermediate variable rather than putting AddressOf() in the argument list directly, as I have not tested that.

'==============================================================================
'  StringPtr_Test_v3.bas
'  PIC18F27Q43 / Positron8 BASIC -- July 2026
'
'  Passing a String variable to a Proc as a parameter.
'
'  Two calling forms are tested:
'    Form A : Proc WorkWithStrA(ByRef pAddr As Word)
'            called as  WorkWithStrA(myString1)
'    Form B : Proc WorkWithStrB(pAddr As Word)
'            called as  wAddr = AddressOf(myString1) : WorkWithStrB(wAddr)
'
'  Both use Len(pAddr) for the runtime length and Ptr8() to walk the data.
'  Bound() is NOT usable inside either proc -- it would return the size of
'  the pointer variable, not the string.
'
'  HARDWARE: Rev 1.0 board. UART1 TX on RC4 via PPS, 115200 baud.
'            EPD power switch (RA4, TPS22917L active-low) left HIGH.
'==============================================================================

    Device = 18F27Q43
    Declare Xtal = 64
    Declare Hserial_Baud = 115200
    Declare HRSOut_Pin  = PORTC.4

Config_Start
    FEXTOSC  = OFF
    RSTOSC  = HFINTOSC_64MHZ
    CLKOUTEN = OFF
    FCMEN    = OFF
    CSWEN    = On
    MCLRE    = EXTMCLR
    MVECEN  = OFF
    IVT1WAY  = OFF
    BOREN    = SBORDIS
    ZCD      = OFF
    PPS1WAY  = OFF
    STVREN  = On
    LVP      = On
    XINST    = OFF
    WDTE    = OFF
Config_End

' =============================================================================
'  VARIABLES
' =============================================================================
Dim myString1 As String * 24
Dim myString2 As String * 15

Dim wAddr1 As Word
Dim wAddr2 As Word
Dim bLen  As Byte
Dim bChar  As Byte

    GoTo Main

' =============================================================================
'  Form A -- string passed straight in, compiler takes the address
'  Deletes the character at position 5 by shifting the remainder left.
'  Structure mirrors Str_TrimLeft in Les  Strings.inc
' =============================================================================
Proc WorkWithStrA(ByRef pAddr As Word)
    Dim wSrc As Word

    If Len(pAddr) < 6 Then
        ExitProc
    EndIf

    pAddr = pAddr + 5                      ' Destination = position 5
    wSrc  = pAddr + 1                      ' Source      = position 6

    While Ptr8(wSrc) <> 0
        Ptr8(pAddr++) = Ptr8(wSrc++)
    Wend
    Ptr8(pAddr) = 0                        ' Re-terminate
EndProc

' =============================================================================
'  Form B -- address passed by value, caller supplies AddressOf
'  Identical behaviour, different calling convention.
' =============================================================================
Proc WorkWithStrB(pAddr As Word)
    Dim wSrc As Word

    If Len(pAddr) < 6 Then
        ExitProc
    EndIf

    pAddr = pAddr + 5
    wSrc  = pAddr + 1

    While Ptr8(wSrc) <> 0
        Ptr8(pAddr++) = Ptr8(wSrc++)
    Wend
    Ptr8(pAddr) = 0
EndProc

' =============================================================================
'  MAIN
' =============================================================================
Main:
    TRISA  = %00001000
    LATA  = %11010101                      ' EPD PWR=1 (disabled), LEDs off
    ANSELA = %00000000
    TRISB  = %11011111
    ANSELB = %00000000
    TRISC  = %00101011
    LATC  = %00000000
    ANSELC = %00001000

    PPSLOCK = $55
    PPSLOCK = $AA
    PPSLOCKbits_PPSLOCKED = 0
    RC4PPS = $20
    PPSLOCK = $55
    PPSLOCK = $AA
    PPSLOCKbits_PPSLOCKED = 1

    DelayMS 500

    HRSOutLn "======================================"
    HRSOutLn "Positron8 String Parameter Test"
    HRSOutLn "======================================"
    HRSOut 13, 10

    myString1 = "ABCDEFGHIJKLMNOP"          ' 16 chars, declared 24
    myString2 = "0123456789"                ' 10 chars, declared 15

    wAddr1 = AddressOf(myString1)
    wAddr2 = AddressOf(myString2)

' --- Bound and storage --------------------------------------------------
    HRSOutLn "-- Bound and storage --"
    bLen = Bound(myString1)
    HRSOutLn "Bound(String * 24)  = ", Dec bLen
    bLen = Bound(myString2)
    HRSOutLn "Bound(String * 15)  = ", Dec bLen
    HRSOutLn "AddressOf myString1 = ", Dec wAddr1
    HRSOutLn "AddressOf myString2 = ", Dec wAddr2
    HRSOutLn "Bytes used by s1    = ", Dec wAddr2 - wAddr1
    HRSOut 13, 10

' --- Len direct vs Len through a pointer --------------------------------
    HRSOutLn "-- Len direct vs pointer --"
    bLen = Len(myString1)
    HRSOutLn "Len(myString1)      = ", Dec bLen, "  expect 16"
    bLen = Len(wAddr1)
    HRSOutLn "Len(wAddr1)        = ", Dec bLen, "  expect 16"
    bLen = Len(myString2)
    HRSOutLn "Len(myString2)      = ", Dec bLen, "  expect 10"
    bLen = Len(wAddr2)
    HRSOutLn "Len(wAddr2)        = ", Dec bLen, "  expect 10"
    HRSOut 13, 10

' --- Data layout --------------------------------------------------------
    HRSOutLn "-- Data layout --"
    bChar = Ptr8(wAddr1)
    HRSOutLn "Ptr8(wAddr1 + 0)    = ", Dec bChar, " char ", bChar, "  expect 65 A"
    bChar = Ptr8(wAddr1 + 1)
    HRSOutLn "Ptr8(wAddr1 + 1)    = ", Dec bChar, " char ", bChar, "  expect 66 B"
    bChar = myString1[0]
    HRSOutLn "myString1[0]        = ", Dec bChar, " char ", bChar
    bChar = myString1[1]
    HRSOutLn "myString1[1]        = ", Dec bChar, " char ", bChar
    HRSOutLn "65 at index 0 means indexing is 0-based"
    HRSOut 13, 10

' --- Form A : ByRef, string passed directly -----------------------------
    HRSOutLn "-- Form A : WorkWithStrA(myString1) --"
    HRSOutLn "s1 before = ", myString1
    WorkWithStrA(myString1)
    HRSOutLn "s1 after  = ", myString1
    HRSOutLn "expect      ABCDEGHIJKLMNOP"
    HRSOutLn "s2 before = ", myString2
    WorkWithStrA(myString2)
    HRSOutLn "s2 after  = ", myString2
    HRSOutLn "expect      012346789"
    HRSOut 13, 10

' --- Form B : address by value ------------------------------------------
    myString1 = "ABCDEFGHIJKLMNOP"          ' Reset both strings
    myString2 = "0123456789"

    HRSOutLn "-- Form B : WorkWithStrB(wAddr) --"
    HRSOutLn "s1 before = ", myString1
    WorkWithStrB(wAddr1)
    HRSOutLn "s1 after  = ", myString1
    HRSOutLn "expect      ABCDEGHIJKLMNOP"
    HRSOutLn "s2 before = ", myString2
    WorkWithStrB(wAddr2)
    HRSOutLn "s2 after  = ", myString2
    HRSOutLn "expect      012346789"
    HRSOut 13, 10

' --- Final lengths ------------------------------------------------------
    HRSOutLn "-- Final lengths --"
    bLen = Len(myString1)
    HRSOutLn "Len(myString1)      = ", Dec bLen, "  expect 15"
    bLen = Len(myString2)
    HRSOutLn "Len(myString2)      = ", Dec bLen, "  expect 9"
    HRSOut 13, 10

    HRSOutLn "======================================"
    HRSOutLn "Test complete"
    HRSOutLn "======================================"

    Stop



The return is here


======================================
Positron8 String Parameter Test
======================================

-- Bound and storage --
Bound(String * 24)  = 23
Bound(String * 15)  = 14
AddressOf myString1 = 1294
AddressOf myString2 = 1319
Bytes used by s1    = 25

-- Len direct vs pointer --
Len(myString1)      = 16  expect 16
Len(wAddr1)        = 16  expect 16
Len(myString2)      = 10  expect 10
Len(wAddr2)        = 10  expect 10

-- Data layout --
Ptr8(wAddr1 + 0)    = 65 char A  expect 65 A
Ptr8(wAddr1 + 1)    = 66 char B  expect 66 B
myString1[0]        = 65 char A
myString1[1]        = 66 char B
65 at index 0 means indexing is 0-based

-- Form A : WorkWithStrA(myString1) --
s1 before = ABCDEFGHIJKLMNOP
s1 after  = ABCDEGHIJKLMNOP
expect      ABCDEGHIJKLMNOP
s2 before = 0123456789
s2 after  = 012346789
expect      012346789

-- Form B : WorkWithStrB(wAddr) --
s1 before = ABCDEFGHIJKLMNOP
s1 after  = ABCDEGHIJKLMNOP
expect      ABCDEGHIJKLMNOP
s2 before = 0123456789
s2 after  = 012346789
expect      012346789

-- Final lengths --
Len(myString1)      = 15  expect 15
Len(myString2)      = 9  expect 9

======================================
Test complete
======================================







LeonJ

Hi Trastikata,
Here's how I do it:

'****************************************************************************************************
Proc FindRamStringChar(ByRef SourcePtr As Word, CharToFind As Byte), Bit

' finds a char in RAM string

' Entry:
'   address of SourceString
' Char to find
'
' Result = 1 if CharToFind found
'   Result = 0 if not found

Dim StringChar As Byte

    Clear Result

    Repeat

        StringChar = Ptr8(SourcePtr++)

        If StringChar = CharToFind Then
            Set Result
            ExitProc
        EndIf

    Until StringChar = 0

EndProc
'****************************************************************************************************
Proc FindRomStringChar(ByRef SourcePtr As Word, CharToFind As Byte), Bit

' finds a char in ROM (flash) string

' Entry:
'   address of SourceString
' Char to find
'
' Result = 1 if CharToFind found
'   Result = 0 if not found

Dim StringChar As Byte

    Clear Result

    Repeat

        StringChar = cPtr8(SourcePtr++)

        If StringChar = CharToFind Then
            Set Result
            ExitProc
        EndIf

    Until StringChar = 0

EndProc
'****************************************************************************************************
' Global vars for FindIndex()
' ----------------------------
Dim ResultIndex As Byte ' the index of a "found" item in a list

Proc FindIndex(ByRef ListPtr As Word, SearchString As String * 20), Bit

' Search in string pointed to by ListPtr for a string in SearchString
' and return Result=1 of found, with the SearchString's index in the list
' ---------------------------------------------------------------------
' Entry:
'   ListPtr      -> Address of first string (usually the declared string' name)
'   SearchString -> String to locate
'
' Exit:
'   Result      = 1 if found
'   ResultIndex = Zero-based index
'   ListPtr     = Points to found string

Dim ListString As String * 20

    Clear Result ' assume not found
    Clear ResultIndex ' starting at index 0
SearchString = ToLower(SearchString) ' make the input string lower case

    Repeat
ListString = CStr ListPtr ' find the next string in list
ListString = ToLower(ListString) ' make it lower case

':: HRSOut1Ln "FindIndex:Checking ", ListString
        If ListString = SearchString Then ' Compare current Flash string against search string
            Set Result
':: HRSOut1Ln "FindIndex:Found [", ListString, "] at ResultIndex=", Dec ResultIndex
ExitProc
Else
ListPtr = ListPtr + Len(ListString) + 1
Inc ResultIndex
EndIf

Until ListString = "listend"

':: HRSOut1Ln "FindIndex:SearchString not Found " ' dont get confused by this msg since if another list was also searched it will come up
        ResultIndex = $FF
       
EndProc
'****************************************************************************************************

Regards,
Leon

trastikata

Thank you LeonJ and JonW for the excellent examples, that's what I need !