mirror of
https://github.com/irmen/prog8.git
synced 2024-12-25 23:29:55 +00:00
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:
parent
4bc65e9ef7
commit
f690f58bd4
@ -139,20 +139,19 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
throw AssemblyError("callfar only works on cx16 target at this time")
|
throw AssemblyError("callfar only works on cx16 target at this time")
|
||||||
|
|
||||||
val bank = fcall.args[0].constValue(program)?.number?.toInt()
|
val bank = fcall.args[0].constValue(program)?.number?.toInt()
|
||||||
val address = fcall.args[1].constValue(program)?.number?.toInt()
|
val address = fcall.args[1].constValue(program)?.number?.toInt() ?: 0
|
||||||
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 argAddrArg = fcall.args[2]
|
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) {
|
if(argAddrArg.constValue(program)?.number == 0.0) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
jsr cx16.jsrfar
|
jsr cx16.jsrfar
|
||||||
.word ${address.toHex()}
|
+ .word ${address.toHex()}
|
||||||
.byte ${bank.toHex()}""")
|
.byte ${bank.toHex()}""")
|
||||||
} else {
|
} else {
|
||||||
when(argAddrArg) {
|
when(argAddrArg) {
|
||||||
@ -162,7 +161,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda ${asmgen.asmVariableName(argAddrArg.identifier)}
|
lda ${asmgen.asmVariableName(argAddrArg.identifier)}
|
||||||
jsr cx16.jsrfar
|
jsr cx16.jsrfar
|
||||||
.word ${address.toHex()}
|
+ .word ${address.toHex()}
|
||||||
.byte ${bank.toHex()}
|
.byte ${bank.toHex()}
|
||||||
sta ${asmgen.asmVariableName(argAddrArg.identifier)}""")
|
sta ${asmgen.asmVariableName(argAddrArg.identifier)}""")
|
||||||
}
|
}
|
||||||
@ -170,7 +169,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda ${argAddrArg.number.toHex()}
|
lda ${argAddrArg.number.toHex()}
|
||||||
jsr cx16.jsrfar
|
jsr cx16.jsrfar
|
||||||
.word ${address.toHex()}
|
+ .word ${address.toHex()}
|
||||||
.byte ${bank.toHex()}
|
.byte ${bank.toHex()}
|
||||||
sta ${argAddrArg.number.toHex()}""")
|
sta ${argAddrArg.number.toHex()}""")
|
||||||
}
|
}
|
||||||
|
@ -899,14 +899,17 @@ memory(name, size, alignment)
|
|||||||
|
|
||||||
callfar(bank, address, argumentaddress) ; NOTE: specific to cx16 target for now
|
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)
|
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).
|
The banked RAM is located in the address range $A000-$BFFF (8 kilobyte), but you can specify
|
||||||
Notice that bank $00 is used by the Kernal and should not be used by user code.
|
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
|
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
|
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,
|
receive the result value returned by the routine in the A register. If you leave this at zero,
|
||||||
no argument passing will be done.
|
no argument passing will be done.
|
||||||
If the routine requires different arguments or return values, ``callfar`` cannot be used
|
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.
|
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
|
callrom(bank, address, argumentaddress) ; NOTE: specific to cx16 target for now
|
||||||
Calls an assembly routine in another rom-bank on the CommanderX16
|
Calls an assembly routine in another rom-bank on the CommanderX16
|
||||||
|
@ -753,14 +753,14 @@ You can still ``break`` out of such a loop if you want though.
|
|||||||
Conditional Execution and Jumps
|
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
|
To jump to another part of the program, you use a ``goto`` statement with an addres or the name
|
||||||
of a label or subroutine::
|
of a label or subroutine::
|
||||||
|
|
||||||
goto $c000 ; address
|
goto $c000 ; address
|
||||||
goto name ; label or subroutine
|
goto name ; label or subroutine
|
||||||
|
|
||||||
uword address = $4000
|
uword address = $4000
|
||||||
goto address ; jump via address variable
|
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
|
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.
|
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
|
Conditional execution
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
%import textio
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
byte tx = 1
|
ubyte derp=2
|
||||||
uword @shared zzzz= $2000 + (tx as ubyte)
|
callfar(0, &func, 0)
|
||||||
txt.print_uwhex(zzzz,true)
|
continue:
|
||||||
|
txt.print("main again.")
|
||||||
|
}
|
||||||
|
|
||||||
|
sub func() {
|
||||||
|
txt.print("func.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user