diff --git a/docs/source/programming.rst b/docs/source/programming.rst index 9e0a53f5d..02e3f07c8 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -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, diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index ece8a1a31..36d6f3ae6 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -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 diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 38bea12ee..b89fca3b8 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -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) ... diff --git a/examples/test.p8 b/examples/test.p8 index 58c2d25ff..b688a0bf8 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -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 + }} + } }