mirror of
https://github.com/irmen/prog8.git
synced 2024-11-26 11:49:22 +00:00
documented the subroutine calling convention
This commit is contained in:
parent
52e8a44517
commit
8b630798d8
@ -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))
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
76
docs/source/technical.rst
Normal 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.
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user