diff --git a/compiler/src/prog8/compiler/functions/BuiltinFunctions.kt b/compiler/src/prog8/compiler/functions/BuiltinFunctions.kt index d61a9a06f..369d4b2df 100644 --- a/compiler/src/prog8/compiler/functions/BuiltinFunctions.kt +++ b/compiler/src/prog8/compiler/functions/BuiltinFunctions.kt @@ -145,6 +145,7 @@ private val functionSignatures: List = listOf( FSignature("memory" , true, listOf(FParam("name", setOf(DataType.STR)), FParam("size", setOf(DataType.UWORD))), DataType.UWORD), FSignature("swap" , false, listOf(FParam("first", NumericDatatypes), FParam("second", NumericDatatypes)), null), FSignature("callfar" , false, listOf(FParam("bank", setOf(DataType.UBYTE)), FParam("address", setOf(DataType.UWORD)), FParam("arg", setOf(DataType.UWORD))), null), + FSignature("callrom" , false, listOf(FParam("bank", setOf(DataType.UBYTE)), FParam("address", setOf(DataType.UWORD)), FParam("arg", setOf(DataType.UWORD))), null), ) diff --git a/compiler/src/prog8/compiler/target/cpu6502/codegen/BuiltinFunctionsAsmGen.kt b/compiler/src/prog8/compiler/target/cpu6502/codegen/BuiltinFunctionsAsmGen.kt index 0ce08cfc4..b319b74a3 100644 --- a/compiler/src/prog8/compiler/target/cpu6502/codegen/BuiltinFunctionsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/cpu6502/codegen/BuiltinFunctionsAsmGen.kt @@ -67,6 +67,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val "poke" -> throw AssemblyError("poke() should have been replaced by @()") "cmp" -> funcCmp(fcall) "callfar" -> funcCallFar(fcall) + "callrom" -> funcCallRom(fcall) else -> throw AssemblyError("missing asmgen for builtin func ${func.name}") } } @@ -116,6 +117,63 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val } } + private fun funcCallRom(fcall: IFunctionCall) { + if(asmgen.options.compTarget !is Cx16Target) + throw AssemblyError("callrom 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("callrom requires constant arguments") + + if(address !in 0xc000..0xffff) + throw AssemblyError("callrom done on address outside of cx16 banked rom") + if(bank>=32) + throw AssemblyError("callrom bank must be <32") + + val argAddrArg = fcall.args[2] + if(argAddrArg.constValue(program)?.number == 0) { + asmgen.out(""" + lda $01 + pha + lda #${bank} + sta $01 + jsr ${address.toHex()} + pla + sta $01""") + } else { + when(argAddrArg) { + is AddressOf -> { + if(argAddrArg.identifier.targetVarDecl(program)?.datatype != DataType.UBYTE) + throw AssemblyError("callrom done with 'arg' pointer to variable that's not UBYTE") + asmgen.out(""" + lda $01 + pha + lda #${bank} + sta $01 + lda ${asmgen.asmVariableName(argAddrArg.identifier)} + jsr ${address.toHex()} + sta ${asmgen.asmVariableName(argAddrArg.identifier)} + pla + sta $01""") + } + is NumericLiteralValue -> { + asmgen.out(""" + lda $01 + pha + lda #${bank} + sta $01 + lda ${argAddrArg.number.toHex()} + jsr ${address.toHex()} + sta ${argAddrArg.number.toHex()} + pla + sta $01""") + } + else -> throw AssemblyError("callrom only accepts pointer-of a (ubyte) variable or constant memory address for the 'arg' parameter") + } + } + } + private fun funcCmp(fcall: IFunctionCall) { val arg1 = fcall.args[0] val arg2 = fcall.args[1] diff --git a/docs/source/programming.rst b/docs/source/programming.rst index 8e4d3cc44..e5f610f6e 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -905,6 +905,20 @@ callfar(bank, address, argumentaddress) ; NOTE: specific to cx16 compiler t 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. + +callrom(bank, address, argumentaddress) ; NOTE: specific to cx16 compiler target for now + Calls an assembly routine in another rom-bank on the CommanderX16 + The banked ROM is located in the address range $C000-$FFFF (16 kilobyte). + There are 32 banks (0 to 31). + 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, ``callrom`` cannot be used + and you'll have to set up a call in assembly code yourself that handles the banking and + argument/returnvalues. Library routines diff --git a/examples/test.p8 b/examples/test.p8 index 76a117383..23839d7f8 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -16,6 +16,26 @@ main { txt.nl() callfar($01, $a000, &uu) txt.print_ub(uu) + + uu = '\n' + callrom($00, $ffd2, &uu) + uu = 'a' + callrom($00, $ffd2, &uu) + uu = '!' + callrom($00, $ffd2, &uu) + uu = '\n' + callrom($00, $ffd2, &uu) + +; cx16.rombank(0) +; %asm{{ +; lda #13 +; jsr $ffd2 +; lda #'a' +; jsr $ffd2 +; lda #13 +; jsr $ffd2 +; }} +; cx16.rombank(4) } } diff --git a/syntax-files/Vim/prog8_builtins.vim b/syntax-files/Vim/prog8_builtins.vim index 298e1073c..a5c2eb5e5 100644 --- a/syntax-files/Vim/prog8_builtins.vim +++ b/syntax-files/Vim/prog8_builtins.vim @@ -17,7 +17,7 @@ syn keyword prog8BuiltInFunc any all len max min reverse sum sort " Miscellaneous functions syn keyword prog8BuiltInFunc cmp lsb msb mkword peek peekw poke pokew rnd rndw syn keyword prog8BuiltInFunc rndf fastrnd8 rol rol2 ror ror2 sizeof offsetof -syn keyword prog8BuiltInFunc swap memory callfar +syn keyword prog8BuiltInFunc swap memory callfar callrom " c64/floats.p8