B068 Call Subroutine
Submitted by Steve Fewell
Description:
[&B068] Set BASIC Text Pointer A to point to the subroutine DEFinition's start address
Set the BASIC Text pointer A address (&0B-&0C) to the address pointed to by (&2A-&2B) [where &2A-&2B
is the address of the variable paremeter block for the specified subroutine name - this parameter block contains the start
address of the subroutine's DEFinition].
[&B072] Handle any arguments in the subroutine, and call the subroutine
Now, BASIC Text pointer A (&0B-&0C) points to the start of the subroutine's definition (that is the position
immediately following the subroutine name).
Set A to zero and push this value to the 6502 Stack - this value is the current number of Arguments and LOCAL variables.
Zero the BASIC Text pointer A offset (&0A), so that we will start processing the first character pointed to by
&0B-&0C.
Get the first non-space character pointed to by BASIC text pointer A. If it's an Open Bracket '(', then jump to &B0CB
to process the subroutine's arguments (routine described below); otherwise, decrement BASIC text pointer A offset
(&0A), so that BASIC text pointer A points to the first non-space character found.
[&B080] Any arguments have now been processed, so start executing the subroutine
Push the BASIC Text Pointer B address (&19-&1A) and offset (&1B) to the 6502 stack. BASIC text pointer B points
to the program location after the subroutine name in the FN/PROC call-statement (i.e. the calling location)?
[&B089] Call routine &900B to start executing the subroutine starting at the address after the subroutine name
and argument list (if any) in the subroutine DEFinition.
Note: routine &900B is called - not jumped to, so that we can regain control once the subroutine finishes. The 6502
'RTS' command will return us to address &B08C. This RTS-command is executed at the end of the ENDPROC statement
processing, or at the end of the '=' statement (to specify the end of a function) processing.
Therefore, on return from &900B the subroutine will have been executed (i.e. an ENDPROC will have been found (for a
PROCedure) or an '=' equals-statement will have been found (for a FuNction). This enables BASIC to correctly tidy up and
exit from the subroutine after the subroutine call.
-
When the Subroutine is called (the 'JSR &900B' statement), the following information is on the 6502 stack:
- Address &01FF - The Subroutine type - FN/PROC (i.e. either the PROC token or the FN token)
- Address &01FE - The address to return to once the subroutine has executed (PTR A) - Offset (&0A)
- Address &01FD - The address to return to once the subroutine has executed (PTR A) - LSB (&0B)
- Address &01FC - The address to return to once the subroutine has executed (PTR A) - MSB (&0C)
- Address &01FB - The number of Arguments or LOCAL variables used by the Subroutine
- Address &01FA - BASIC Text Pointer B - LSB (&19)
- Address &01F9 - BASIC Text Pointer B - MSB (&1A)
- Address &01F8 - BASIC Text Pointer B - Offset (&1B)
- Address &01F7 - Return address for the JSR &900B statement - LSB
- Address &01F6 - Return address for the JSR &900B statement - MSB
- Address &01F5 - <The next free Stack position - pointed to by the 6502 Stack Pointer>
[&B08C] Now the subroutine code has been executed, so tidy up after the execution of the subroutine:
Retrieve the BASIC text pointer B address (&19-&1A) and offset (&1B) from the 6502 stack.
Retrieve the number of subroutine arguments (and LOCAL variables) from the 6502 stack.
-
If there are subroutine arguments and LOCAL variables (the value returned from the stack is not zero) then:
- * 1) [&B098] Store the number of subroutine arguments and LOCAL variables in location &3F
- * 2) Call routine &BD06 to retrieve the Integer value from the BASIC stack to locations &37-&3A
- (the integer value returned is the 2-byte variable value address and 1-byte variable type).
- * 3) Call routine &BC6A to set the variable (i.e. the value pointed to by &37-&38) to the value
- from the BASIC stack (i.e. this retrieves the variable's previous value, which was pushed to the BASIC stack when the
- argument/local variable was processed - and the variable parameter block (the variable address and type information)
- was pushed directly after the variable value).
- * 4) Decrement location &3F to decrease the number of outstanding arguments and/or LOCAL variables
- * 5) If there are more variables (arguments and/or LOCAL variables) that need to be set back to the value
- that they held before the subroutine was called (&3F is not zero) then jump back to &B09A (step 2) to process
- the next variable.
[&B0A4] Now the original values of the arguments and LOCAL variables (if any) have been restored.
Retrieve the BASIC Text Pointer A location (which points to the program location immediately after the subroutine call
statement (i.e. the FN/PROC call) from the 6502 stack (3 bytes are retrieved, &0A-&0B is set to the address value
and &0C is set to the address offset value).
[&B0AA] Retrieve the Keyword token (either FN or PROC, indicating the subroutine call type) from the 6502 stack,
and discard its value, as it is no longer needed.
[&B0AB] Reset the 6502 stack to its original state - before the subroutine call:
Load the character at the top of the BASIC Stack (this is the 6502 stack pointer value before the subroutine call was
made). Set the 6502 Stack pointer to this value.
Set Y to zero. Keep incrementing X and Y, and retrieving the next 6502 stack byte from the BASIC Stack (to its correct
&0100 + X location), until X has reached #&FF.
Now, the 6502 stack has been restored to its original state (i.e. if a subroutine is active, then it will contain the
details of the subroutine.
Add Y bytes to the
BASIC Stack pointer (&04-&05) to remove the original 6502 stack contents from the
BASIC stack.
Exit with A set to the value of location &27 (the return value type - if a value was returned (FN-call)).
[&B0CB] Process the subroutine's arguments
Store the BASIC Text Pointer B address (&19-&1A) and offset (&1B) to the 6502 stack.
Call routine &98AE to set BASIC Text pointer B to the position after the bracket in the subroutine definition (that
is: the address in BASIC text pointer A), and read the variable name at that location, getting its address and type
information (and creating the variable if it doesn't exist).
If the zero flag is set on return from routine &98AE then no variable name was found (the name was invalid), so jump
to &B12B to set the 6502 stack pointer to #&FB (to point to the stack location after the call statement address,
retrieve the address to return to once the call statement has executed from the 6502 stack (into PTR A - &0B-&0C)
[so that the error occurs at the call statement program line - and not at the subroutine definition position] and issue
Arguments error (as the required formal parameter was not found).
[&B0D9] Now we have sucessfully obtained details of the first (or next) variable in the subroutine definition (the
formal parameter); later, we will map onto this variable the value from the call statement parameters (i.e. the actual
parameter). But first the value and address of the current variable's contents needs to be stored, and any other formal
parameters need to be processed - this is done as described below.
Store the BASIC Text Pointer B offset to the BASIC Text pointer A offset (so that BASIC text pointer A now also points
to the character after the variable name in the subroutine definition (either DEF PROC or DEF FN).
Retrieve the BASIC Text Pointer B 3-byte value (address and offset) from the 6502 stack - this points to the call
statement (the FN/PROC statement) location.
Retrieve the number of arguments value from the 6502 stack (at location &01FB), and store this value in X.
Store the [formal parameter] variable address and type information (&2A-&2C) on the 6502 stack.
Increment X (the number of subroutine arguments) and store this new number of arguments value on the 6502 stack.
Call routine &B181 to load the value of the variable (the value is pointed to by &2A-&2B) and to store the
current value of the variable on the BASIC Stack - this ensures that if a formal parameter variable has the same name as
an already existing variable, then the original value can be restored once the subroutine has finished executing. Routine
&B181 also pushes the variable's address and type information (&2A-&2C) to the BASIC stack.
Call routine &8CE5 to get the next non-space character pointed to by BASIC Text pointer A (the location after the
variable name (just processed) in the subroutine definition, and compare the character with a comma (','). If it was a
comma, then jump back to &B0CB to process the next [formal parameter] variable name.
Otherwise, check whether the character is a closing bracket ')' - if it isn't then jump to &B12B to set the 6502 stack
pointer to #&FB (to point to the stack location after the call statement address, retrieve the address to return to
once the call statement has executed from the 6502 stack (into PTR A - &0B-&0C) [so that the error occurs at the
call statement program line - not at the subroutine definition position] and issue Arguments error (as the formal
parameter definition is invalid).
[&B0FE] Otherwise, the formal parameters have been declared without problem, and they have been processed, now BASIC
needs to assign the actual parameter values to these formal parameters, this is done as described below.
Firstly zero is stored on the 6502 stack (number of actual arguments that have been processed).
If the first non-space character at the BASIC Text Pointer B location (the program location of the call statement) is not
an open bracket '(' character, then no actual parameters were found, so jump to &B12B to set the 6502 stack pointer
to #&FB (to point to the stack location after the call statement address, retrieve the address to return to once
the call statement has executed from the 6502 stack (into PTR A - &0B-&0C) [so that the error occurs at the call
statement program line - not at the subroutine definition position] and issue Arguments error (as the required actual
parameters have not been provided.
[&B108] Evaluate the next actual parameter value and push the value to the BASIC stack
Call routine &9D3B to evaluate the expression after the open bracket character and to store the expression result in
either the
SWA (if the result is a String value), the
FWA (if the result is a Floating-Point value) or the
IWA (if the
result is an Integer. This result value should be the value of the first actual parameter.
Call routine &BC22 to push the result value to the BASIC Stack.
Store the value in location &27 (the result type) to location &2D and push the IWA value (bytes &2A-&2D) to
the BASIC Stack. Note: This is wasteful - as we only need to store 1-byte to the BASIC stack (not 4). This method is
probably used due to the lack of a routine to push a 1-byte value to the BASIC stack.
Retrieve the number of actual arguments from the 6502 stack (in to register X), increment this counter and push the new
value back to the 6502 stack.
If the next character at the BASIC Text pointer B location (after the actual parameter value we just evaluated) is a
comma ',', then jump back to &B108 to evaluate the next actual parameter and push its value to the BASIC stack.
As the next non-space character is not a comma, it must be a closing bracket - which denotes the finish of the actual
parameter list - if it isn't then jump to &B12B to tidy up, set the BASIC Text pointer A location to the call
statement location and issue Arguments error.
Retrieve the number of actual parameters (in the subroutine call statement) from the 6502 stack (& discard this value - as
we already have it in the X register).
Retrieve the number of formal parameters (in the subroutine definition) from the 6502 stack and store this value in both
location &4C and location &4D.
Compare X (number of actual parameters) with the value in location &4C (number of formal parameters) if they do not
match, then continue to &B12B to tidy up, set the BASIC Text pointer A location to the call statement location and
issue Arguments error.
[&B140] Call routine &BCE6 to retrieve the IWA value from the BASIC stack (this sets location &2D to the
type of the last actual parameter).
Retrieve the last Formal parameter variable's address (2 bytes) & type details from the 6502 stack to locations
&2A-&2C.
-
If the formal parameter variable is a String (location &2C is negative (#&80 ?)), then:
- * [&B16D] Check the actual parameter's type (location &2D) - if it is not zero (i.e. not a String
- value) then jump to &B12B to Set BASIC text pointer A to the call statement
- location (so Error line number is correct) and issue Arguments error.
- * Call routine &BCD2 to retrieve the String value from the BASIC Stack (to the SWA).
- * Call routine &90AE to set the String variable's value (pointed to by &37-&38) to the SWA
- value
- * [&B177] Now the formal parameter variable has been set to the value of the actual parameter.
- * Decrement location &4C (the number of arguments to process)
- * If there are further arguments to process then jump back to &B140 to set the next Formal parameter
- to its corresponding Actual parameter value.
- * Otherwise, all arguments have been processed, so push the value at location &4D (the number of arguments
- in the subroutine call) to the 6502 stack and jump to &B080 to continue with the Subroutine call.
If the formal parameter variable is numeric (location &2C is positive (#&04 or #&05 ?)), then:
* [&B14D] Check the actual parameter's type (location &2D) - if it is zero (String value) then
jump to &B12B to Set BASIC text pointer A to the call statement location (so that the
Error Line Number (ERL) is correct) and issue Arguments error.
* Store the Actual parameter's type value (&2D) in location &27
* Call routine &BDC6 to copy the IWA (&2A-&2D) to locations &37-&3A
* If the Actual parameter type (location &27) is an Integer then [&B165] Retrieve the IWA value
from the BASIC stack
* If the Actual parameter type (location &27) is a Floating-Point value then [&B15D] Retrieve the
FWA value from the BASIC stack (first call &BBE8 to pop the value and set &4A-&4B to point to the value,
then call &A541 to load the value pointed to by argp (&4A-&4B) in the FWA).
* [&B168] Call routine &B338 to set the numeric variable's value (at the address pointed to by
&37-&38) to the current result value (in either the IWA or FWA, depending on the value at location &27)
* [&B177] Now the formal parameter variable has been set to the value of the actual parameter.
* Decrement location &4C (the number of arguments to process)
* If there are further arguments to process then jump back to &B140 to set the next Formal parameter
to its corresponding Actual parameter value.
* Otherwise, all arguments have been processed, so push the value at location &4D (the number of arguments
in the subroutine call) to the 6502 stack and jump to &B080 to continue with the Subroutine call.
Disassembly for the Call Subroutine routine
B068 |
* |
178 042 |
B2 2A |
LDA (&2A) |
B06A |
|
133 011 |
85 0B |
STA &0B |
B06C |
|
160 001 |
A0 01 |
LDY#&01 |
B06E |
* |
177 042 |
B1 2A |
LDA (&2A),Y |
B070 |
|
133 012 |
85 0C |
STA &0C |
B072 |
|
169 000 |
A9 00 |
LDA#&00 |
B074 |
H |
072 |
48 |
PHA |
B075 |
d |
100 010 |
64 0A |
STZ &0A |
B077 |
|
032 224 142 |
20 E0 8E |
JSR &8EE0 Get next non-space character pointed to by Ptr A |
B07A |
( |
201 040 |
C9 28 |
CMP#&28 |
B07C |
M |
240 077 |
F0 4D |
BEQ 77 --> &B0CB Process Subroutine Arguments |
B07E |
|
198 010 |
C6 0A |
DEC &0A |
B080 |
|
165 027 |
A5 1B |
LDA &1B |
B082 |
H |
072 |
48 |
PHA |
B083 |
|
165 025 |
A5 19 |
LDA &19 |
B085 |
H |
072 |
48 |
PHA |
B086 |
|
165 026 |
A5 1A |
LDA &1A |
B088 |
H |
072 |
48 |
PHA |
B089 |
|
032 011 144 |
20 0B 90 |
JSR &900B Execute subroutine (starting at the BASIC program statement at PTR A) |
B08C |
h |
104 |
68 |
PLA |
B08D |
|
133 026 |
85 1A |
STA &1A |
B08F |
h |
104 |
68 |
PLA |
B090 |
|
133 025 |
85 19 |
STA &19 |
B092 |
h |
104 |
68 |
PLA |
B093 |
|
133 027 |
85 1B |
STA &1B |
B095 |
h |
104 |
68 |
PLA |
B096 |
|
240 012 |
F0 0C |
BEQ 12 --> &B0A4 |
B098 |
? |
133 063 |
85 3F |
STA &3F |
B09A |
|
032 006 189 |
20 06 BD |
JSR &BD06 Retrieve Integer from stack to locations &37-&3A |
B09D |
j |
032 106 188 |
20 6A BC |
JSR &BC6A Set Variable to value from the BASIC Stack |
B0A0 |
? |
198 063 |
C6 3F |
DEC &3F |
B0A2 |
|
208 246 |
D0 F6 |
BNE -10 --> &B09A |
B0A4 |
h |
104 |
68 |
PLA |
B0A5 |
|
133 012 |
85 0C |
STA &0C |
B0A7 |
h |
104 |
68 |
PLA |
B0A8 |
|
133 011 |
85 0B |
STA &0B |
B0AA |
h |
104 |
68 |
PLA |
B0AB |
|
133 010 |
85 0A |
STA &0A |
B0AD |
h |
104 |
68 |
PLA |
B0AE |
|
178 004 |
B2 04 |
LDA (&04) |
B0B0 |
|
170 |
AA |
TAX |
B0B1 |
|
154 |
9A |
TXS |
B0B2 |
|
160 000 |
A0 00 |
LDY#&00 |
B0B4 |
|
200 |
C8 |
INY |
B0B5 |
|
232 |
E8 |
INX |
B0B6 |
|
177 004 |
B1 04 |
LDA (&04),Y |
B0B8 |
|
157 000 001 |
9D 00 01 |
STA &0100,X |
B0BB |
|
224 255 |
E0 FF |
CPX#&FF |
B0BD |
|
208 245 |
D0 F5 |
BNE -11 --> &B0B4 |
B0BF |
|
152 |
98 |
TYA |
B0C0 |
e |
101 004 |
65 04 |
ADC &04 |
B0C2 |
|
133 004 |
85 04 |
STA &04 |
B0C4 |
|
144 002 |
90 02 |
BCC 2 --> &B0C8 |
B0C6 |
|
230 005 |
E6 05 |
INC &05 |
B0C8 |
' |
165 039 |
A5 27 |
LDA &27 |
B0CA |
` |
096 |
60 |
RTS |
[&B0CB] "Process the subroutine's arguments" disassembly
B0CB |
|
165 027 |
A5 1B |
LDA &1B |
B0CD |
H |
072 |
48 |
PHA |
B0CE |
|
165 025 |
A5 19 |
LDA &19 |
B0D0 |
H |
072 |
48 |
PHA |
B0D1 |
|
165 026 |
A5 1A |
LDA &1A |
B0D3 |
H |
072 |
48 |
PHA |
B0D4 |
|
032 174 152 |
20 AE 98 |
JSR &98AE Evaluate variable name & create it if it's a new variable |
B0D7 |
R |
240 082 |
F0 52 |
BEQ 82 --> &B12B Tidy up and issue 'Arguments' error |
B0D9 |
|
165 027 |
A5 1B |
LDA &1B |
B0DB |
|
133 010 |
85 0A |
STA &0A |
B0DD |
h |
104 |
68 |
PLA |
B0DE |
|
133 026 |
85 1A |
STA &1A |
B0E0 |
h |
104 |
68 |
PLA |
B0E1 |
|
133 025 |
85 19 |
STA &19 |
B0E3 |
h |
104 |
68 |
PLA |
B0E4 |
|
133 027 |
85 1B |
STA &1B |
B0E6 |
|
250 |
FA |
PLX |
B0E7 |
, |
165 044 |
A5 2C |
LDA &2C |
B0E9 |
H |
072 |
48 |
PHA |
B0EA |
+ |
165 043 |
A5 2B |
LDA &2B |
B0EC |
H |
072 |
48 |
PHA |
B0ED |
* |
165 042 |
A5 2A |
LDA &2A |
B0EF |
H |
072 |
48 |
PHA |
B0F0 |
|
232 |
E8 |
INX |
B0F1 |
|
218 |
DA |
PHX |
B0F2 |
|
032 129 177 |
20 81 B1 |
JSR &B181 Load Variable & Push its value, address and type to the BASIC Stack |
B0F5 |
|
032 229 140 |
20 E5 8C |
JSR &8CE5 Compare next non-space [PTR A] character with ',' |
B0F8 |
|
240 209 |
F0 D1 |
BEQ -47 --> &B0CB Process Subroutine Arguments |
B0FA |
) |
201 041 |
C9 29 |
CMP#&29 |
B0FC |
- |
208 045 |
D0 2D |
BNE 45 --> &B12B Tidy up and issue 'Arguments' error |
B0FE |
|
169 000 |
A9 00 |
LDA#&00 |
B100 |
H |
072 |
48 |
PHA |
B101 |
|
032 213 142 |
20 D5 8E |
JSR &8ED5 Get next non-space character (PTR B) |
B104 |
( |
201 040 |
C9 28 |
CMP#&28 |
B106 |
# |
208 035 |
D0 23 |
BNE 35 --> &B12B Tidy up and issue 'Arguments' error |
B108 |
; |
032 059 157 |
20 3B 9D |
JSR &9D3B Evaluate expression at BASIC Text pointer B |
B10B |
" |
032 034 188 |
20 22 BC |
JSR &BC22 Push Variable to BASIC Stack (calls correct routine depending on flags) |
B10E |
' |
165 039 |
A5 27 |
LDA &27 |
B110 |
- |
133 045 |
85 2D |
STA &2D |
B112 |
& |
032 038 188 |
20 26 BC |
JSR &BC26 Push IWA value to the BASIC Stack [pushi] |
B115 |
|
250 |
FA |
PLX |
B116 |
|
232 |
E8 |
INX |
B117 |
|
218 |
DA |
PHX |
B118 |
|
032 235 142 |
20 EB 8E |
JSR &8EEB Get next non-space char (PTR B) & compare with ',' |
B11B |
|
240 235 |
F0 EB |
BEQ -21 --> &B108 Process next actual parameter (call statement location) |
B11D |
) |
201 041 |
C9 29 |
CMP#&29 |
B11F |
|
208 010 |
D0 0A |
BNE 10 --> &B12B Tidy up and issue 'Arguments' error |
B121 |
h |
104 |
68 |
PLA |
B122 |
h |
104 |
68 |
PLA |
B123 |
L |
133 076 |
85 4C |
STA &4C |
B125 |
M |
133 077 |
85 4D |
STA &4D |
B127 |
L |
228 076 |
E4 4C |
CPX &4C |
B129 |
|
240 021 |
F0 15 |
BEQ 21 --> &B140 |
B12B |
|
162 251 |
A2 FB |
LDX#&FB |
B12D |
|
154 |
9A |
TXS |
B12E |
h |
104 |
68 |
PLA |
B12F |
|
133 012 |
85 0C |
STA &0C |
B131 |
h |
104 |
68 |
PLA |
B132 |
|
133 011 |
85 0B |
STA &0B |
B134 |
|
|
|
...'Arguments' error... |
B140 |
|
032 230 188 |
20 E6 BC |
JSR &BCE6 Retrieve IWA value from the BASIC Stack [popi] |
B143 |
h |
104 |
68 |
PLA |
B144 |
* |
133 042 |
85 2A |
STA &2A |
B146 |
h |
104 |
68 |
PLA |
B147 |
+ |
133 043 |
85 2B |
STA &2B |
B149 |
h |
104 |
68 |
PLA |
B14A |
, |
133 044 |
85 2C |
STA &2C |
B14C |
0 |
048 031 |
30 1F |
BMI 31 --> &B16D |
B14E |
- |
165 045 |
A5 2D |
LDA &2D |
B150 |
|
240 217 |
F0 D9 |
BEQ -39 --> &B12B Tidy up and issue 'Arguments' error |
B152 |
' |
133 039 |
85 27 |
STA &27 |
B154 |
7 |
162 055 |
A2 37 |
LDX#&37 |
B156 |
|
032 198 189 |
20 C6 BD |
JSR &BDC6 Store Integer (IWA) to zero page location (specified by X) |
B159 |
' |
165 039 |
A5 27 |
LDA &27 |
B15B |
|
016 008 |
10 08 |
BPL 8 --> &B165 |
B15D |
|
032 232 187 |
20 E8 BB |
JSR &BBE8 Pop Float from Stack and set argp (&4A-&4B) to point to the value |
B160 |
A |
032 065 165 |
20 41 A5 |
JSR &A541 Load FWA with Floating-Point variable (pointed to by argp) |
B163 |
|
128 003 |
80 03 |
BRA 3 --> &B168 |
B165 |
|
032 230 188 |
20 E6 BC |
JSR &BCE6 Retrieve IWA value from the BASIC Stack [popi] |
B168 |
8 |
032 056 179 |
20 38 B3 |
JSR &B338 Set numeric variable |
B16B |
|
128 010 |
80 0A |
BRA 10 --> &B177 |
B16D |
- |
165 045 |
A5 2D |
LDA &2D |
B16F |
|
208 186 |
D0 BA |
BNE -70 --> &B12B Tidy up and issue 'Arguments' error |
B171 |
|
032 210 188 |
20 D2 BC |
JSR &BCD2 Pop String (SWA) from the stack |
B174 |
|
032 174 144 |
20 AE 90 |
JSR &90AE Set String variable |
B177 |
L |
198 076 |
C6 4C |
DEC &4C |
B179 |
|
208 197 |
D0 C5 |
BNE -59 --> &B140 |
B17B |
M |
165 077 |
A5 4D |
LDA &4D |
B17D |
H |
072 |
48 |
PHA |
B17E |
L |
076 128 176 |
4C 80 B0 |
JMP &B080 Start executing the subroutine |
Or