diff --git a/compiler/src/prog8/ast/processing/AstChecker.kt b/compiler/src/prog8/ast/processing/AstChecker.kt index f860dc69f..531e3ebb1 100644 --- a/compiler/src/prog8/ast/processing/AstChecker.kt +++ b/compiler/src/prog8/ast/processing/AstChecker.kt @@ -622,8 +622,9 @@ internal class AstChecker(private val program: Program, directive.args[0].name != "basicsafe" && directive.args[0].name != "floatsafe" && directive.args[0].name != "kernalsafe" && + directive.args[0].name != "dontuse" && directive.args[0].name != "full") - err("invalid zp type, expected basicsafe, floatsafe, kernalsafe, or full") + err("invalid zp type, expected basicsafe, floatsafe, kernalsafe, dontuse, or full") } "%zpreserved" -> { if(directive.parent !is Module) err("this directive may only occur at module level") diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index b22b1e53d..87083170d 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -24,7 +24,8 @@ enum class ZeropageType { BASICSAFE, FLOATSAFE, KERNALSAFE, - FULL + FULL, + DONTUSE } data class IntegerOrAddressOf(val integer: Int?, val addressOf: AddressOf?) diff --git a/compiler/src/prog8/compiler/target/c64/MachineDefinition.kt b/compiler/src/prog8/compiler/target/c64/MachineDefinition.kt index 9808df11c..7f312cd2c 100644 --- a/compiler/src/prog8/compiler/target/c64/MachineDefinition.kt +++ b/compiler/src/prog8/compiler/target/c64/MachineDefinition.kt @@ -42,14 +42,14 @@ object MachineDefinition { } override val exitProgramStrategy: ExitProgramStrategy = when (options.zeropage) { - ZeropageType.BASICSAFE -> ExitProgramStrategy.CLEAN_EXIT + ZeropageType.BASICSAFE, ZeropageType.DONTUSE -> ExitProgramStrategy.CLEAN_EXIT ZeropageType.FLOATSAFE, ZeropageType.KERNALSAFE, ZeropageType.FULL -> ExitProgramStrategy.SYSTEM_RESET } init { - if (options.floats && options.zeropage != ZeropageType.FLOATSAFE && options.zeropage != ZeropageType.BASICSAFE) - throw CompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe'") + if (options.floats && options.zeropage !in setOf(ZeropageType.FLOATSAFE, ZeropageType.BASICSAFE, ZeropageType.DONTUSE )) + throw CompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'") if (options.zeropage == ZeropageType.FULL) { free.addAll(0x04..0xf9) @@ -84,11 +84,16 @@ object MachineDefinition { )) } - // add the other free Zp addresses, - // these are valid for the C-64 (when no RS232 I/O is performed) but to keep BASIC running fully: - free.addAll(listOf(0x04, 0x05, 0x06, 0x0a, 0x0e, - 0x94, 0x95, 0xa7, 0xa8, 0xa9, 0xaa, - 0xb5, 0xb6, 0xf7, 0xf8, 0xf9)) + if(options.zeropage==ZeropageType.BASICSAFE) { + // add the other free Zp addresses, + // these are valid for the C-64 (when no RS232 I/O is performed) but to keep BASIC running fully: + free.addAll(listOf(0x04, 0x05, 0x06, 0x0a, 0x0e, + 0x94, 0x95, 0xa7, 0xa8, 0xa9, 0xaa, + 0xb5, 0xb6, 0xf7, 0xf8, 0xf9)) + } else { + // don't use the zeropage at all + free.clear() + } } assert(SCRATCH_B1 !in free) assert(SCRATCH_REG !in free) diff --git a/compiler/src/prog8/compiler/target/c64/codegen2/AsmGen2.kt b/compiler/src/prog8/compiler/target/c64/codegen2/AsmGen2.kt index 29e1b7b81..a138dd233 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen2/AsmGen2.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen2/AsmGen2.kt @@ -1466,21 +1466,46 @@ $endLabel""") } targetArrayIdx!=null -> { val index = targetArrayIdx.arrayspec.index - val targetName = asmIdentifierName(targetArrayIdx.identifier) - val elementDt = ArrayElementTypes.getValue(targetArrayIdx.identifier.targetVarDecl(program.namespace)!!.datatype) + val what = asmIdentifierName(targetArrayIdx.identifier) + val arrayDt = targetArrayIdx.identifier.inferType(program)!! + val elementDt = ArrayElementTypes.getValue(arrayDt) when(index) { is NumericLiteralValue -> { val indexValue = index.number.toInt() * elementDt.memorySize() - out(if(incr) " inc $targetName+$indexValue" else " dec $targetName+$indexValue") + when(elementDt) { + in ByteDatatypes -> out(if (incr) " inc $what+$indexValue" else " dec $what+$indexValue") + in WordDatatypes -> { + if(incr) + out(" inc $what+$indexValue | bne + | inc $what+$indexValue+1 |+") + else + out(""" + lda $what+$indexValue + bne + + dec $what+$indexValue+1 ++ dec $what+$indexValue +""") + } + DataType.FLOAT -> { + out(" lda #<$what+$indexValue | ldy #>$what+$indexValue") + out(if(incr) " jsr c64flt.inc_var_f" else " jsr c64flt.dec_var_f") + } + else -> throw AssemblyError("need numeric type") + } } is RegisterExpr -> { - TODO("postincrdecr $elementDt array $targetName [ $index ]") + // TODO optimize common cases + translateArrayIndexIntoA(targetArrayIdx) + incrDecrArrayvalueWithIndexA(incr, arrayDt, what) } is IdentifierReference -> { - TODO("postincrdecr $elementDt array $targetName [ $index ]") + // TODO optimize common cases + translateArrayIndexIntoA(targetArrayIdx) + incrDecrArrayvalueWithIndexA(incr, arrayDt, what) } else -> { - TODO("postincrdecr $elementDt array $targetName [ $index ]") + // TODO optimize common cases + translateArrayIndexIntoA(targetArrayIdx) + incrDecrArrayvalueWithIndexA(incr, arrayDt, what) } } } @@ -1488,6 +1513,33 @@ $endLabel""") } } + private fun incrDecrArrayvalueWithIndexA(incr: Boolean, arrayDt: DataType, arrayVarName: String) { + out(" stx ${C64Zeropage.SCRATCH_REG_X} | tax") + when(arrayDt) { + DataType.STR, DataType.STR_S, + DataType.ARRAY_UB, DataType.ARRAY_B -> { + out(if(incr) " inc $arrayVarName,x" else " dec $arrayVarName,x") + } + DataType.ARRAY_UW, DataType.ARRAY_W -> { + if(incr) + out(" inc $arrayVarName,x | bne + | inc $arrayVarName+1,x |+") + else + out(""" + lda $arrayVarName,x + bne + + dec $arrayVarName+1,x ++ dec $arrayVarName +""") + } + DataType.ARRAY_F -> { + out(" lda #<$arrayVarName | ldy #>$arrayVarName") + out(if(incr) " jsr c64flt.inc_indexed_var_f" else " jsr c64flt.dec_indexed_var_f") + } + else -> throw AssemblyError("weird array dt") + } + out(" ldx ${C64Zeropage.SCRATCH_REG_X}") + } + private fun translate(jmp: Jump) { out(" jmp ${getJumpTarget(jmp)}") } @@ -1588,7 +1640,7 @@ $endLabel""") throw AssemblyError("weird array type") } } else { - translateCalcArrayIndexIntoA(arrayExpr) + translateArrayIndexIntoA(arrayExpr) readAndPushArrayvalueWithIndexA(arrayDt, arrayExpr.identifier) } assignFromEvalResult(assign.target) @@ -1617,8 +1669,7 @@ $endLabel""") } } - private fun translateCalcArrayIndexIntoA(expr: ArrayIndexedExpression) { - val arrayDt = expr.identifier.targetVarDecl(program.namespace)!!.datatype + private fun translateArrayIndexIntoA(expr: ArrayIndexedExpression) { val index = expr.arrayspec.index when (index) { is NumericLiteralValue -> throw AssemblyError("this should be optimized directly") @@ -1699,12 +1750,7 @@ $endLabel""") when(expression) { is PrefixExpression -> translateExpression(expression) is BinaryExpression -> translateExpression(expression) - is ArrayIndexedExpression -> { - // assume *reading* from an array - translateCalcArrayIndexIntoA(expression) - val arrayDt = expression.identifier.targetVarDecl(program.namespace)!!.datatype - readAndPushArrayvalueWithIndexA(arrayDt, expression.identifier) - } + is ArrayIndexedExpression -> translatePushFromArray(expression as ArrayIndexedExpression) is TypecastExpression -> translateExpression(expression) is AddressOf -> translateExpression(expression) is DirectMemoryRead -> translateExpression(expression) @@ -1726,6 +1772,32 @@ $endLabel""") } } + private fun translatePushFromArray(arrayExpr: ArrayIndexedExpression) { + // assume *reading* from an array + val index = arrayExpr.arrayspec.index + val arrayDt = arrayExpr.identifier.targetVarDecl(program.namespace)!!.datatype + val arrayVarName = asmIdentifierName(arrayExpr.identifier) + if(index is NumericLiteralValue) { + val elementDt = ArrayElementTypes.getValue(arrayDt) + val indexValue = index.number.toInt() * elementDt.memorySize() + when(elementDt) { + in ByteDatatypes -> { + out(" lda $arrayVarName+$indexValue | sta $ESTACK_LO_HEX,x | dex") + } + in WordDatatypes -> { + out(" lda $arrayVarName+$indexValue | sta $ESTACK_LO_HEX,x | lda $arrayVarName+$indexValue+1 | sta $ESTACK_HI_HEX,x | dex") + } + DataType.FLOAT -> { + out(" lda #<$arrayVarName+$indexValue | ldy #>$arrayVarName+$indexValue | jsr c64flt.push_float") + } + else -> throw AssemblyError("weird type") + } + } else { + translateArrayIndexIntoA(arrayExpr) + readAndPushArrayvalueWithIndexA(arrayDt, arrayExpr.identifier) + } + } + private fun translateExpression(expr: AddressOf) { val name = asmIdentifierName(expr.identifier) out(" lda #<$name | sta $ESTACK_LO_HEX,x | lda #>$name | sta $ESTACK_HI_HEX,x | dex") @@ -2398,24 +2470,20 @@ $endLabel""") val indexValue = index.number.toInt() * MachineDefinition.Mflpt5.MemorySize out(" lda #<$arrayVarName+$indexValue | ldy #>$arrayVarName+$indexValue | jsr c64flt.pop_float") } else { - translateCalcArrayIndexIntoA(targetArrayIdx) + translateArrayIndexIntoA(targetArrayIdx) out(" sta ${C64Zeropage.SCRATCH_REG} | asl a | asl a | clc | adc ${C64Zeropage.SCRATCH_REG}") out(""" tay lda $constFloat sta $arrayVarName,y lda $constFloat+1 - iny - sta $arrayVarName,y + sta $arrayVarName+1,y lda $constFloat+2 - iny - sta $arrayVarName,y + sta $arrayVarName+2,y lda $constFloat+3 - iny - sta $arrayVarName,y + sta $arrayVarName+3,y lda $constFloat+4 - iny - sta $arrayVarName,y + sta $arrayVarName+4,y """) // TODO use a subroutine for this } } diff --git a/compiler/test/UnitTests.kt b/compiler/test/UnitTests.kt index da703041c..2d84632b7 100644 --- a/compiler/test/UnitTests.kt +++ b/compiler/test/UnitTests.kt @@ -165,6 +165,8 @@ class TestZeropage { } } + // TODO test dontuse option + @Test fun testFreeSpaces() { val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true)) diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index 9abebe9cd..cd76a6446 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -74,6 +74,7 @@ Directives As with ``kernalsafe``, it is not possible to cleanly exit the program, other than to reset the machine. This option makes programs smaller and faster because even more variables can be stored in the ZP (which allows for more efficient assembly code). + - style ``dontuse`` -- don't use *any* location in the zeropage. Also read :ref:`zeropage`. diff --git a/examples/test.p8 b/examples/test.p8 index 921848b95..01191bbb5 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,18 +1,25 @@ %import c64utils %import c64flt %option enable_floats -%zeropage basicsafe ; @todo dontuse +%zeropage basicsafe main { sub start() { + c64scr.plot(0,24) + ubyte ub=200 byte bb=-100 uword uw = 2000 word ww = -1000 - float fl = 99.99 + float fl = 999.99 + ubyte[3] ubarr = 200 + byte[3] barr = -100 + uword[3] uwarr = 2000 + word[3] warr = -1000 + float[3] flarr = 999.99 c64scr.print("++\n") ub++ @@ -20,15 +27,30 @@ main { uw++ ww++ fl++ + ubarr[1]++ + barr[1]++ + uwarr[1]++ + warr[1]++ + flarr[1] ++ check_ub(ub, 201) Y=100 Y++ check_ub(Y, 101) - check_fl(fl, 100.99) + check_fl(fl, 1000.99) check_b(bb, -99) check_uw(uw, 2001) check_w(ww, -999) + check_ub(ubarr[0], 200) + check_fl(flarr[0], 999.99) + check_b(barr[0], -100) + check_uw(uwarr[0], 2000) + check_w(warr[0], -1000) + check_ub(ubarr[1], 201) + check_fl(flarr[1], 1000.99) + check_b(barr[1], -99) + check_uw(uwarr[1], 2001) + check_w(warr[1], -999) c64scr.print("--\n") ub-- @@ -36,16 +58,26 @@ main { uw-- ww-- fl-- + ubarr[1]-- + barr[1]-- + uwarr[1]-- + warr[1]-- + flarr[1] -- check_ub(ub, 200) Y=100 Y-- check_ub(Y, 99) - check_fl(fl, 99.99) + check_fl(fl, 999.99) check_b(bb, -100) check_uw(uw, 2000) check_w(ww, -1000) + check_ub(ubarr[1], 200) + check_fl(flarr[1], 999.99) + check_b(barr[1], -100) + check_uw(uwarr[1], 2000) + check_w(warr[1], -1000) - @($0400+39) = X + @($0400+400-1) = X } sub check_ub(ubyte value, ubyte expected) {