allow "goto pointervar" for indirect jumps

This commit is contained in:
Irmen de Jong 2022-01-21 22:46:10 +01:00
parent c8bd57cd4d
commit 9219ec539d
8 changed files with 129 additions and 28 deletions

View File

@ -875,7 +875,10 @@ class AsmGen(private val program: Program,
}
}
is Assignment -> assignmentAsmGen.translate(stmt)
is Jump -> jmp(getJumpTarget(stmt))
is Jump -> {
val (asmLabel, indirect) = getJumpTarget(stmt)
jmp(asmLabel, indirect)
}
is GoSub -> translate(stmt)
is PostIncrDecr -> postincrdecrAsmGen.translate(stmt)
is Label -> translate(stmt)
@ -1563,7 +1566,17 @@ $repeatLabel lda $counterVar
if(jump!=null) {
// branch with only a jump (goto)
val instruction = branchInstruction(stmt.condition, false)
out(" $instruction ${getJumpTarget(jump)}")
val (asmLabel, indirect) = getJumpTarget(jump)
if(indirect) {
val complementedInstruction = branchInstruction(stmt.condition, true)
out("""
$complementedInstruction +
jmp ($asmLabel)
+""")
}
else {
out(" $instruction $asmLabel")
}
translate(stmt.elsepart)
} else {
if(stmt.elsepart.isEmpty()) {
@ -1638,15 +1651,22 @@ $repeatLabel lda $counterVar
}
}
private fun getJumpTarget(jump: Jump): String {
private fun getJumpTarget(jump: Jump): Pair<String, Boolean> {
val ident = jump.identifier
val label = jump.generatedLabel
val addr = jump.address
return when {
ident!=null -> asmSymbolName(ident)
label!=null -> label
addr!=null -> addr.toHex()
else -> "????"
ident!=null -> {
// can be a label, or a pointer variable
val target = ident.targetVarDecl(program)
if(target!=null)
Pair(asmSymbolName(ident), true) // indirect
else
Pair(asmSymbolName(ident), false)
}
label!=null -> Pair(label, false)
addr!=null -> Pair(addr.toHex(), false)
else -> Pair("????", false)
}
}
@ -1758,11 +1778,15 @@ $repeatLabel lda $counterVar
return zeropage.allocatedZeropageVariable(vardecl.scopedName)!=null
}
internal fun jmp(asmLabel: String) {
if(isTargetCpu(CpuType.CPU65c02))
out(" bra $asmLabel") // note: 64tass will convert this automatically to a jmp if the relative distance is too large
else
out(" jmp $asmLabel")
internal fun jmp(asmLabel: String, indirect: Boolean=false) {
if(indirect) {
out(" jmp ($asmLabel)")
} else {
if (isTargetCpu(CpuType.CPU65c02))
out(" bra $asmLabel") // note: 64tass will convert this automatically to a jmp if the relative distance is too large
else
out(" jmp $asmLabel")
}
}
internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: Expression): Pair<Expression, Expression>? {

View File

@ -1308,8 +1308,18 @@ internal class AstChecker(private val program: Program,
private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: Statement): Statement? {
when (val targetStatement = target.targetStatement(program)) {
is Label, is Subroutine, is BuiltinFunctionPlaceholder -> return targetStatement
null -> errors.err("undefined function or subroutine: ${target.nameInSource.joinToString(".")}", statement.position)
else -> errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", statement.position)
is VarDecl -> {
if(statement is Jump) {
if (targetStatement.datatype == DataType.UWORD)
return targetStatement
else
errors.err("wrong address variable datatype, expected uword", target.position)
}
else
errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", target.position)
}
null -> errors.err("undefined function or subroutine: ${target.nameInSource.joinToString(".")}", target.position)
else -> errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", target.position)
}
return null
}

View File

@ -332,4 +332,57 @@ class TestScoping: FunSpec({
errors.errors[3] shouldContain "undefined symbol: routine.nested.nestedvalue"
errors.errors[4] shouldContain "undefined symbol: nested.nestedvalue"
}
test("various good goto targets") {
val text="""
main {
sub start() {
uword address = $4000
goto ${'$'}c000
goto address ; indirect jump
goto main.routine
goto main.jumplabel
if_cc
goto ${'$'}c000
if_cc
goto address ; indirect jump
if_cc
goto main.routine
if_cc
goto main.jumplabel
}
jumplabel:
%asm {{
rts
}}
sub routine() {
}
}
"""
compileText(C64Target, false, text, writeAssembly = false).assertSuccess()
}
test("various wrong goto targets") {
val text = """
main {
sub start() {
byte wrongaddress = 100
goto wrongaddress ; must be uword
goto main.routine ; can't take args
}
sub routine(ubyte arg) {
}
}
"""
val errors = ErrorReporterForTests()
compileText(C64Target, false, text, writeAssembly = false, errors = errors).assertFailure()
errors.errors.size shouldBe 2
errors.errors[0] shouldContain "wrong address"
errors.errors[1] shouldContain "takes parameters"
}
})

View File

@ -231,7 +231,7 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
override fun visit(jump: Jump) {
output("goto ")
when {
jump.address!=null -> output(jump.address.toHex())
jump.address!=null -> output(jump.address!!.toHex())
jump.generatedLabel!=null -> output(jump.generatedLabel)
jump.identifier!=null -> jump.identifier.accept(this)
}

View File

@ -527,7 +527,7 @@ class PostIncrDecr(var target: AssignTarget, val operator: String, override val
override fun toString() = "PostIncrDecr(op: $operator, target: $target, pos=$position)"
}
class Jump(val address: UInt?,
class Jump(var address: UInt?,
val identifier: IdentifierReference?,
val generatedLabel: String?, // can be used in code generation scenarios
override val position: Position) : Statement() {
@ -538,7 +538,13 @@ class Jump(val address: UInt?,
identifier?.linkParents(this)
}
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
override fun replaceChildNode(node: Node, replacement: Node) {
if(node===identifier && replacement is NumericLiteralValue) {
address = replacement.number.toUInt()
}
else
throw FatalAstException("can't replace $node")
}
override fun copy() = Jump(address, identifier?.copy(), generatedLabel, position)
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)

View File

@ -784,10 +784,15 @@ of a label or subroutine::
goto $c000 ; address
goto name ; label or subroutine
uword address = $4000
goto address ; jump via address variable
Notice that this is a valid way to end a subroutine (you can either ``return`` from it, or jump
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.
Conditional execution
^^^^^^^^^^^^^^^^^^^^^

View File

@ -3,7 +3,6 @@ TODO
For next release
^^^^^^^^^^^^^^^^
- allow goto pointervar (goto $a000 already works...) then use this in cx16assem's run_file()
- allow "xxx" * constexpr (where constexpr is not a number literal, now gives expression error not same type)
- is * lower prio than bitwise & ? fix prios if so!

View File

@ -3,16 +3,20 @@
main {
sub start() {
cx16.mouse_config(1, 0)
txt.print("yo\n")
uword jumps = $4000
if_cc
goto jumps
repeat {
ubyte mb = cx16.mouse_pos()
txt.print_uw(cx16.r0)
txt.spc()
txt.print_uw(cx16.r1)
txt.spc()
txt.print_ub(mb)
txt.nl()
}
goto jumps
}
}
test $4000 {
%option force_output
jumper:
%asm {{
rts
}}
}