diff --git a/compiler/res/prog8lib/c64/syslib.p8 b/compiler/res/prog8lib/c64/syslib.p8 index b9101ca41..419757b27 100644 --- a/compiler/res/prog8lib/c64/syslib.p8 +++ b/compiler/res/prog8lib/c64/syslib.p8 @@ -225,6 +225,27 @@ romsub $FFF3 = IOBASE() -> uword @ XY ; read base addr ; ---- end of C64 ROM kernal routines ---- +; ---- utilities ----- + +asmsub STOP2() -> ubyte @A { + ; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag. + %asm {{ + txa + pha + jsr c64.STOP + beq + + pla + tax + lda #0 + rts ++ pla + tax + lda #1 + rts + }} +} + + ; ---- C64 specific system utility routines: ---- diff --git a/compiler/res/prog8lib/cx16/syslib.p8 b/compiler/res/prog8lib/cx16/syslib.p8 index 3ad4b972a..fbdad6482 100644 --- a/compiler/res/prog8lib/cx16/syslib.p8 +++ b/compiler/res/prog8lib/cx16/syslib.p8 @@ -56,6 +56,23 @@ romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X. romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices +; ---- utility + +asmsub STOP2() -> ubyte @A { + ; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag. + %asm {{ + phx + jsr c64.STOP + beq + + plx + lda #0 + rts ++ plx + lda #1 + rts + }} +} + } cx16 { diff --git a/compiler/res/prog8lib/diskio.p8 b/compiler/res/prog8lib/diskio.p8 index 4092b9d21..a8b7c2ac2 100644 --- a/compiler/res/prog8lib/diskio.p8 +++ b/compiler/res/prog8lib/diskio.p8 @@ -3,6 +3,8 @@ ; Note: this code is compatible with C64 and CX16. +; TODO directory() BROKEN ON C64, SEEMS TO WORK ON CX16 + diskio { @@ -30,16 +32,17 @@ diskio { txt.print_uw(mkword(high, low)) txt.chrout(' ') ubyte @zp char - do { + repeat { char = c64.CHRIN() + if char==0 ; TODO doesn't work??? + break txt.chrout(char) - } until char==0 + } txt.chrout('\n') void c64.CHRIN() ; skip 2 bytes void c64.CHRIN() status = c64.READST() - void c64.STOP() - if_z + if c64.STOP2() break } @@ -160,7 +163,7 @@ io_error: ; read the filename repeat { ubyte char = c64.CHRIN() - if_z + if char==0 break if char=='\"' break diff --git a/compiler/src/prog8/ast/statements/AstStatements.kt b/compiler/src/prog8/ast/statements/AstStatements.kt index 14bac6d87..dc41134c9 100644 --- a/compiler/src/prog8/ast/statements/AstStatements.kt +++ b/compiler/src/prog8/ast/statements/AstStatements.kt @@ -757,7 +757,6 @@ class Subroutine(override val name: String, fun regXasResult() = asmReturnvaluesRegisters.any { it.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) } fun regXasParam() = asmParameterRegisters.any { it.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) } - fun shouldPreserveStatusRegisterAfterCall() = asmReturnvaluesRegisters.any { it.statusflag != null } || shouldSaveX() fun shouldSaveX() = CpuRegister.X in asmClobbers || regXasResult() || regXasParam() fun amountOfRtsInAsm(): Int = statements diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt index fa7b66df9..2b61ef9ad 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt @@ -547,8 +547,20 @@ internal class AsmGen(private val program: Program, private fun fixNameSymbols(name: String) = name.replace("<", "prog8_").replace(">", "") // take care of the autogenerated invalid (anon) label names - internal fun saveRegister(register: CpuRegister, dontUseStack: Boolean, scope: Subroutine) { - if(dontUseStack) { + internal fun saveRegister(register: CpuRegister, scope: Subroutine, useStack: Boolean) { + if(useStack) { + when (register) { + CpuRegister.A -> out(" pha") + CpuRegister.X -> { + if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phx") + else out(" sta P8ZP_SCRATCH_REG | txa | pha | lda P8ZP_SCRATCH_REG") + } + CpuRegister.Y -> { + if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phy") + else out(" sta P8ZP_SCRATCH_REG | tya | pha | lda P8ZP_SCRATCH_REG") + } + } + } else { when (register) { CpuRegister.A -> { out(" sta _prog8_regsaveA") @@ -563,48 +575,28 @@ internal class AsmGen(private val program: Program, scope.asmGenInfo.usedRegsaveY = true } } - - } else { - when (register) { - CpuRegister.A -> out(" pha") - CpuRegister.X -> { - if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phx") - else { - out(" stx _prog8_regsaveX") - scope.asmGenInfo.usedRegsaveX = true - } - } - CpuRegister.Y -> { - if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phy") - else { - out(" sty _prog8_regsaveY") - scope.asmGenInfo.usedRegsaveY = true - } - } - } } } - internal fun restoreRegister(register: CpuRegister, dontUseStack: Boolean) { - if(dontUseStack) { - when (register) { - CpuRegister.A -> out(" sta _prog8_regsaveA") - CpuRegister.X -> out(" ldx _prog8_regsaveX") - CpuRegister.Y -> out(" ldy _prog8_regsaveY") - } - - } else { + internal fun restoreRegister(register: CpuRegister, useStack: Boolean) { + if(useStack) { when (register) { CpuRegister.A -> out(" pla") CpuRegister.X -> { if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" plx") - else out(" ldx _prog8_regsaveX") + else out(" pla | tax") } CpuRegister.Y -> { if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" ply") - else out(" ldy _prog8_regsaveY") + else out(" pla | tay") } } + } else { + when (register) { + CpuRegister.A -> out(" sta _prog8_regsaveA") + CpuRegister.X -> out(" ldx _prog8_regsaveX") + CpuRegister.Y -> out(" ldy _prog8_regsaveY") + } } } @@ -738,8 +730,8 @@ internal class AsmGen(private val program: Program, internal fun translateBuiltinFunctionCallExpression(functionCall: FunctionCall, signature: FSignature, resultToStack: Boolean) = builtinFunctionsAsmGen.translateFunctioncallExpression(functionCall, signature, resultToStack) - internal fun translateFunctionCall(functionCall: FunctionCall, preserveStatusRegisterAfterCall: Boolean) = - functioncallAsmGen.translateFunctionCall(functionCall, preserveStatusRegisterAfterCall) + internal fun translateFunctionCall(functionCall: FunctionCall) = + functioncallAsmGen.translateFunctionCall(functionCall) internal fun translateNormalAssignment(assign: AsmAssignment) = assignmentAsmGen.translateNormalAssignment(assign) diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AsmOptimizer.kt b/compiler/src/prog8/compiler/target/c64/codegen/AsmOptimizer.kt index e8f70f3d9..efa1b0a47 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AsmOptimizer.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AsmOptimizer.kt @@ -179,7 +179,7 @@ private fun optimizeSameAssignments(linesByFourteen: List>>): List { - // sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can be eliminated + // sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated // TODO this is not true if X is not a regular RAM memory address (but instead mapped I/O or ROM) val mods = mutableListOf() for (pair in linesByFour) { @@ -196,10 +196,14 @@ private fun optimizeStoreLoadSame(linesByFour: List>>) (first.startsWith("sty ") && second.startsWith("ldy ")) || (first.startsWith("stx ") && second.startsWith("ldx ")) ) { - val firstLoc = first.substring(4).trimStart() - val secondLoc = second.substring(4).trimStart() - if (firstLoc == secondLoc) { - mods.add(Modification(pair[1].index, true, null)) + val third = pair[2].value.trimStart() + if(!third.startsWith("b")) { + // no branch instruction follows, we can potentiall remove the load instruction + val firstLoc = first.substring(4).trimStart() + val secondLoc = second.substring(4).trimStart() + if (firstLoc == secondLoc) { + mods.add(Modification(pair[1].index, true, null)) + } } } } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt index da1ba36b0..97feae629 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/BuiltinFunctionsAsmGen.kt @@ -120,8 +120,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.R0) asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.R1) asmgen.assignExpressionToRegister(fcall.args[2], RegisterOrPair.A) - val sub = (fcall as FunctionCallStatement).definingSubroutine()!! - asmgen.saveRegister(CpuRegister.X, false, sub) + asmgen.saveRegister(CpuRegister.X, scope, false) asmgen.out(" jsr cx16.memory_fill") asmgen.restoreRegister(CpuRegister.X, false) } @@ -139,8 +138,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.R0) asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.R1) asmgen.assignExpressionToRegister(fcall.args[2], RegisterOrPair.R2) - val sub = (fcall as FunctionCallStatement).definingSubroutine()!! - asmgen.saveRegister(CpuRegister.X, false, sub) + asmgen.saveRegister(CpuRegister.X, scope, false) asmgen.out(" jsr cx16.memory_copy") asmgen.restoreRegister(CpuRegister.X, false) } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt index fefe63352..13463f590 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt @@ -74,6 +74,8 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge is FunctionCall -> { if(dt in ByteDatatypes) { asmgen.assignExpressionToRegister(left, RegisterOrPair.A) + if(left is FunctionCall) + asmgen.out(" cmp #0") asmgen.out(" bne $jumpIfFalseLabel") return } @@ -112,6 +114,8 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge is FunctionCall -> { if(dt in ByteDatatypes) { asmgen.assignExpressionToRegister(left, RegisterOrPair.A) + if(left is FunctionCall) + asmgen.out(" cmp #0") asmgen.out(" beq $jumpIfFalseLabel") return } @@ -1329,8 +1333,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge asmgen.translateBuiltinFunctionCallExpression(expression, builtinFunc, true) } else { sub as Subroutine - val preserveStatusRegisterAfterCall = sub.shouldPreserveStatusRegisterAfterCall() - asmgen.translateFunctionCall(expression, preserveStatusRegisterAfterCall) + asmgen.translateFunctionCall(expression) val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters) for ((_, reg) in returns) { // result value in cpu or status registers, put it on the stack diff --git a/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt index 61f66ae2d..a67c50c5f 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/FunctionCallAsmGen.kt @@ -15,22 +15,18 @@ import prog8.compiler.target.c64.codegen.assignment.* internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) { internal fun translateFunctionCallStatement(stmt: IFunctionCall) { - val sub = stmt.target.targetSubroutine(program.namespace)!! - val preserveStatusRegisterAfterCall = sub.shouldPreserveStatusRegisterAfterCall() - translateFunctionCall(stmt, preserveStatusRegisterAfterCall) + translateFunctionCall(stmt) // functioncalls no longer return results on the stack, so simply ignore the results in the registers - if(preserveStatusRegisterAfterCall) - asmgen.out(" plp") // restore status flags from call } - internal fun translateFunctionCall(stmt: IFunctionCall, preserveStatusRegisterAfterCall: Boolean) { + internal fun translateFunctionCall(stmt: IFunctionCall) { // output the code to setup the parameters and perform the actual call // does NOT output the code to deal with the result values! val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}") val saveX = sub.shouldSaveX() if(saveX) - asmgen.saveRegister(CpuRegister.X, preserveStatusRegisterAfterCall, (stmt as Node).definingSubroutine()!!) + asmgen.saveRegister(CpuRegister.X, (stmt as Node).definingSubroutine()!!, true) val subName = asmgen.asmSymbolName(stmt.target) if(stmt.args.isNotEmpty()) { @@ -67,15 +63,8 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg } } asmgen.out(" jsr $subName") - - if(preserveStatusRegisterAfterCall) { - asmgen.out(" php") // save status flags from call - // note: the containing statement (such as the FunctionCallStatement or the Assignment or the Expression) - // must take care of popping this value again at the end! - } - if(saveX) - asmgen.restoreRegister(CpuRegister.X, preserveStatusRegisterAfterCall) + asmgen.restoreRegister(CpuRegister.X, true) } private fun registerArgsViaStackEvaluation(stmt: IFunctionCall, sub: Subroutine) { diff --git a/compiler/src/prog8/compiler/target/c64/codegen/PostIncrDecrAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/PostIncrDecrAsmGen.kt index ec8c5f7ef..4a5d5e357 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/PostIncrDecrAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/PostIncrDecrAsmGen.kt @@ -91,7 +91,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg else { asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.A) - asmgen.saveRegister(CpuRegister.X, false, scope!!) + asmgen.saveRegister(CpuRegister.X, scope!!, false) asmgen.out(" tax") when(elementDt) { in ByteDatatypes -> { diff --git a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt index 9d417b65f..4f7c4b30d 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AssignmentAsmGen.kt @@ -142,8 +142,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen is FunctionCall -> { when (val sub = value.target.targetStatement(program.namespace)) { is Subroutine -> { - val preserveStatusRegisterAfterCall = sub.shouldPreserveStatusRegisterAfterCall() - asmgen.translateFunctionCall(value, preserveStatusRegisterAfterCall) + asmgen.translateFunctionCall(value) val returnValue = sub.returntypes.zip(sub.asmReturnvaluesRegisters).single { it.second.registerOrPair!=null } when (returnValue.first) { DataType.STR -> { @@ -182,8 +181,6 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen } } } - if (preserveStatusRegisterAfterCall) - asmgen.out(" plp") // restore status flags from call } is BuiltinFunctionStatementPlaceholder -> { val signature = BuiltinFunctions.getValue(sub.name) @@ -2003,9 +2000,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen asmgen.storeByteIntoPointer(addressExpr, null) } else -> { - asmgen.saveRegister(register, false, memoryAddress.definingSubroutine()!!) + asmgen.saveRegister(register, memoryAddress.definingSubroutine()!!, true) assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null) - asmgen.restoreRegister(CpuRegister.A, false) + asmgen.restoreRegister(CpuRegister.A, true) asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y") } } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AugmentableAssignmentAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AugmentableAssignmentAsmGen.kt index 33eae85de..8eb227de7 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/assignment/AugmentableAssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/assignment/AugmentableAssignmentAsmGen.kt @@ -1417,7 +1417,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, private fun inplaceModification_float_value_to_variable(name: String, operator: String, value: Expression, scope: Subroutine) { asmgen.assignExpressionToRegister(value, RegisterOrPair.FAC1) - asmgen.saveRegister(CpuRegister.X, false, scope) + asmgen.saveRegister(CpuRegister.X, scope, false) when (operator) { "**" -> { asmgen.out(""" @@ -1472,7 +1472,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, throw AssemblyError("float variable expected") val otherName = asmgen.asmVariableName(ident) - asmgen.saveRegister(CpuRegister.X, false, scope) + asmgen.saveRegister(CpuRegister.X, scope, false) when (operator) { "**" -> { if(CompilationTarget.instance is Cx16Target) { @@ -1550,7 +1550,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double, scope: Subroutine) { val constValueName = asmgen.getFloatAsmConst(value) - asmgen.saveRegister(CpuRegister.X, false, scope) + asmgen.saveRegister(CpuRegister.X, scope, false) when (operator) { "**" -> { if(CompilationTarget.instance is Cx16Target) { diff --git a/docs/source/programming.rst b/docs/source/programming.rst index 5575cf69f..67348966b 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -481,6 +481,12 @@ condition meaning So ``if_cc goto target`` will directly translate into the single CPU instruction ``BCC target``. +.. caution:: + These special ``if_XX`` branching statements are only useful in certain specific situations where you are *certain* + that the status register (still) contains the correct status bits. + This is not always the case after a fuction call or other operations! + If in doubt, check the generated assembly code! + .. note:: For now, the symbols used or declared in the statement block(s) are shared with the same scope the if statement itself is in. diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index 23b1f14b3..e04adbaa3 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -721,6 +721,13 @@ The XX corresponds to one of the eigth branching instructions so the possibiliti ``if_cs``, ``if_cc``, ``if_eq``, ``if_ne``, ``if_pl``, ``if_mi``, ``if_vs`` and ``if_vc``. It can also be one of the four aliases that are easier to read: ``if_z``, ``if_nz``, ``if_pos`` and ``if_neg``. +.. caution:: + These special ``if_XX`` branching statements are only useful in certain specific situations where you are *certain* + that the status register (still) contains the correct status bits. + This is not always the case after a fuction call or other operations! + If in doubt, check the generated assembly code! + + when statement ('jump table') ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The structure of a when statement is like this:: diff --git a/examples/tehtriz.p8 b/examples/tehtriz.p8 index 8e021aeda..824eca03d 100644 --- a/examples/tehtriz.p8 +++ b/examples/tehtriz.p8 @@ -7,6 +7,9 @@ ; staged speed increase ; some simple sound effects +; TODO BROKEN: DOESN'T REGISTER KEYSTROKES ANYMORE (CAN'T START GAME) + + %target c64 %import syslib %import textio diff --git a/examples/test.p8 b/examples/test.p8 index 4edba6d6d..081a53e53 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,5 +1,5 @@ %import textio -;%import diskio +%import diskio ;%import floats ;%import graphics %zeropage basicsafe @@ -70,14 +70,24 @@ _y .byte 0 } sub start () { + + while c64.CHRIN() { + ; read the rest of the entry until the end + } + ; cx16.r0 = 65535 ; set_8_pixels_opaque_OLD(111,222,33) ; txt.chrout('\n') - ubyte bb = 44 - set_8_pixels_opaque(bb,111,222,33) - txt.chrout('\n') - set_8_pixels_opaque(c64.CHRIN(),111,222,33) - txt.chrout('\n') +; ;ubyte qq=c64.CHKIN(3) ;; TODO fix compiler crash "can't translate zero return values in assignment" +; +; test_stack.test() +; ubyte bb = 44 +; set_8_pixels_opaque(bb,111,222,33) +; txt.chrout('\n') +; test_stack.test() +; +; set_8_pixels_opaque(c64.CHRIN(),111,222,33) +; txt.chrout('\n') test_stack.test() } diff --git a/examples/textelite.p8 b/examples/textelite.p8 index 3c660e829..35f6acfe1 100644 --- a/examples/textelite.p8 +++ b/examples/textelite.p8 @@ -10,6 +10,9 @@ ; Note: this program is compatible with C64 and CX16. +; TODO BROKEN IN VICE WHEN LOADING WITHOUT DISK: PRINTS WEIRD CHARACTERS FOR IO-ERROR + + main { const ubyte numforLave = 7 ; Lave is 7th generated planet in galaxy one