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 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. 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:: .. attention::
**Data type conversion (in assignments):** **Data type conversion (in assignments):**
When assigning a value with a 'smaller' datatype to variable with a 'larger' datatype, 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. 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. Otherwise the compiler will warn you about discarding the result of the call.
.. _multiassign:
Multiple return values Multiple return values
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
Normal subroutines can only return zero or one return values. Normal subroutines can only return zero or one return values.
However, the special ``asmsub`` routines (implemented in assembly code) or ``romsub`` routines 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. (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. 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 In these cases, it is possible to do a "multi assign" where the multiple return values of the subroutine call,
directly from the language, because only single value assignments are possible. are all assigned to individual assignment targets. You simply write them as a comma separated list, so for instance::
You can still call the subroutine and not store the results.
**There is an exception:** if there's just one return value in a register, and one or more others that are returned bool flag
as bits in the status register (such as the Carry bit), the compiler allows you to call the subroutine. 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 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, 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. 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), .. note::
you'll have to write a small block of custom inline assembly that does the call and stores the values For asmsubs or romsubs that return a boolean status flag in a cpu status register such as the Carry flag,
appropriately. Don't forget to save/restore any registers that are modified. 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 Subroutine definitions

View File

@ -1,7 +1,7 @@
TODO 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 %zeropage basicsafe
%option no_sysinit %option no_sysinit
main { main {
sub start() { sub start() {
bool @shared flag bool @shared flag
ubyte @shared bytevar
uword @shared wordvar
cx16.r1=9999 ; cx16.r1=9999
flag = test(42) ; flag = test(42)
cx16.r0L, flag = test2(12345, 5566, flag, -42) ; cx16.r0L, flag = test2(12345, 5566, flag, -42)
cx16.r1, flag = test3() ; 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 $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 $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 romsub $8003 = test3() -> uword @R1, bool @Pc
asmsub test4() -> uword @AY, ubyte @X, bool @Pc {
%asm {{
lda #<$11ee
ldy #>$11ee
ldx #42
sec
rts
}}
}
} }