From 77e376f6bf1325563c6ef389b41e147ce456b3c2 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 5 Nov 2024 23:56:58 +0100 Subject: [PATCH] romsub @bank now also accepts a variable so the bank can be dynamic --- codeCore/src/prog8/code/SymbolTable.kt | 3 +- codeCore/src/prog8/code/ast/AstPrinter.kt | 6 +- codeCore/src/prog8/code/ast/AstStatements.kt | 7 ++- .../src/prog8/codegen/cpu6502/AsmGen.kt | 3 + .../codegen/cpu6502/FunctionCallAsmGen.kt | 60 +++++++++++++++++-- .../codegen/cpu6502/ProgramAndVarsGen.kt | 6 +- .../codegen/intermediate/ExpressionGen.kt | 10 ++-- codeGenIntermediate/test/TestVmCodeGen.kt | 2 +- .../compiler/astprocessing/AstChecker.kt | 13 +++- .../astprocessing/IntermediateAstMaker.kt | 5 +- .../src/prog8/ast/AstToSourceTextConverter.kt | 6 +- compilerAst/src/prog8/ast/SymbolDumper.kt | 6 +- .../src/prog8/ast/antlr/Antlr2Kotlin.kt | 6 +- .../prog8/ast/expressions/AstExpressions.kt | 2 +- .../src/prog8/ast/statements/AstStatements.kt | 21 ++++++- compilerAst/src/prog8/ast/walk/AstWalker.kt | 1 + compilerAst/src/prog8/ast/walk/IAstVisitor.kt | 1 + docs/source/comparing.rst | 2 +- docs/source/syntaxreference.rst | 2 +- docs/source/technical.rst | 6 +- docs/source/todo.rst | 2 - examples/cx16/banking/program.p8 | 2 + examples/test.p8 | 38 +++++++----- parser/antlr/Prog8ANTLR.g4 | 2 +- 24 files changed, 159 insertions(+), 53 deletions(-) diff --git a/codeCore/src/prog8/code/SymbolTable.kt b/codeCore/src/prog8/code/SymbolTable.kt index 835240dce..8b8222e3e 100644 --- a/codeCore/src/prog8/code/SymbolTable.kt +++ b/codeCore/src/prog8/code/SymbolTable.kt @@ -1,5 +1,6 @@ package prog8.code +import prog8.code.ast.PtAsmSub import prog8.code.ast.PtNode import prog8.code.ast.PtProgram import prog8.code.core.* @@ -257,7 +258,7 @@ class StSub(name: String, val parameters: List, val retur class StRomSub(name: String, - val address: Pair?, // null in case of asmsub, specified in case of romsub. bank, address. + val address: PtAsmSub.Address?, // null in case of asmsub, specified in case of romsub. val parameters: List, val returns: List, astNode: PtNode) : diff --git a/codeCore/src/prog8/code/ast/AstPrinter.kt b/codeCore/src/prog8/code/ast/AstPrinter.kt index 8cb5d82a9..75b97a559 100644 --- a/codeCore/src/prog8/code/ast/AstPrinter.kt +++ b/codeCore/src/prog8/code/ast/AstPrinter.kt @@ -92,8 +92,10 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni if(node.address == null) { str + "asmsub ${node.name}($params) $clobbers $returns" } else { - val bank = if(node.address.first!=null) "@bank ${node.address.first}" else "" - str + "romsub $bank ${node.address.second.toHex()} = ${node.name}($params) $clobbers $returns" + val bank = if(node.address.constbank!=null) "@bank ${node.address.constbank}" + else if(node.address.varbank!=null) "@bank ${node.address.varbank?.name}" + else "" + str + "romsub $bank ${node.address.address.toHex()} = ${node.name}($params) $clobbers $returns" } } is PtBlock -> { diff --git a/codeCore/src/prog8/code/ast/AstStatements.kt b/codeCore/src/prog8/code/ast/AstStatements.kt index a7dd6f346..3b5325444 100644 --- a/codeCore/src/prog8/code/ast/AstStatements.kt +++ b/codeCore/src/prog8/code/ast/AstStatements.kt @@ -10,13 +10,16 @@ sealed interface IPtSubroutine { class PtAsmSub( name: String, - val address: Pair?, // bank, address + val address: Address?, val clobbers: Set, val parameters: List>, val returns: List>, val inline: Boolean, position: Position -) : PtNamedNode(name, position), IPtSubroutine +) : PtNamedNode(name, position), IPtSubroutine { + + class Address(val constbank: UByte?, var varbank: PtIdentifier?, val address: UInt) +} class PtSub( diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index 991a05790..f3627b3b0 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -48,6 +48,9 @@ class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend { is PtAsmSub -> { prefixNamedNode(node) node.parameters.forEach { (_, param) -> prefixNamedNode(param) } + if(node.address?.varbank!=null) { + node.address!!.varbank = node.address!!.varbank!!.prefix(node, st) + } } is PtSub -> { prefixNamedNode(node) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt index b4d7980ee..a989593b2 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/FunctionCallAsmGen.kt @@ -40,30 +40,78 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as sub.children.forEach { asmgen.translate(it as PtInlineAssembly) } asmgen.out(" \t; inlined routine end: ${sub.name}") } else { - val bank = sub.address?.first - if(bank==null) - asmgen.out(" jsr $subAsmName") + val bank = sub.address?.constbank?.toString() + if(bank==null) { + val varbank = if(sub.address?.varbank==null) null else asmgen.asmVariableName(sub.address!!.varbank!!) + if(varbank!=null) { + when(asmgen.options.compTarget.name) { + "cx16" -> { + // JSRFAR can jump to a banked RAM address as well! + asmgen.out(""" + php + pha + lda $varbank + sta + + pla + plp + jsr cx16.JSRFAR + .word $subAsmName ; ${sub.address!!.address.toHex()} ++ .byte 0 ; modified""" + ) + } + "c64" -> { + asmgen.out(""" + php + pha + lda $varbank + sta + + pla + plp + jsr c64.x16jsrfar + .word $subAsmName ; ${sub.address!!.address.toHex()} ++ .byte 0 ; modified""" + ) + } + "c128" -> { + asmgen.out(""" + php + pha + lda $varbank + sta + + pla + plp + jsr c128.x16jsrfar + .word $subAsmName ; ${sub.address!!.address.toHex()} ++ .byte 0 ; modified""" + ) + } + else -> throw AssemblyError("callfar is not supported on the selected compilation target") + } + } else { + asmgen.out(" jsr $subAsmName") + } + } else { when(asmgen.options.compTarget.name) { "cx16" -> { // JSRFAR can jump to a banked RAM address as well! asmgen.out(""" jsr cx16.JSRFAR - .word $subAsmName ; ${sub.address!!.second.toHex()} + .word $subAsmName ; ${sub.address!!.address.toHex()} .byte $bank""" ) } "c64" -> { asmgen.out(""" jsr c64.x16jsrfar - .word $subAsmName ; ${sub.address!!.second.toHex()} + .word $subAsmName ; ${sub.address!!.address.toHex()} .byte $bank""" ) } "c128" -> { asmgen.out(""" jsr c128.x16jsrfar - .word $subAsmName ; ${sub.address!!.second.toHex()} + .word $subAsmName ; ${sub.address!!.address.toHex()} .byte $bank""" ) } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt index 53ed04948..1ae16c7b1 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt @@ -783,8 +783,10 @@ internal class ProgramAndVarsGen( .forEach { asmsub -> asmsub as PtAsmSub val address = asmsub.address!! - val bank = if(address.first!=null) "; @bank ${address.first}" else "" - asmgen.out(" ${asmsub.name} = ${address.second.toHex()} $bank") + val bank = if(address.constbank!=null) "; @bank ${address.constbank}" + else if(address.varbank!=null) "; @bank ${address.varbank?.name}" + else "" + asmgen.out(" ${asmsub.name} = ${address.address.toHex()} $bank") } } diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt index 87ba4be88..d691b835c 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt @@ -645,14 +645,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegs)) else { val address = callTarget.address!! - if(address.first==null) { + if(address.constbank==null && address.varbank==null) { IRInstruction( Opcode.CALL, - address = address.second.toInt(), + address = address.address.toInt(), fcallArgs = FunctionCallArgs(argRegisters, returnRegs)) } else { - TODO("callfar is not implemented for the selected compilation target") + TODO("callfar into another bank is not implemented for the selected compilation target") } } addInstr(result, call, null) @@ -767,10 +767,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegisters)) else { val address = callTarget.address!! - if(address.first==null) { + if(address.constbank==null && address.varbank==null) { IRInstruction( Opcode.CALL, - address = address.second.toInt(), + address = address.address.toInt(), fcallArgs = FunctionCallArgs(argRegisters, returnRegisters) ) } diff --git a/codeGenIntermediate/test/TestVmCodeGen.kt b/codeGenIntermediate/test/TestVmCodeGen.kt index 0a6ed57dd..a896efb09 100644 --- a/codeGenIntermediate/test/TestVmCodeGen.kt +++ b/codeGenIntermediate/test/TestVmCodeGen.kt @@ -534,7 +534,7 @@ class TestVmCodeGen: FunSpec({ val codegen = VmCodeGen() val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY) - val romsub = PtAsmSub("routine", null to 0x5000u, setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY) + val romsub = PtAsmSub("routine", PtAsmSub.Address(null, null, 0x5000u), setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY) block.add(romsub) val sub = PtSub("start", emptyList(), null, Position.DUMMY) val call = PtFunctionCall("main.routine", true, DataType.UNDEFINED, Position.DUMMY) diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index a42e5e772..d7b52f74a 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -377,9 +377,16 @@ internal class AstChecker(private val program: Program, if(uniqueNames.size!=subroutine.parameters.size) err("parameter names must be unique") - val bank = subroutine.asmAddress?.first - if(bank!=null && bank>255u) - err("bank must be 0 to 255") + val bank = subroutine.asmAddress?.constbank + if (bank!=null) { + if (bank > 255u) err("bank must be 0 to 255") + if (subroutine.asmAddress?.varbank!=null) throw FatalAstException("need either constant or variable bank") + } + val varbank = subroutine.asmAddress?.varbank + if(varbank!=null) { + if(varbank.targetVarDecl(program)?.datatype!=DataType.UBYTE) + err("bank variable must be ubyte") + } if(subroutine.inline && subroutine.asmAddress!=null) throw FatalAstException("romsub cannot be inline") diff --git a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt index 1eca43a5a..639b306dd 100644 --- a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt +++ b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt @@ -466,7 +466,8 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr private fun transformAsmSub(srcSub: Subroutine): PtAsmSub { val params = srcSub.asmParameterRegisters.zip(srcSub.parameters.map { PtSubroutineParameter(it.name, it.type, it.position) }) - val asmAddr = if(srcSub.asmAddress==null) null else srcSub.asmAddress!!.first to srcSub.asmAddress!!.second + val varbank = if(srcSub.asmAddress?.varbank==null) null else transform(srcSub.asmAddress!!.varbank!!) + val asmAddr = if(srcSub.asmAddress==null) null else PtAsmSub.Address(srcSub.asmAddress!!.constbank, varbank, srcSub.asmAddress!!.address) val sub = PtAsmSub(srcSub.name, asmAddr, srcSub.asmClobbers, @@ -475,6 +476,8 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr srcSub.inline, srcSub.position) sub.parameters.forEach { it.second.parent=sub } + if(varbank!=null) + asmAddr?.varbank?.parent = sub if(srcSub.asmAddress==null) { var combinedTrueAsm = "" diff --git a/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt b/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt index 567da5c61..08b8c10c3 100644 --- a/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt +++ b/compilerAst/src/prog8/ast/AstToSourceTextConverter.kt @@ -181,8 +181,10 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program: output("inline ") if(subroutine.isAsmSubroutine) { if(subroutine.asmAddress!=null) { - val bank = if(subroutine.asmAddress.first!=null) "@bank ${subroutine.asmAddress.first}" else "" - output("romsub $bank ${subroutine.asmAddress.second.toHex()} = ${subroutine.name} (") + val bank = if(subroutine.asmAddress.constbank!=null) "@bank ${subroutine.asmAddress.constbank}" + else if(subroutine.asmAddress.varbank!=null) "@bank ${subroutine.asmAddress.varbank?.nameInSource?.joinToString(".")}" + else "" + output("romsub $bank ${subroutine.asmAddress.address.toHex()} = ${subroutine.name} (") } else output("asmsub ${subroutine.name} (") diff --git a/compilerAst/src/prog8/ast/SymbolDumper.kt b/compilerAst/src/prog8/ast/SymbolDumper.kt index 11a66b4c1..e8d519a3f 100644 --- a/compilerAst/src/prog8/ast/SymbolDumper.kt +++ b/compilerAst/src/prog8/ast/SymbolDumper.kt @@ -173,8 +173,10 @@ private class SymbolDumper(val skipLibraries: Boolean): IAstVisitor { } } if(subroutine.asmAddress!=null) { - val bank = if(subroutine.asmAddress.first!=null) "@bank ${subroutine.asmAddress.first}" else "" - output("$bank = ${subroutine.asmAddress.second.toHex()}") + val bank = if(subroutine.asmAddress.constbank!=null) "@bank ${subroutine.asmAddress.constbank}" + else if(subroutine.asmAddress.varbank!=null) "@bank ${subroutine.asmAddress.varbank?.nameInSource?.joinToString(".")}" + else "" + output("$bank = ${subroutine.asmAddress.address.toHex()}") } output("\n") diff --git a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt index 37a69feed..3c8bcb18d 100644 --- a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt +++ b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt @@ -177,11 +177,13 @@ private fun AsmsubroutineContext.toAst(): Subroutine { private fun RomsubroutineContext.toAst(): Subroutine { val subdecl = asmsub_decl().toAst() - val bank = bank?.toAst()?.number?.toUInt()?.toUByte() + val constbank = constbank?.toAst()?.number?.toUInt()?.toUByte() + val varbank = varbank?.toAst() val addr = address.toAst().number.toUInt() + val address = Subroutine.Address(constbank, varbank, addr) return Subroutine(subdecl.name, subdecl.parameters.toMutableList(), subdecl.returntypes.toMutableList(), subdecl.asmParameterRegisters, subdecl.asmReturnvaluesRegisters, - subdecl.asmClobbers, bank to addr, true, inline = false, statements = mutableListOf(), position = toPosition() + subdecl.asmClobbers, address, true, inline = false, statements = mutableListOf(), position = toPosition() ) } diff --git a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt index 05b1b2e9a..c5eec7e2d 100644 --- a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt +++ b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt @@ -442,7 +442,7 @@ data class AddressOf(var identifier: IdentifierReference, var arrayIndex: ArrayI } val targetAsmAddress = (target as? Subroutine)?.asmAddress if(targetAsmAddress!=null) { - return NumericLiteral(DataType.UWORD, targetAsmAddress.second.toDouble(), position) + return NumericLiteral(DataType.UWORD, targetAsmAddress.address.toDouble(), position) } return null } diff --git a/compilerAst/src/prog8/ast/statements/AstStatements.kt b/compilerAst/src/prog8/ast/statements/AstStatements.kt index 6a67f03d4..0ca05fe10 100644 --- a/compilerAst/src/prog8/ast/statements/AstStatements.kt +++ b/compilerAst/src/prog8/ast/statements/AstStatements.kt @@ -806,7 +806,7 @@ class Subroutine(override val name: String, val asmParameterRegisters: List, val asmReturnvaluesRegisters: List, val asmClobbers: Set, - val asmAddress: Pair?, // bank, address + val asmAddress: Address?, val isAsmSubroutine: Boolean, var inline: Boolean, var hasBeenInlined: Boolean=false, @@ -818,6 +818,7 @@ class Subroutine(override val name: String, override fun linkParents(parent: Node) { this.parent = parent + this.asmAddress?.varbank?.linkParents(this) parameters.forEach { it.linkParents(this) } statements.forEach { it.linkParents(this) } } @@ -834,11 +835,18 @@ class Subroutine(override val name: String, statements[idx] = replacement replacement.parent = this } + is NumericLiteral -> { + if(node===asmAddress?.varbank) { + asmAddress.constbank = replacement.number.toInt().toUByte() + asmAddress.varbank = null + } else throw FatalAstException("can't replace") + } else -> throw FatalAstException("can't replace") } } override fun referencesIdentifier(nameInSource: List): Boolean = + asmAddress?.varbank?.referencesIdentifier(nameInSource)==true || statements.any { it.referencesIdentifier(nameInSource) } || parameters.any { it.referencesIdentifier(nameInSource) } @@ -846,6 +854,17 @@ class Subroutine(override val name: String, override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) override fun toString() = "Subroutine(name=$name, parameters=$parameters, returntypes=$returntypes, ${statements.size} statements, address=$asmAddress)" + + class Address(var constbank: UByte?, var varbank: IdentifierReference?, val address: UInt) { + override fun toString(): String { + if(constbank!=null) + return "$constbank:${address.toHex()}" + else if(varbank!=null) + return "${varbank?.nameInSource?.joinToString(".")}:${address.toHex()}" + else + return address.toHex() + } + } } open class SubroutineParameter(val name: String, diff --git a/compilerAst/src/prog8/ast/walk/AstWalker.kt b/compilerAst/src/prog8/ast/walk/AstWalker.kt index 829a131a0..da25ffc33 100644 --- a/compilerAst/src/prog8/ast/walk/AstWalker.kt +++ b/compilerAst/src/prog8/ast/walk/AstWalker.kt @@ -276,6 +276,7 @@ abstract class AstWalker { fun visit(subroutine: Subroutine, parent: Node) { track(before(subroutine, parent), subroutine, parent) + subroutine.asmAddress?.varbank?.accept(this, subroutine) subroutine.statements.forEach { it.accept(this, subroutine) } track(after(subroutine, parent), subroutine, parent) } diff --git a/compilerAst/src/prog8/ast/walk/IAstVisitor.kt b/compilerAst/src/prog8/ast/walk/IAstVisitor.kt index f1e3f3f99..1c04ffc27 100644 --- a/compilerAst/src/prog8/ast/walk/IAstVisitor.kt +++ b/compilerAst/src/prog8/ast/walk/IAstVisitor.kt @@ -45,6 +45,7 @@ interface IAstVisitor { } fun visit(subroutine: Subroutine) { + subroutine.asmAddress?.varbank?.accept(this) subroutine.statements.forEach { it.accept(this) } } diff --git a/docs/source/comparing.rst b/docs/source/comparing.rst index 286390b89..20862272b 100644 --- a/docs/source/comparing.rst +++ b/docs/source/comparing.rst @@ -81,7 +81,7 @@ Foreign function interface (external/ROM calls) ----------------------------------------------- - You can use the ``romsub`` keyword to define the call signature of foreign functions (usually ROM routines, hence the name) in a natural way. Calling those generates code that is as efficient or even more efficient as calling regular subroutines. - No additional stubs are needed. (unless there is bank switching going on, but this *may* be improved in a future language version) + No additional stubs are needed. You can even specify the memory bank the routine is in and the compiler takes care of bank switching when calling it. Optimizations ------------- diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index 59281f80d..7223a9a40 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -794,7 +794,7 @@ and returning stuff in several registers as well. The ``clobbers`` clause is use what CPU registers are clobbered by the call instead of being unchanged or returning a meaningful result value. **Banks:** it is possible to declare a non-standard ROM or RAM bank that the routine is living in, with ``@bank`` like this: -``romsub @bank 10 $C09F = audio_init()`` to define a routine at $C09F in bank 10. +``romsub @bank 10 $C09F = audio_init()`` to define a routine at $C09F in bank 10. You can also specify a variable for the bank. See :ref:`banking` for more information. .. note:: diff --git a/docs/source/technical.rst b/docs/source/technical.rst index 5a5da26aa..c10ff2ee1 100644 --- a/docs/source/technical.rst +++ b/docs/source/technical.rst @@ -51,9 +51,11 @@ The compiler will then transparently change a call to this routine so that the c automatically before the normal jump to the subroutine (and switched back on return). The programmer doesn't have to bother anymore with setting/resetting the banks manually, or having the program crash because the routine is called in the wrong bank! You define such a routine by adding ``@bank `` -to the romsub subroutine definition. This specifies the bank number where the subroutine is located in:: +to the romsub subroutine definition. This specifies the bank number where the subroutine is located in. +You can use a constant bank number 0-255, or a ubyte variable to make it dynamic:: romsub @bank 10 $C09F = audio_init() + romsub @bank banknr $A000 = first_hiram_routine() When you then call this routine in your program as usual, the compiler will no longer generate a simple JSR instruction to the routine. Instead it will generate a piece of code that automatically switches the ROM or RAM bank to the @@ -64,7 +66,7 @@ a similar call exists (but requires a lot more code to prepare, so beware). On the Commodore 64 some custom code is also emitted that toggle the banks, retains some registers, and does the call. Other compilation targets don't have banking or prog8 doesn't yet support automatic bank selection on them. -There's a "banking" (not financial) example for the Commander X16 that shows a possible application +There's a "banking" example for the Commander X16 that shows a possible application of the romsub with bank support, check out the `bank example code `_ . diff --git a/docs/source/todo.rst b/docs/source/todo.rst index a0c5932b9..bd28fb5a0 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,8 +1,6 @@ TODO ==== -make @bank accept a variable as well to make it dynamic - rename 'romsub' to 'extsub' ? keep romsub as alias? for releasenotes: gfx2.width and gfx2.height got renamed as gfx_lores.WIDTH/HEIGHT or gfx_hires4.WIDTH/HEIGTH constants. Screen mode routines also renamed. diff --git a/examples/cx16/banking/program.p8 b/examples/cx16/banking/program.p8 index e90a20c0f..6a32653f9 100644 --- a/examples/cx16/banking/program.p8 +++ b/examples/cx16/banking/program.p8 @@ -12,6 +12,8 @@ main { sub start() { ; load the example libraries in hiram banks 4 and 5 + ; in this example these are constants, but you can also specify + ; a variable for the bank so you can vary the bank where the routine is loaded. cx16.rambank(4) void diskio.load("library1.prg", $a000) cx16.rambank(5) diff --git a/examples/test.p8 b/examples/test.p8 index 2c47c9558..9bcc27d11 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,23 +1,31 @@ %import textio %zeropage basicsafe +%option no_sysinit main { - sub start() { - basic_area.routine1() - } -} + ubyte bank -basic_area $a000 { - sub routine1() { - txt.print("hello from basic rom area ") - txt.print_uwhex(&routine1, true) + romsub @bank bank $a000 = routine_in_hiram(uword arg @AY) -> uword @AY + + sub start() { + ; copy the routine into hiram bank 8 + bank = 8 + cx16.rambank(bank) + sys.memcopy(&the_increment_routine, $a000, 255) + cx16.rambank(1) + + txt.print("incremented by one=") + txt.print_uw(routine_in_hiram(37119)) txt.nl() } -} -;hiram_area $c000 { -; %option force_output -; sub thing() { -; cx16.r0++ -; } -;} + asmsub the_increment_routine(uword arg @AY) -> uword @AY { + %asm {{ + clc + adc #1 + bcc + + iny ++ rts + }} + } +} diff --git a/parser/antlr/Prog8ANTLR.g4 b/parser/antlr/Prog8ANTLR.g4 index 97116a6dc..aac97d63c 100644 --- a/parser/antlr/Prog8ANTLR.g4 +++ b/parser/antlr/Prog8ANTLR.g4 @@ -294,7 +294,7 @@ asmsubroutine : ; romsubroutine : - 'romsub' ('@bank' bank=integerliteral)? address=integerliteral '=' asmsub_decl + 'romsub' ('@bank' (constbank=integerliteral | varbank=scoped_identifier))? address=integerliteral '=' asmsub_decl ; asmsub_decl : identifier '(' asmsub_params? ')' asmsub_clobbers? asmsub_returns? ;