diff --git a/compiler/res/prog8lib/cx16/textio.p8 b/compiler/res/prog8lib/cx16/textio.p8 index 8aee3a3f4..79c071ba8 100644 --- a/compiler/res/prog8lib/cx16/textio.p8 +++ b/compiler/res/prog8lib/cx16/textio.p8 @@ -420,7 +420,7 @@ _print_byte_digits jsr c64.CHROUT pla jsr c64.CHROUT - jmp _ones + bra _ones + pla cmp #'0' beq _ones @@ -443,7 +443,7 @@ asmsub print_b (byte value @ A) clobbers(A,Y) { jsr c64.CHROUT + pla jsr conv.byte2decimal - jmp print_ub._print_byte_digits + bra print_ub._print_byte_digits }} } @@ -494,7 +494,7 @@ asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) { jsr print_ubbin pla clc - jmp print_ubbin + bra print_ubbin }} } @@ -507,7 +507,7 @@ asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) { jsr print_ubhex pla clc - jmp print_ubhex + bra print_ubhex }} } @@ -570,7 +570,7 @@ asmsub print_w (word value @ AY) clobbers(A,Y) { adc #1 bcc + iny -+ jmp print_uw ++ bra print_uw }} } diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index a806fbb39..6628de741 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -218,13 +218,13 @@ internal class AstChecker(private val program: Program, err("subroutines can only have one return value") // subroutine must contain at least one 'return' or 'goto' - // (or if it has an asm block, that must contain a 'rts' or 'jmp') + // (or if it has an asm block, that must contain a 'rts' or 'jmp' or 'bra') if(subroutine.statements.count { it is Return || it is Jump } == 0) { if (subroutine.amountOfRtsInAsm() == 0) { if (subroutine.returntypes.isNotEmpty()) { // for asm subroutines with an address, no statement check is possible. if (subroutine.asmAddress == null && !subroutine.inline) - err("non-inline subroutine has result value(s) and thus must have at least one 'return' or 'goto' in it (or 'rts' / 'jmp' in case of %asm)") + err("non-inline subroutine has result value(s) and thus must have at least one 'return' or 'goto' in it (or rts/jmp/bra in case of %asm)") } } } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt index ef8da67a6..879458b31 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt @@ -1024,7 +1024,7 @@ $repeatLabel lda $counterVar + dec $counterVar """) translate(body) - out(" jmp $repeatLabel") + jmp(repeatLabel) if(constIterations!=null && constIterations>=16 && zeropage.available() > 1) { // allocate count var on ZP val zpAddr = zeropage.allocate(counterVar, DataType.UWORD, body.position, errors) @@ -1378,6 +1378,13 @@ $label nop""") return vardecl.makeScopedName(vardecl.name) in allocatedZeropageVariables } + 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 pointerViaIndexRegisterPossible(pointerOffsetExpr: Expression): Pair? { if(pointerOffsetExpr is BinaryExpression && pointerOffsetExpr.operator=="+") { val leftDt = pointerOffsetExpr.left.inferType(program) diff --git a/compiler/src/prog8/optimizer/CallGraph.kt b/compiler/src/prog8/optimizer/CallGraph.kt index 0b8ab593e..464a646db 100644 --- a/compiler/src/prog8/optimizer/CallGraph.kt +++ b/compiler/src/prog8/optimizer/CallGraph.kt @@ -19,7 +19,7 @@ private val alwaysKeepSubroutines = setOf( Pair("main", "start") ) -private val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE) +private val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr|bra)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE) private val asmRefRx = Regex("""[\-+a-zA-Z0-9_ \t]+(...)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE) @@ -178,7 +178,7 @@ class CallGraph(private val program: Program, private val asmFileLoader: (filena } override fun visit(inlineAssembly: InlineAssembly) { - // parse inline asm for subroutine calls (jmp, jsr) + // parse inline asm for subroutine calls (jmp, jsr, bra) val scope = inlineAssembly.definingSubroutine() scanAssemblyCode(inlineAssembly.assembly, inlineAssembly, scope) super.visit(inlineAssembly) diff --git a/compilerAst/src/prog8/ast/statements/AstStatements.kt b/compilerAst/src/prog8/ast/statements/AstStatements.kt index 38101d90b..567b0841e 100644 --- a/compilerAst/src/prog8/ast/statements/AstStatements.kt +++ b/compilerAst/src/prog8/ast/statements/AstStatements.kt @@ -728,7 +728,7 @@ class Subroutine(override val name: String, .asSequence() .filter { it is InlineAssembly } .map { (it as InlineAssembly).assembly } - .count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it } + .count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it || " bra" in it || "\tbra" in it} } diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index cc438df71..d5882745e 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -592,7 +592,7 @@ flag such as Carry (Pc). Asmsubs can also be tagged as ``inline asmsub`` to make trivial pieces of assembly inserted directly instead of a call to them. Note that it is literal copy-paste of code that is done, so make sure the assembly is actually written to behave like such - which probably means you - don't want a ``rts`` or ``jmp`` in it! + don't want a ``rts`` or ``jmp`` or ``bra`` in it! .. note:: diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 4c7b4b80f..f637de914 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -2,6 +2,8 @@ TODO ==== +- on 65C02 target; replace all jmp by bra (64tass will sort it out if the jump exceeds relative distance) + - optimize assigning array and struct variables (multi-element assings -> memcopy) - hoist all variable declarations up to the subroutine scope *before* even the constant folding takes place (to avoid undefined symbol errors when referring to a variable from another nested scope in the subroutine) - optimize swap of two memread values with index, using the same pointer expression/variable, like swap(@(ptr+1), @(ptr+2)) diff --git a/examples/cx16/cobramk3-gfx.p8 b/examples/cx16/cobramk3-gfx.p8 index 50e8beed4..2884f0dc3 100644 --- a/examples/cx16/cobramk3-gfx.p8 +++ b/examples/cx16/cobramk3-gfx.p8 @@ -64,25 +64,25 @@ main { asmsub print_number_gfx(ubyte num @ A) clobbers(A,Y) { %asm {{ - phx - jsr conv.ubyte2decimal - phx - pha - cpy #'0' - beq + - tya - jsr cx16.GRAPH_put_char - pla - jsr cx16.GRAPH_put_char - jmp _ones - + pla + phx + jsr conv.ubyte2decimal + phx + pha + cpy #'0' + beq + + tya + jsr cx16.GRAPH_put_char + pla + jsr cx16.GRAPH_put_char + bra _ones ++ pla cmp #'0' beq _ones jsr cx16.GRAPH_put_char - _ones pla - jsr cx16.GRAPH_put_char - plx - rts +_ones pla + jsr cx16.GRAPH_put_char + plx + rts }} } diff --git a/examples/test.p8 b/examples/test.p8 index 60c278389..c25cd3907 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -4,15 +4,20 @@ main { sub start() { - uword screen = 2000 - ubyte i = 1 - uword w = 33 - str derp ="derp" - ubyte[] array = [1,2,3] + uword xx1 + uword xx2 + uword xx3 + uword total - @(screen+i) = 128 - @(i+screen) = 129 - - txt.print("done\n") + c64.SETTIM(0,0,0) + repeat 600 { + repeat 600 { + xx1++ + xx2++ + xx3++ + } + } + uword time = c64.RDTIM16() + txt.print_uw(time) } }