mirror of
https://github.com/irmen/prog8.git
synced 2025-01-04 12:30:52 +00:00
romsub @bank now also accepts a variable so the bank can be dynamic
This commit is contained in:
parent
491e5dbcfb
commit
77e376f6bf
@ -1,5 +1,6 @@
|
|||||||
package prog8.code
|
package prog8.code
|
||||||
|
|
||||||
|
import prog8.code.ast.PtAsmSub
|
||||||
import prog8.code.ast.PtNode
|
import prog8.code.ast.PtNode
|
||||||
import prog8.code.ast.PtProgram
|
import prog8.code.ast.PtProgram
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
@ -257,7 +258,7 @@ class StSub(name: String, val parameters: List<StSubroutineParameter>, val retur
|
|||||||
|
|
||||||
|
|
||||||
class StRomSub(name: String,
|
class StRomSub(name: String,
|
||||||
val address: Pair<UByte?, UInt>?, // 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<StRomSubParameter>,
|
val parameters: List<StRomSubParameter>,
|
||||||
val returns: List<StRomSubParameter>,
|
val returns: List<StRomSubParameter>,
|
||||||
astNode: PtNode) :
|
astNode: PtNode) :
|
||||||
|
@ -92,8 +92,10 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
|||||||
if(node.address == null) {
|
if(node.address == null) {
|
||||||
str + "asmsub ${node.name}($params) $clobbers $returns"
|
str + "asmsub ${node.name}($params) $clobbers $returns"
|
||||||
} else {
|
} else {
|
||||||
val bank = if(node.address.first!=null) "@bank ${node.address.first}" else ""
|
val bank = if(node.address.constbank!=null) "@bank ${node.address.constbank}"
|
||||||
str + "romsub $bank ${node.address.second.toHex()} = ${node.name}($params) $clobbers $returns"
|
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 -> {
|
is PtBlock -> {
|
||||||
|
@ -10,13 +10,16 @@ sealed interface IPtSubroutine {
|
|||||||
|
|
||||||
class PtAsmSub(
|
class PtAsmSub(
|
||||||
name: String,
|
name: String,
|
||||||
val address: Pair<UByte?, UInt>?, // bank, address
|
val address: Address?,
|
||||||
val clobbers: Set<CpuRegister>,
|
val clobbers: Set<CpuRegister>,
|
||||||
val parameters: List<Pair<RegisterOrStatusflag, PtSubroutineParameter>>,
|
val parameters: List<Pair<RegisterOrStatusflag, PtSubroutineParameter>>,
|
||||||
val returns: List<Pair<RegisterOrStatusflag, DataType>>,
|
val returns: List<Pair<RegisterOrStatusflag, DataType>>,
|
||||||
val inline: Boolean,
|
val inline: Boolean,
|
||||||
position: Position
|
position: Position
|
||||||
) : PtNamedNode(name, position), IPtSubroutine
|
) : PtNamedNode(name, position), IPtSubroutine {
|
||||||
|
|
||||||
|
class Address(val constbank: UByte?, var varbank: PtIdentifier?, val address: UInt)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class PtSub(
|
class PtSub(
|
||||||
|
@ -48,6 +48,9 @@ class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
|
|||||||
is PtAsmSub -> {
|
is PtAsmSub -> {
|
||||||
prefixNamedNode(node)
|
prefixNamedNode(node)
|
||||||
node.parameters.forEach { (_, param) -> prefixNamedNode(param) }
|
node.parameters.forEach { (_, param) -> prefixNamedNode(param) }
|
||||||
|
if(node.address?.varbank!=null) {
|
||||||
|
node.address!!.varbank = node.address!!.varbank!!.prefix(node, st)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
is PtSub -> {
|
is PtSub -> {
|
||||||
prefixNamedNode(node)
|
prefixNamedNode(node)
|
||||||
|
@ -40,30 +40,78 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
|||||||
sub.children.forEach { asmgen.translate(it as PtInlineAssembly) }
|
sub.children.forEach { asmgen.translate(it as PtInlineAssembly) }
|
||||||
asmgen.out(" \t; inlined routine end: ${sub.name}")
|
asmgen.out(" \t; inlined routine end: ${sub.name}")
|
||||||
} else {
|
} else {
|
||||||
val bank = sub.address?.first
|
val bank = sub.address?.constbank?.toString()
|
||||||
if(bank==null)
|
if(bank==null) {
|
||||||
asmgen.out(" jsr $subAsmName")
|
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 {
|
else {
|
||||||
when(asmgen.options.compTarget.name) {
|
when(asmgen.options.compTarget.name) {
|
||||||
"cx16" -> {
|
"cx16" -> {
|
||||||
// JSRFAR can jump to a banked RAM address as well!
|
// JSRFAR can jump to a banked RAM address as well!
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
jsr cx16.JSRFAR
|
jsr cx16.JSRFAR
|
||||||
.word $subAsmName ; ${sub.address!!.second.toHex()}
|
.word $subAsmName ; ${sub.address!!.address.toHex()}
|
||||||
.byte $bank"""
|
.byte $bank"""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
"c64" -> {
|
"c64" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
jsr c64.x16jsrfar
|
jsr c64.x16jsrfar
|
||||||
.word $subAsmName ; ${sub.address!!.second.toHex()}
|
.word $subAsmName ; ${sub.address!!.address.toHex()}
|
||||||
.byte $bank"""
|
.byte $bank"""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
"c128" -> {
|
"c128" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
jsr c128.x16jsrfar
|
jsr c128.x16jsrfar
|
||||||
.word $subAsmName ; ${sub.address!!.second.toHex()}
|
.word $subAsmName ; ${sub.address!!.address.toHex()}
|
||||||
.byte $bank"""
|
.byte $bank"""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -783,8 +783,10 @@ internal class ProgramAndVarsGen(
|
|||||||
.forEach { asmsub ->
|
.forEach { asmsub ->
|
||||||
asmsub as PtAsmSub
|
asmsub as PtAsmSub
|
||||||
val address = asmsub.address!!
|
val address = asmsub.address!!
|
||||||
val bank = if(address.first!=null) "; @bank ${address.first}" else ""
|
val bank = if(address.constbank!=null) "; @bank ${address.constbank}"
|
||||||
asmgen.out(" ${asmsub.name} = ${address.second.toHex()} $bank")
|
else if(address.varbank!=null) "; @bank ${address.varbank?.name}"
|
||||||
|
else ""
|
||||||
|
asmgen.out(" ${asmsub.name} = ${address.address.toHex()} $bank")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -645,14 +645,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegs))
|
IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegs))
|
||||||
else {
|
else {
|
||||||
val address = callTarget.address!!
|
val address = callTarget.address!!
|
||||||
if(address.first==null) {
|
if(address.constbank==null && address.varbank==null) {
|
||||||
IRInstruction(
|
IRInstruction(
|
||||||
Opcode.CALL,
|
Opcode.CALL,
|
||||||
address = address.second.toInt(),
|
address = address.address.toInt(),
|
||||||
fcallArgs = FunctionCallArgs(argRegisters, returnRegs))
|
fcallArgs = FunctionCallArgs(argRegisters, returnRegs))
|
||||||
}
|
}
|
||||||
else {
|
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)
|
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))
|
IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegisters))
|
||||||
else {
|
else {
|
||||||
val address = callTarget.address!!
|
val address = callTarget.address!!
|
||||||
if(address.first==null) {
|
if(address.constbank==null && address.varbank==null) {
|
||||||
IRInstruction(
|
IRInstruction(
|
||||||
Opcode.CALL,
|
Opcode.CALL,
|
||||||
address = address.second.toInt(),
|
address = address.address.toInt(),
|
||||||
fcallArgs = FunctionCallArgs(argRegisters, returnRegisters)
|
fcallArgs = FunctionCallArgs(argRegisters, returnRegisters)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -534,7 +534,7 @@ class TestVmCodeGen: FunSpec({
|
|||||||
val codegen = VmCodeGen()
|
val codegen = VmCodeGen()
|
||||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||||
val block = PtBlock("main", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
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)
|
block.add(romsub)
|
||||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||||
val call = PtFunctionCall("main.routine", true, DataType.UNDEFINED, Position.DUMMY)
|
val call = PtFunctionCall("main.routine", true, DataType.UNDEFINED, Position.DUMMY)
|
||||||
|
@ -377,9 +377,16 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(uniqueNames.size!=subroutine.parameters.size)
|
if(uniqueNames.size!=subroutine.parameters.size)
|
||||||
err("parameter names must be unique")
|
err("parameter names must be unique")
|
||||||
|
|
||||||
val bank = subroutine.asmAddress?.first
|
val bank = subroutine.asmAddress?.constbank
|
||||||
if(bank!=null && bank>255u)
|
if (bank!=null) {
|
||||||
err("bank must be 0 to 255")
|
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)
|
if(subroutine.inline && subroutine.asmAddress!=null)
|
||||||
throw FatalAstException("romsub cannot be inline")
|
throw FatalAstException("romsub cannot be inline")
|
||||||
|
|
||||||
|
@ -466,7 +466,8 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
|
|||||||
|
|
||||||
private fun transformAsmSub(srcSub: Subroutine): PtAsmSub {
|
private fun transformAsmSub(srcSub: Subroutine): PtAsmSub {
|
||||||
val params = srcSub.asmParameterRegisters.zip(srcSub.parameters.map { PtSubroutineParameter(it.name, it.type, it.position) })
|
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,
|
val sub = PtAsmSub(srcSub.name,
|
||||||
asmAddr,
|
asmAddr,
|
||||||
srcSub.asmClobbers,
|
srcSub.asmClobbers,
|
||||||
@ -475,6 +476,8 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
|
|||||||
srcSub.inline,
|
srcSub.inline,
|
||||||
srcSub.position)
|
srcSub.position)
|
||||||
sub.parameters.forEach { it.second.parent=sub }
|
sub.parameters.forEach { it.second.parent=sub }
|
||||||
|
if(varbank!=null)
|
||||||
|
asmAddr?.varbank?.parent = sub
|
||||||
|
|
||||||
if(srcSub.asmAddress==null) {
|
if(srcSub.asmAddress==null) {
|
||||||
var combinedTrueAsm = ""
|
var combinedTrueAsm = ""
|
||||||
|
@ -181,8 +181,10 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
|
|||||||
output("inline ")
|
output("inline ")
|
||||||
if(subroutine.isAsmSubroutine) {
|
if(subroutine.isAsmSubroutine) {
|
||||||
if(subroutine.asmAddress!=null) {
|
if(subroutine.asmAddress!=null) {
|
||||||
val bank = if(subroutine.asmAddress.first!=null) "@bank ${subroutine.asmAddress.first}" else ""
|
val bank = if(subroutine.asmAddress.constbank!=null) "@bank ${subroutine.asmAddress.constbank}"
|
||||||
output("romsub $bank ${subroutine.asmAddress.second.toHex()} = ${subroutine.name} (")
|
else if(subroutine.asmAddress.varbank!=null) "@bank ${subroutine.asmAddress.varbank?.nameInSource?.joinToString(".")}"
|
||||||
|
else ""
|
||||||
|
output("romsub $bank ${subroutine.asmAddress.address.toHex()} = ${subroutine.name} (")
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
output("asmsub ${subroutine.name} (")
|
output("asmsub ${subroutine.name} (")
|
||||||
|
@ -173,8 +173,10 @@ private class SymbolDumper(val skipLibraries: Boolean): IAstVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(subroutine.asmAddress!=null) {
|
if(subroutine.asmAddress!=null) {
|
||||||
val bank = if(subroutine.asmAddress.first!=null) "@bank ${subroutine.asmAddress.first}" else ""
|
val bank = if(subroutine.asmAddress.constbank!=null) "@bank ${subroutine.asmAddress.constbank}"
|
||||||
output("$bank = ${subroutine.asmAddress.second.toHex()}")
|
else if(subroutine.asmAddress.varbank!=null) "@bank ${subroutine.asmAddress.varbank?.nameInSource?.joinToString(".")}"
|
||||||
|
else ""
|
||||||
|
output("$bank = ${subroutine.asmAddress.address.toHex()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
output("\n")
|
output("\n")
|
||||||
|
@ -177,11 +177,13 @@ private fun AsmsubroutineContext.toAst(): Subroutine {
|
|||||||
|
|
||||||
private fun RomsubroutineContext.toAst(): Subroutine {
|
private fun RomsubroutineContext.toAst(): Subroutine {
|
||||||
val subdecl = asmsub_decl().toAst()
|
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 addr = address.toAst().number.toUInt()
|
||||||
|
val address = Subroutine.Address(constbank, varbank, addr)
|
||||||
return Subroutine(subdecl.name, subdecl.parameters.toMutableList(), subdecl.returntypes.toMutableList(),
|
return Subroutine(subdecl.name, subdecl.parameters.toMutableList(), subdecl.returntypes.toMutableList(),
|
||||||
subdecl.asmParameterRegisters, subdecl.asmReturnvaluesRegisters,
|
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()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,7 +442,7 @@ data class AddressOf(var identifier: IdentifierReference, var arrayIndex: ArrayI
|
|||||||
}
|
}
|
||||||
val targetAsmAddress = (target as? Subroutine)?.asmAddress
|
val targetAsmAddress = (target as? Subroutine)?.asmAddress
|
||||||
if(targetAsmAddress!=null) {
|
if(targetAsmAddress!=null) {
|
||||||
return NumericLiteral(DataType.UWORD, targetAsmAddress.second.toDouble(), position)
|
return NumericLiteral(DataType.UWORD, targetAsmAddress.address.toDouble(), position)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -806,7 +806,7 @@ class Subroutine(override val name: String,
|
|||||||
val asmParameterRegisters: List<RegisterOrStatusflag>,
|
val asmParameterRegisters: List<RegisterOrStatusflag>,
|
||||||
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
|
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
|
||||||
val asmClobbers: Set<CpuRegister>,
|
val asmClobbers: Set<CpuRegister>,
|
||||||
val asmAddress: Pair<UByte?, UInt>?, // bank, address
|
val asmAddress: Address?,
|
||||||
val isAsmSubroutine: Boolean,
|
val isAsmSubroutine: Boolean,
|
||||||
var inline: Boolean,
|
var inline: Boolean,
|
||||||
var hasBeenInlined: Boolean=false,
|
var hasBeenInlined: Boolean=false,
|
||||||
@ -818,6 +818,7 @@ class Subroutine(override val name: String,
|
|||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
|
this.asmAddress?.varbank?.linkParents(this)
|
||||||
parameters.forEach { it.linkParents(this) }
|
parameters.forEach { it.linkParents(this) }
|
||||||
statements.forEach { it.linkParents(this) }
|
statements.forEach { it.linkParents(this) }
|
||||||
}
|
}
|
||||||
@ -834,11 +835,18 @@ class Subroutine(override val name: String,
|
|||||||
statements[idx] = replacement
|
statements[idx] = replacement
|
||||||
replacement.parent = this
|
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")
|
else -> throw FatalAstException("can't replace")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun referencesIdentifier(nameInSource: List<String>): Boolean =
|
override fun referencesIdentifier(nameInSource: List<String>): Boolean =
|
||||||
|
asmAddress?.varbank?.referencesIdentifier(nameInSource)==true ||
|
||||||
statements.any { it.referencesIdentifier(nameInSource) } ||
|
statements.any { it.referencesIdentifier(nameInSource) } ||
|
||||||
parameters.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 accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
override fun toString() =
|
override fun toString() =
|
||||||
"Subroutine(name=$name, parameters=$parameters, returntypes=$returntypes, ${statements.size} statements, address=$asmAddress)"
|
"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,
|
open class SubroutineParameter(val name: String,
|
||||||
|
@ -276,6 +276,7 @@ abstract class AstWalker {
|
|||||||
|
|
||||||
fun visit(subroutine: Subroutine, parent: Node) {
|
fun visit(subroutine: Subroutine, parent: Node) {
|
||||||
track(before(subroutine, parent), subroutine, parent)
|
track(before(subroutine, parent), subroutine, parent)
|
||||||
|
subroutine.asmAddress?.varbank?.accept(this, subroutine)
|
||||||
subroutine.statements.forEach { it.accept(this, subroutine) }
|
subroutine.statements.forEach { it.accept(this, subroutine) }
|
||||||
track(after(subroutine, parent), subroutine, parent)
|
track(after(subroutine, parent), subroutine, parent)
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@ interface IAstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun visit(subroutine: Subroutine) {
|
fun visit(subroutine: Subroutine) {
|
||||||
|
subroutine.asmAddress?.varbank?.accept(this)
|
||||||
subroutine.statements.forEach { it.accept(this) }
|
subroutine.statements.forEach { it.accept(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
- 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.
|
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
|
Optimizations
|
||||||
-------------
|
-------------
|
||||||
|
@ -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.
|
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:
|
**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.
|
See :ref:`banking` for more information.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
@ -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
|
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
|
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 <bank>``
|
the routine is called in the wrong bank! You define such a routine by adding ``@bank <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 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
|
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
|
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.
|
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.
|
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 <https://github.com/irmen/prog8/tree/master/examples/cx16/banking>`_ .
|
of the romsub with bank support, check out the `bank example code <https://github.com/irmen/prog8/tree/master/examples/cx16/banking>`_ .
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
TODO
|
TODO
|
||||||
====
|
====
|
||||||
|
|
||||||
make @bank accept a variable as well to make it dynamic
|
|
||||||
|
|
||||||
rename 'romsub' to 'extsub' ? keep romsub as alias?
|
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.
|
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.
|
||||||
|
@ -12,6 +12,8 @@ main {
|
|||||||
sub start() {
|
sub start() {
|
||||||
|
|
||||||
; load the example libraries in hiram banks 4 and 5
|
; 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)
|
cx16.rambank(4)
|
||||||
void diskio.load("library1.prg", $a000)
|
void diskio.load("library1.prg", $a000)
|
||||||
cx16.rambank(5)
|
cx16.rambank(5)
|
||||||
|
@ -1,23 +1,31 @@
|
|||||||
%import textio
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
%option no_sysinit
|
||||||
|
|
||||||
main {
|
main {
|
||||||
sub start() {
|
ubyte bank
|
||||||
basic_area.routine1()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
basic_area $a000 {
|
romsub @bank bank $a000 = routine_in_hiram(uword arg @AY) -> uword @AY
|
||||||
sub routine1() {
|
|
||||||
txt.print("hello from basic rom area ")
|
sub start() {
|
||||||
txt.print_uwhex(&routine1, true)
|
; 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()
|
txt.nl()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
;hiram_area $c000 {
|
asmsub the_increment_routine(uword arg @AY) -> uword @AY {
|
||||||
; %option force_output
|
%asm {{
|
||||||
; sub thing() {
|
clc
|
||||||
; cx16.r0++
|
adc #1
|
||||||
; }
|
bcc +
|
||||||
;}
|
iny
|
||||||
|
+ rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -294,7 +294,7 @@ asmsubroutine :
|
|||||||
;
|
;
|
||||||
|
|
||||||
romsubroutine :
|
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? ;
|
asmsub_decl : identifier '(' asmsub_params? ')' asmsub_clobbers? asmsub_returns? ;
|
||||||
|
Loading…
Reference in New Issue
Block a user