callfar() now accepts a variable as address, so it can be used to indirect JSR to a subroutine whose address is not fixed. ('goto' already could indirect JMP to a variable address.)

This commit is contained in:
Irmen de Jong 2022-07-14 19:25:08 +02:00
parent 4bc65e9ef7
commit f690f58bd4
4 changed files with 30 additions and 23 deletions

View File

@ -139,20 +139,19 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
throw AssemblyError("callfar only works on cx16 target at this time")
val bank = fcall.args[0].constValue(program)?.number?.toInt()
val address = fcall.args[1].constValue(program)?.number?.toInt()
if(bank==null || address==null)
throw AssemblyError("callfar (jsrfar) requires constant arguments")
if(address !in 0xa000..0xbfff)
throw AssemblyError("callfar done on address outside of cx16 banked ram")
if(bank==0)
throw AssemblyError("callfar done on bank 0 which is reserved for the kernal")
val address = fcall.args[1].constValue(program)?.number?.toInt() ?: 0
val argAddrArg = fcall.args[2]
if(bank==null)
throw AssemblyError("callfar (jsrfar) bank has to be a constant")
if(fcall.args[1].constValue(program) == null) {
assignAsmGen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY, false)
asmgen.out(" sta (+)+0 | sty (+)+1 ; store jsrfar address word")
}
if(argAddrArg.constValue(program)?.number == 0.0) {
asmgen.out("""
jsr cx16.jsrfar
.word ${address.toHex()}
+ .word ${address.toHex()}
.byte ${bank.toHex()}""")
} else {
when(argAddrArg) {
@ -162,7 +161,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
asmgen.out("""
lda ${asmgen.asmVariableName(argAddrArg.identifier)}
jsr cx16.jsrfar
.word ${address.toHex()}
+ .word ${address.toHex()}
.byte ${bank.toHex()}
sta ${asmgen.asmVariableName(argAddrArg.identifier)}""")
}
@ -170,7 +169,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
asmgen.out("""
lda ${argAddrArg.number.toHex()}
jsr cx16.jsrfar
.word ${address.toHex()}
+ .word ${address.toHex()}
.byte ${bank.toHex()}
sta ${argAddrArg.number.toHex()}""")
}

View File

@ -899,14 +899,17 @@ memory(name, size, alignment)
callfar(bank, address, argumentaddress) ; NOTE: specific to cx16 target for now
Calls an assembly routine in another ram-bank on the CommanderX16 (using the ``jsrfar`` routine)
The banked RAM is located in the address range $A000-$BFFF (8 kilobyte).
Notice that bank $00 is used by the Kernal and should not be used by user code.
The banked RAM is located in the address range $A000-$BFFF (8 kilobyte), but you can specify
any address in system ram (why this can be useful is explained at the end of this paragraph)
The third argument can be used to designate the memory address
of an argument for the routine; it will be loaded into the A register and will
receive the result value returned by the routine in the A register. If you leave this at zero,
no argument passing will be done.
If the routine requires different arguments or return values, ``callfar`` cannot be used
and you'll have to set up a call to ``jsrfar`` yourself to process this.
Note: the address can be a variable or other expression, which allows you to use ``callfar`` with bank 0 to do an indirect JSR to a subroutine
whose address can vary (jump table, etc. ``goto`` can do an indirect JMP to a variable address): ``callfar(0, &routine, &argument)``
This is not very efficient though, so maybe you should write a small piece of inline assembly for this instead.
callrom(bank, address, argumentaddress) ; NOTE: specific to cx16 target for now
Calls an assembly routine in another rom-bank on the CommanderX16

View File

@ -753,14 +753,14 @@ You can still ``break`` out of such a loop if you want though.
Conditional Execution and Jumps
-------------------------------
Unconditional jump
^^^^^^^^^^^^^^^^^^
Unconditional jump: goto
^^^^^^^^^^^^^^^^^^^^^^^^
To jump to another part of the program, you use a ``goto`` statement with an addres or the name
of a label or subroutine::
goto $c000 ; address
goto name ; label or subroutine
goto $c000 ; address
goto name ; label or subroutine
uword address = $4000
goto address ; jump via address variable
@ -770,6 +770,8 @@ to another piece of code that eventually returns).
If you jump to an address variable (uword), it is doing an 'indirect' jump: the jump will be done
to the address that's currently in the variable.
Note: to do an indirect *JSR* to a routine with a varying address, you can use the ``callfar`` builtin function
(which is not very efficient) or you have to write a small piece of inline assembly.
Conditional execution

View File

@ -1,12 +1,15 @@
%import textio
%zeropage basicsafe
main {
sub start() {
byte tx = 1
uword @shared zzzz= $2000 + (tx as ubyte)
txt.print_uwhex(zzzz,true)
ubyte derp=2
callfar(0, &func, 0)
continue:
txt.print("main again.")
}
sub func() {
txt.print("func.")
}
}