document the @R0 - @R15 register support for normal subroutine parameters

This commit is contained in:
Irmen de Jong 2024-11-30 20:46:31 +01:00
parent f603c543d3
commit 58f696d00a
4 changed files with 40 additions and 11 deletions

View File

@ -987,6 +987,27 @@ Subroutines can be defined in a Block, but also nested inside another subroutine
There are three different types of subroutines: regular subroutines (the one above), assembly-only, and
external subroutines. These last two are described in detail below.
Reusing *virtual registers* R0-R15 for parameters
*************************************************
.. sidebar::
🦶🔫 Footgun warning
when using this the program can clobber the contents of R0-R15 when doing other operations that also
use these registers, or when calling other routines because Prog8 doesn't have a callstack.
Be very aware of what you are doing, the compiler can't guarantee correct values by itself anymore.
Normally, every subroutine parameter will get its own local variable in the subroutine where the argument value
will be stored when the subroutine is called. In certain situations, this may lead to many variables being allocated.
You *can* instruct the compiler to not allocate a new variable, but instead to reuse one of the *virtual registers* R0-R15
(accessible in the code as ``cx16.r0`` - ``cx16.r15``) for the parameter. This is done by adding a ``@Rx`` tag
to the parameter. This can only be done for byte and word types.
Note: the R0-R15 *virtual registers* are described in more detail below for the Assembly subroutines.
Here's an example that reuses the R0 and the R1L (lower byte of R1) virtual registers for the paremeters::
sub get_indexed_byte(uword pointer @R0, ubyte index @R1) -> ubyte {
return @(cx16.r0 + cx16.r1L)
}
Assembly-Subroutines
^^^^^^^^^^^^^^^^^^^^

View File

@ -157,7 +157,10 @@ some builtin functions are special and won't exactly follow these rules.
**Some arguments will be passed in registers:**
For single byte and word arguments, the values are simply loaded in cpu registers by the caller before calling the subroutine.
*The subroutine itself will take care of putting the values into the parameter variables.* This saves on code size because
otherwise all callers would have to store the values in those variables themselves. The rules for this are as follows:
otherwise all callers would have to store the values in those variables themselves.
Note that his convention is also still used for subroutines that specify parameters to be put into
one of the *virtual registers* R0-R15, as those are in the end just variables too.
The rules are as follows:
Single byte parameter: ``sub foo(ubyte bar) { ... }``
gets bar in the accumulator A, *subroutine* stores it into parameter variable

View File

@ -1,8 +1,6 @@
TODO
====
document the @R0 - @R15 register support for normal subroutine parameters (🦶🔫 Footgun warning!)
make a compiler switch to disable footgun warnings
what to do with bankof(): keep it? add another syntax like \`value or ^value to get the bank byte?

View File

@ -1,12 +1,19 @@
%import textio
%option no_sysinit
%zeropage basicsafe
main {
sub start() {
}
}
xyz {
uword buffer_ptr = memory("buffers_stack", 8192, 0)
sub pop() -> ubyte {
return buffer_ptr[2]
@($2005) = 0
txt.print_ub(get_indexed_byte($2000, 5))
txt.nl()
@($2005) = 123
txt.print_ub(get_indexed_byte($2000, 5))
txt.nl()
}
sub get_indexed_byte(uword pointer @R0, ubyte index @R1) -> ubyte {
return @(cx16.r0 + cx16.r1L)
}
}