====== PROC ... ENDPROC ====== Syntax: proc [(, , ...)] endproc call [(, , ...)] 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 ===== Parameter types ===== 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 ===== Accessing global variables ===== 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 ===== Simulated output params ===== 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 ===== Locals are static ===== 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 ===== Procedures without parameters ===== To declare and call parameterless procedures, just omit the parentheses: proc simpleproc print "hello from simpleproc" endproc call simpleproc ===== Recursion ===== **XC=BASIC** supports direct recursion which means that a procedure can call itself. [[https://en.wikipedia.org/wiki/Recursion_(computer_science)#Indirect_recursion|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. ==== Prevent stack overflow ==== **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.