documented the subroutine calling convention

This commit is contained in:
Irmen de Jong 2020-12-19 03:18:40 +01:00
parent 52e8a44517
commit 8b630798d8
5 changed files with 80 additions and 8 deletions

View File

@ -724,7 +724,7 @@ class Subroutine(override val name: String,
return when(returntypes.singleOrNull()) {
in ByteDatatypes -> listOf(RegisterOrStatusflag(RegisterOrPair.A, null))
in WordDatatypes -> listOf(RegisterOrStatusflag(RegisterOrPair.AY, null))
DataType.FLOAT -> listOf(RegisterOrStatusflag(RegisterOrPair.AY, null))
DataType.FLOAT -> listOf(RegisterOrStatusflag(RegisterOrPair.FAC1, null))
null -> emptyList()
else -> listOf(RegisterOrStatusflag(RegisterOrPair.AY, null))
}

View File

@ -1195,12 +1195,8 @@ $label nop""")
throw AssemblyError("normal subroutines can't return value in status register directly")
when (returnType) {
in IntegerDatatypes -> {
assignmentAsmGen.assignExpressionToRegister(returnvalue, returnReg.registerOrPair)
}
DataType.FLOAT -> {
// return the float value via FAC1
assignExpressionToRegister(returnvalue, RegisterOrPair.FAC1)
in NumericDatatypes -> {
assignExpressionToRegister(returnvalue, returnReg.registerOrPair)
}
else -> {
// all else take its address and assign that also to AY register pair

View File

@ -165,6 +165,7 @@ If you're targeting the CommanderX16 instead, there's the `x16emu <https://githu
programming.rst
syntaxreference.rst
libraries.rst
technical.rst
todo.rst

76
docs/source/technical.rst Normal file
View File

@ -0,0 +1,76 @@
===============
Technical stuff
===============
All variables are static in memory
----------------------------------
All variables are allocated statically, there is no concept of dynamic heap or stack frames.
Essentially all variables are global (but scoped) and can be accessed and modified anywhere,
but care should be taken ofcourse to avoid unexpected side effects.
Especially when you're dealing with interrupts or re-entrant routines: don't modify variables
that you not own or else you will break stuff.
Software stack for expression evaluation
----------------------------------------
Prog8 uses a software stack to evaluate complex expressions that it can't calculate in-place or
directly into the target variable, register, or memory location.
'software stack' means: seperated and not using the processor's hardware stack.
The software stack is implemented as follows:
- 2 pages of memory are allocated for this, exact locations vary per machine target.
For the C-64 they are set at $ce00 and $cf00 (so $ce00-$cfff is reserved).
For the Commander X16 they are set at $0400 and $0500 (so $0400-$05ff are reserved).
- these are the high and low bytes of the values on the stack (it's a 'split 16 bit word stack')
- for byte values just the lsb page is used, for word values both pages
- float values (5 bytes) are chopped up into 2 words and 1 byte on this stack.
- the X register is permanently allocated to be the stack pointer in the software stack.
- you can use the X register as long as you're not using the software stack.
But you *must* make sure it is saved and restored after the code that modifies it,
otherwise the evaluation stack gets corrupted.
Subroutine Calling Convention
-----------------------------
Calling a subroutine requires three steps:
#. preparing the arguments (if any) and passing them to the routine
#. calling the routine
#. preparig the return value (if any) and returning that from the call.
Calling the routine is just a simple JSR instruction, but the other two work like this:
``asmsub`` routines
^^^^^^^^^^^^^^^^^^^
These are usually declarations of kernel (ROM) routines or low-level assembly only routines,
that have their arguments solely passed into specific registers.
Sometimes even via a processor status flag such as the Carry flag.
Return values also via designated registers.
regular subroutines
^^^^^^^^^^^^^^^^^^^
- subroutine parameters are just variables scoped to the subroutine.
- the arguments passed in a call are evaluated (using the eval-stack if needed) and then
copied into those variables.
- the return value is passed back to the caller via cpu register(s):
Byte values will be put in ``A`` .
Word values will be put in ``A`` + ``Y`` register pair.
Float values will be put in the ``FAC1`` float 'register' (Basic allocated this somewhere in ram).
Calls to builtin functions are treated in a special way:
Generally if they have a single argument it's passed in a register or register pair.
Multiple arguments are passed like a normal subroutine, into variables.
Some builtin functions have a fully custom implementation.

View File

@ -20,7 +20,6 @@ Add more compiler optimizations to the existing ones.
- further optimize assignment codegeneration, such as the following:
- binexpr splitting (beware self-referencing expressions and asm code ballooning though)
- detect var->var argument passing to subroutines and avoid the second variable and copying of the value
- more optimizations on the language AST level
- more optimizations on the final assembly source level
- note: subroutine inlining is abandoned because of problems referencing non-local stuff. Can't move everything around.