Syntax:
proc <proc_name> [(<any param1>, <any param2>, ...)] <statements> endproc call <proc_name> [(<any param1>, <any param2>, ...)]
The PROC
command introduces a new procedure that spans until the ENDPROC
command. Procedures are named subroutines that have a unique variable and label scope. Procedures may have one or more parameters that are passed to by the CALL
command. The CALL
command is the only way to execute a procedure (you can't GOTO
into a procedure, for example). You can use RETURN
to early exit a procedure.
The name of the procedure and the name of a parameter can't start with a command or directive name. For example, you can't define a procedure named repeatnumber
as REPEAT
is a command. Likewise, you can't name a parameter procparameter1
as PROC
is a directive.
Example:
rem ** procedure example ** rem ** these variables are global ** a = 1 b = 2 proc printmin(x, y) rem ** x, y and a are local variables ** a = 3 if x < y then print x else print y endproc call printmin(a, b) call printmin(-1, -5) print a
The above program will output (note that the value of a
remained 1 in the global scope):
1 -5 1
Parameters, like other variables have types. If the passed parameter's type mismatches the expected type, implicit type conversion will occur and a warning will be emitted.
For example:
proc output_params(byteparam!, intparam) print byteparam! print intparam endproc myByte! = 42 myInt = 300 call output_params(myInt, myByte!)
Note that wrong types are passed in the above example. The compiler will emit a warning about truncating the parameter myInt
to byte. The other parameter, myByte!
will be silently promoted to integer. Thus the above example will output:
44 42
To access global variables from within a procedure, prefix the variable name with the \
' modifier. Example:
let a=1 proc someproc let a=2 print \a endproc
rem ** will display: 1 call someproc
Although procedures can't return a value or be passed an output parameter, you can simulate this feature by passing an address to a procedure. Here is an example:
proc sumofints(a, b, result_addr) sum = a+b doke result_addr, sum endproc result = 0 call sumofints(2019, 25, @result) print result
Local variables of a procedure are static which means they are not dynamically allocated on each procedure call. This also means they keep their values through subsequent executions of the same procedure. Take the following example.
proc staticexample(firstrun) dim a if firstrun = 1 then let a = 1 else inc a print a endproc call staticexample(1) call staticexample(0)
The above program will output:
1 2
To declare and call parameterless procedures, just omit the parentheses:
proc simpleproc print "hello from simpleproc" endproc call simpleproc
XC=BASIC supports direct recursion which means that a procedure can call itself. Indirect recursion is not supported.
When the compiler detects direct recursion, it generates code to save local variables and procedure parameters on the stack and restore them when the procedure returns.
Be careful with recursion. The C64's stack is very small, it's very easy to provoke a stack overflow. To estimate how big stack space you'll need, you can use this formula:
(size of params + size of local vars + 2 byte address ) * possible number of iterations
Always leave some stack space for normal operations as well (8-16 bytes in general, but this can be more depending on the context).
Tip: a good practice to prevent stack overflow is to move as many local variables to the global scope as possible.