SUB Declaration

Syntax:
SUB name [( [BYVAL|BYREF] param [AS type] [, ...] )]
    ... statements ...
END SUB

Description

A SUB (subroutine) declares a callable procedure that performs actions but does not return a value. Subroutines are used for procedural code, side effects, and operations where no result needs to be passed back to the caller.

Parameters follow the same rules as FUNCTION declarations: they can be passed BYVAL (by value, the default) or BYREF (by reference). A subroutine terminates when execution reaches END SUB or when EXIT SUB is encountered.

Since subroutines have no return value, they are invoked with the CALL statement or by simply using their name followed by arguments.

Parameters

ParameterDescription
nameThe identifier name of the subroutine. Must be unique within the program scope. Follows standard PBXA64 naming rules.
BYVALOptional keyword. Passes the argument by value (default). The subroutine receives a copy.
BYREFOptional keyword. Passes the argument by reference. Modifications to the parameter affect the caller's variable.
paramThe parameter name used within the subroutine body.
AS typeOptional type specification. Supported types include LONG, DWORD, INTEGER, BYTE, STRING, SINGLE, DOUBLE, QUAD, CUR, EXT, and user-defined TYPEs.

Example

SUB PrintGreeting(BYVAL name AS STRING)
    PRINT "Hello, "; name; "!"
END SUB

SUB SwapValues(BYREF a AS LONG, BYREF b AS LONG)
    LOCAL temp AS LONG
    temp = a
    a = b
    b = temp
END SUB

FUNCTION PBMAIN() AS LONG
    LOCAL x AS LONG
    LOCAL y AS LONG

    x = 10
    y = 20

    PrintGreeting "PBXA64 User"
    PRINT "Before swap: x="; x; " y="; y

    CALL SwapValues(x, y)
    PRINT "After swap:  x="; x; " y="; y

    FUNCTION = 0
END FUNCTION

Generated Assembly

Subroutine Prologue (Stack Frame Setup)

; Standard Win64 prologue
push   rbp                          ; save old base pointer
mov    rbp, rsp                     ; set new base pointer
sub    rsp, frame_size              ; allocate stack frame for locals

; Save non-volatile registers (if used)
push   rbx
push   rsi
push   rdi
push   r12
push   r13
push   r14
push   r15

; Initialize BYVAL parameters (copy from registers to stack)
mov    [rbp - param1_offset], rcx    ; 1st BYVAL param -> stack
mov    [rbp - param2_offset], rdx    ; 2nd BYVAL param -> stack
mov    [rbp - param3_offset], r8     ; 3rd BYVAL param -> stack
mov    [rbp - param4_offset], r9     ; 4th BYVAL param -> stack
; 5th+ params are already on the caller's stack above RSP

; Initialize BYREF parameters (store pointer directly)
; (No copy needed, pointer is already in RCX/RDX/etc.)

Subroutine Epilogue

; Restore non-volatile registers
pop    r15
pop    r14
pop    r13
pop    r12
pop    rdi
pop    rsi
pop    rbx

; Restore stack frame
add    rsp, frame_size
pop    rbp
ret

EXIT SUB

jmp    __pb_sub_epilogue            ; jump directly to epilogue

Notes