docs about multi-assign

This commit is contained in:
Irmen de Jong 2024-03-28 23:24:14 +01:00
parent 4700a239b9
commit 56f41d5e34
4 changed files with 53 additions and 13 deletions

View File

@ -654,6 +654,11 @@ a fixed amount of memory which will not change. (You *can* change the value of
It is possible to "chain" assignments: ``x = y = z = 42``, this is just a shorthand
for the three individual assignments with the same value 42.
Only for certain subroutines that return multiple values it is possible to write a "multi assign" statement
with comma separated assignment targets, that assigns those multiple values to different targets in one statement.
Details can be found here: :ref:`multiassign`.
.. attention::
**Data type conversion (in assignments):**
When assigning a value with a 'smaller' datatype to variable with a 'larger' datatype,

View File

@ -665,25 +665,35 @@ takes no parameters. If the subroutine returns a value, usually you assign it t
If you're not interested in the return value, prefix the function call with the ``void`` keyword.
Otherwise the compiler will warn you about discarding the result of the call.
.. _multiassign:
Multiple return values
^^^^^^^^^^^^^^^^^^^^^^
Normal subroutines can only return zero or one return values.
However, the special ``asmsub`` routines (implemented in assembly code) or ``romsub`` routines
(referencing a routine in Kernal ROM) can return more than one return value.
For example a status in the carry bit and a number in A, or a 16-bit value in A/Y registers.
It is not possible to process the results of a call to these kind of routines
directly from the language, because only single value assignments are possible.
You can still call the subroutine and not store the results.
In these cases, it is possible to do a "multi assign" where the multiple return values of the subroutine call,
are all assigned to individual assignment targets. You simply write them as a comma separated list, so for instance::
**There is an exception:** if there's just one return value in a register, and one or more others that are returned
as bits in the status register (such as the Carry bit), the compiler allows you to call the subroutine.
bool flag
ubyte bytevar
uword wordvar
wordvar, flag, bytevar = multisub() ; call and assign the three result values
asmsub multisub() -> uword @AY, bool @Pc, ubyte @X { ... }
**There is also a special rule:** if there's just one return value in a register, and one or more others that are returned
as bits in the status register (such as the Carry bit), the compiler *also* allows you to call the subroutine and just assign a *single* return value.
It will then store the result value in a variable if required, and *try to keep the status register untouched
after the call* so you can often use a conditional branch statement for that. But the latter is tricky,
make sure you check the generated assembly code.
If there really are multiple relevant return values (other than a combined 16 bit return value in 2 registers),
you'll have to write a small block of custom inline assembly that does the call and stores the values
appropriately. Don't forget to save/restore any registers that are modified.
.. note::
For asmsubs or romsubs that return a boolean status flag in a cpu status register such as the Carry flag,
it is always more efficient to use a conditional branch like `if_cs` to act on that value, than storing
it in a variable and then adding an `if flag...` statement afterwards.
Subroutine definitions

View File

@ -1,7 +1,7 @@
TODO
====
add docs for multi-assigns.
make it possible to omit the Status Register values in multi-assigns regardless of the number of return values (is now 1 value)
...

View File

@ -1,17 +1,42 @@
%import textio
%import test_stack
%zeropage basicsafe
%option no_sysinit
main {
sub start() {
bool @shared flag
ubyte @shared bytevar
uword @shared wordvar
cx16.r1=9999
flag = test(42)
cx16.r0L, flag = test2(12345, 5566, flag, -42)
cx16.r1, flag = test3()
; cx16.r1=9999
; flag = test(42)
; cx16.r0L, flag = test2(12345, 5566, flag, -42)
; cx16.r1, flag = test3()
wordvar, bytevar, flag = test4()
wordvar, bytevar, flag = test4()
txt.print_uwhex(wordvar, true)
txt.spc()
txt.print_bool(flag)
txt.spc()
txt.print_ub(bytevar)
txt.nl()
}
romsub $8000 = test(ubyte arg @A) -> bool @Pc
romsub $8002 = test2(uword arg @AY, uword arg2 @R1, bool flag @Pc, byte value @X) -> ubyte @A, bool @Pc
romsub $8003 = test3() -> uword @R1, bool @Pc
asmsub test4() -> uword @AY, ubyte @X, bool @Pc {
%asm {{
lda #<$11ee
ldy #>$11ee
ldx #42
sec
rts
}}
}
}