diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index c96a1123b..3c9736e19 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -438,6 +438,8 @@ internal class AssignmentAsmGen( private fun assignExpression(assign: AsmAssignment, scope: IPtSubroutine?) { when(val value = assign.source.expression!!) { is PtAddressOf -> { + val source = asmgen.symbolTable.lookup(value.identifier.name) + require(source !is StConstant) { "addressOf of a constant should have been rewritten to a simple addition expression" } val arrayDt = value.identifier.type val sourceName = if(value.isMsbForSplitArray) @@ -1567,8 +1569,8 @@ internal class AssignmentAsmGen( if(ptrVar!=null && asmgen.isZpVar(ptrVar)) { assignExpressionToRegister(value, RegisterOrPair.A, false) val pointername = asmgen.asmVariableName(ptrVar) - if (constOffset != null && constOffset < 256) { - // we have value + @(zpptr + 255), or value - @(zpptr+255) + if (constOffset != null) { + // we have value + @(zpptr + 255), or value - @(zpptr+255). the offset is always <256. asmgen.out(" ldy #$constOffset") if (operator == "+") asmgen.out(" clc | adc ($pointername),y") @@ -2613,13 +2615,23 @@ $endLabel""") if (arrayDt.isUnsignedWord) { require(!msb) assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position) - if(constIndex>0) + if(constIndex in 1..255) asmgen.out(""" clc adc #$constIndex bcc + iny +""") + else if(constIndex>=256) { + asmgen.out(""" + clc + adc #<$constIndex + pha + tya + adc #>$constIndex + tay + pla""") + } } else { if(constIndex>0) { @@ -2637,15 +2649,33 @@ $endLabel""") assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position) asmgen.saveRegisterStack(CpuRegister.A, false) asmgen.saveRegisterStack(CpuRegister.Y, false) - assignExpressionToVariable(arrayIndexExpr, "P8ZP_SCRATCH_REG", DataType.UBYTE) - asmgen.restoreRegisterStack(CpuRegister.Y, false) - asmgen.restoreRegisterStack(CpuRegister.A, false) - asmgen.out(""" - clc - adc P8ZP_SCRATCH_REG - bcc + - iny + if(arrayIndexExpr.type.isWord) { + assignExpressionToRegister(arrayIndexExpr, RegisterOrPair.AY, false) + asmgen.out(""" + sta P8ZP_SCRATCH_W1 + sty P8ZP_SCRATCH_W1+1 + pla + tay + pla + clc + adc P8ZP_SCRATCH_W1 + pha + tya + adc P8ZP_SCRATCH_W1+1 + tay + pla""") + } + else { + assignExpressionToVariable(arrayIndexExpr, "P8ZP_SCRATCH_REG", DataType.UBYTE) + asmgen.restoreRegisterStack(CpuRegister.Y, false) + asmgen.restoreRegisterStack(CpuRegister.A, false) + asmgen.out(""" + clc + adc P8ZP_SCRATCH_REG + bcc + + iny +""") + } } else { assignExpressionToRegister(arrayIndexExpr, RegisterOrPair.A, false) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt index 7f4eaf79d..f277c0ec4 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt @@ -1,9 +1,6 @@ package prog8.codegen.intermediate -import prog8.code.StExtSub -import prog8.code.StNode -import prog8.code.StNodeType -import prog8.code.StSub +import prog8.code.* import prog8.code.ast.* import prog8.code.core.AssemblyError import prog8.code.core.BaseDataType @@ -184,12 +181,19 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { if(expr.isFromArrayElement) { val indexTr = translateExpression(expr.arrayIndexExpr!!) addToResult(result, indexTr, indexTr.resultReg, -1) - val indexWordReg = codeGen.registers.next(IRDataType.WORD) - addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=indexWordReg, reg2=indexTr.resultReg), null) + val indexWordReg = if(indexTr.dt==IRDataType.BYTE) { + val ixWord = codeGen.registers.next(IRDataType.WORD) + addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=ixWord, reg2=indexTr.resultReg), null) + ixWord + } else indexTr.resultReg if(expr.identifier.type.isUnsignedWord) { require(!expr.isMsbForSplitArray) result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = symbol) + val ptr = codeGen.symbolTable.lookup(expr.identifier.name) + it += if(ptr is StConstant) + IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, immediate = ptr.value.toInt()) + else + IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = symbol) it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg) } } else { diff --git a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt index ac356f56d..bc815967d 100644 --- a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt +++ b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt @@ -429,5 +429,24 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter, } return noModifications } + + override fun after(addressOf: AddressOf, parent: Node): Iterable { + if(addressOf.arrayIndex!=null) { + val tgt = addressOf.identifier.constValue(program) + if (tgt != null && tgt.type.isWord) { + // &constant[idx] --> constant + idx + val indexExpr = addressOf.arrayIndex!!.indexExpr + val right = if(indexExpr.inferType(program) issimpletype tgt.type) + indexExpr + else + TypecastExpression(indexExpr, tgt.type, true, indexExpr.position) + val add = BinaryExpression(tgt, "+", right, addressOf.position) + return listOf( + IAstModification.ReplaceNode(addressOf, add, parent) + ) + } + } + return noModifications + } } diff --git a/compiler/test/ast/TestConst.kt b/compiler/test/ast/TestConst.kt index b94c55d40..cd8be2178 100644 --- a/compiler/test/ast/TestConst.kt +++ b/compiler/test/ast/TestConst.kt @@ -6,7 +6,6 @@ import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe import io.kotest.matchers.string.shouldContain import io.kotest.matchers.types.instanceOf -import prog8.ast.expressions.AddressOf import prog8.ast.expressions.BinaryExpression import prog8.ast.expressions.IdentifierReference import prog8.ast.expressions.NumericLiteral @@ -261,16 +260,16 @@ main { } test("const address-of memory mapped arrays") { - val src=""" + val src= """ main { sub start() { - &uword[30] @nosplit wb = ${'$'}2000 - &uword[100] @nosplit array1 = ${'$'}9e00 + &uword[30] @nosplit wb = $2000 + &uword[100] @nosplit array1 = $9e00 &uword[30] @nosplit array2 = &array1[len(wb)] - cx16.r0 = &array1 ; ${'$'}9e00 - cx16.r1 = &array1[len(wb)] ; ${'$'}9e3c - cx16.r2 = &array2 ; ${'$'}9e3c + cx16.r0 = &array1 ; $9e00 + cx16.r1 = &array1[len(wb)] ; $9e3c + cx16.r2 = &array2 ; $9e3c } }""" val result = compileText(Cx16Target(), false, src, outputDir, writeAssembly = false)!! @@ -307,10 +306,10 @@ main { } test("address of a const uword pointer array expression") { - val src=""" + val src= """ main { sub start() { - const uword buffer = ${'$'}2000 + const uword buffer = 2000 uword @shared addr = &buffer[2] const ubyte width = 100 @@ -323,10 +322,9 @@ main { val st = result.compilerAst.entrypoint.statements st.size shouldBe 11 val assignAddr = (st[2] as Assignment).value - (assignAddr as NumericLiteral).number shouldBe 8194.0 - val assignAddr2 = ((st[9] as Assignment).value as AddressOf) - assignAddr2.identifier.nameInSource shouldBe listOf("buffer") - assignAddr2.arrayIndex!!.indexExpr shouldBe instanceOf() + (assignAddr as NumericLiteral).number shouldBe 2002.0 + val assignAddr2 = (st[9] as Assignment).value as BinaryExpression + assignAddr2.operator shouldBe "+" } test("out of range const byte and word give correct error") { diff --git a/compiler/test/ast/TestVariousCompilerAst.kt b/compiler/test/ast/TestVariousCompilerAst.kt index 7c669bf4b..6acc256cc 100644 --- a/compiler/test/ast/TestVariousCompilerAst.kt +++ b/compiler/test/ast/TestVariousCompilerAst.kt @@ -1060,6 +1060,24 @@ main { st2[3].children.dropLast(1).map { (it as PtAssignTarget).identifier!!.name } shouldBe listOf("p8b_main.p8s_start.p8v_x", "p8b_main.p8s_start.p8v_y", "p8b_main.p8s_start.p8v_z") ((st2[3] as PtAssignment).value as PtFunctionCall).name shouldBe "p8b_main.p8s_multi" } + + test("address-of a uword pointer with word index should not overflow") { + val src= """ +main { + sub start() { + const uword cbuffer = $2000 + uword @shared buffer = $2000 + + cx16.r1 = &cbuffer[2000] + cx16.r5 = &buffer[2000] + + cx16.r3 = &cbuffer[cx16.r0] + cx16.r4 = &buffer[cx16.r0] + } +}""" + compileText(Cx16Target(), optimize=false, src, outputDir, writeAssembly=true) shouldNotBe null + compileText(VMTarget(), optimize=false, src, outputDir, writeAssembly=true) shouldNotBe null + } } }) diff --git a/compiler/test/vm/TestCompilerVirtual.kt b/compiler/test/vm/TestCompilerVirtual.kt index 6d31661c6..42321eb08 100644 --- a/compiler/test/vm/TestCompilerVirtual.kt +++ b/compiler/test/vm/TestCompilerVirtual.kt @@ -532,4 +532,22 @@ main { instructions[10].type shouldBe IRDataType.WORD } + test("typed address-of a const pointer with non-const array indexing") { + val src= """ +main { + sub start() { + const uword cbuffer = $2000 + uword @shared buffer = $2000 + + cx16.r2 = @(cbuffer + cx16.r0) + cx16.r1 = &cbuffer[cx16.r0] + + cx16.r3 = @(buffer + cx16.r0) + cx16.r4 = &buffer[cx16.r0] + } +}""" + val result = compileText(VMTarget(), false, src, outputDir)!! + val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir") + VmRunner().runProgram(virtfile.readText(), true) + } }) \ No newline at end of file diff --git a/docs/source/todo.rst b/docs/source/todo.rst index a744a2954..7038b7fd9 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -31,6 +31,7 @@ Future Things and Ideas IR/VM ----- +- fix bug: label not found error (see unit test "typed address-of a const pointer with non-const array indexing") - getting it in shape for code generation...: the IR file should be able to encode every detail about a prog8 program (the VM doesn't have to actually be able to run all of it though!) - fix call() return value handling (... what's wrong with it again?) - encode asmsub/extsub clobber info in the call , or maybe include these definitions in the p8ir file itself too. (return registers are already encoded in the CALL instruction) diff --git a/examples/test.p8 b/examples/test.p8 index da13f7328..c30165488 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -2,26 +2,7 @@ main { sub start() { - word[5] xpos - - xpos[4] &= $fff8 - xpos[4] &= $fff8 as word - xpos[4] = xpos[4] & $fff8 - xpos[4] = xpos[4] & $fff8 as word - - xpos[4] &= $7000 - xpos[4] &= $7000 as word - xpos[4] = xpos[4] & $7000 - xpos[4] = xpos[4] & $7000 as word - - xpos[4] |= $7000 - xpos[4] |= $7000 as word - xpos[4] = xpos[4] | $7000 - xpos[4] = xpos[4] | $7000 as word - - xpos[4] += $7000 - xpos[4] += $7000 as word - xpos[4] = xpos[4] + $7000 - xpos[4] = xpos[4] + $7000 as word + const uword cbuffer = $2000 + cx16.r1 = &cbuffer[cx16.r0] ; ERROR } } diff --git a/virtualmachine/src/prog8/vm/VmProgramLoader.kt b/virtualmachine/src/prog8/vm/VmProgramLoader.kt index c2fbcdfb3..285af2441 100644 --- a/virtualmachine/src/prog8/vm/VmProgramLoader.kt +++ b/virtualmachine/src/prog8/vm/VmProgramLoader.kt @@ -149,7 +149,7 @@ class VmProgramLoader { // placeholder is not a variable, so it must be a label of a code chunk instead val target: IRCodeChunk? = chunks.firstOrNull { it.label==label } if(target==null) - throw IRParseException("label '$label' not found in variables nor labels. VM cannot reference other things such as blocks") + throw IRParseException("label '$label' not found in variables nor labels. VM cannot reference other things such as blocks, and constants should have been replaced by their value") else if(instr.opcode in OpcodesThatBranch) chunk.instructions[line] = instr.copy(branchTarget = target, address = null) else {