mirror of
https://github.com/irmen/prog8.git
synced 2025-11-03 04:17:16 +00:00
fix various bugs around word-indexing combined with address-of: &buffer[2000]
This commit is contained in:
@@ -438,6 +438,8 @@ internal class AssignmentAsmGen(
|
|||||||
private fun assignExpression(assign: AsmAssignment, scope: IPtSubroutine?) {
|
private fun assignExpression(assign: AsmAssignment, scope: IPtSubroutine?) {
|
||||||
when(val value = assign.source.expression!!) {
|
when(val value = assign.source.expression!!) {
|
||||||
is PtAddressOf -> {
|
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 arrayDt = value.identifier.type
|
||||||
val sourceName =
|
val sourceName =
|
||||||
if(value.isMsbForSplitArray)
|
if(value.isMsbForSplitArray)
|
||||||
@@ -1567,8 +1569,8 @@ internal class AssignmentAsmGen(
|
|||||||
if(ptrVar!=null && asmgen.isZpVar(ptrVar)) {
|
if(ptrVar!=null && asmgen.isZpVar(ptrVar)) {
|
||||||
assignExpressionToRegister(value, RegisterOrPair.A, false)
|
assignExpressionToRegister(value, RegisterOrPair.A, false)
|
||||||
val pointername = asmgen.asmVariableName(ptrVar)
|
val pointername = asmgen.asmVariableName(ptrVar)
|
||||||
if (constOffset != null && constOffset < 256) {
|
if (constOffset != null) {
|
||||||
// we have value + @(zpptr + 255), or value - @(zpptr+255)
|
// we have value + @(zpptr + 255), or value - @(zpptr+255). the offset is always <256.
|
||||||
asmgen.out(" ldy #$constOffset")
|
asmgen.out(" ldy #$constOffset")
|
||||||
if (operator == "+")
|
if (operator == "+")
|
||||||
asmgen.out(" clc | adc ($pointername),y")
|
asmgen.out(" clc | adc ($pointername),y")
|
||||||
@@ -2613,13 +2615,23 @@ $endLabel""")
|
|||||||
if (arrayDt.isUnsignedWord) {
|
if (arrayDt.isUnsignedWord) {
|
||||||
require(!msb)
|
require(!msb)
|
||||||
assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position)
|
assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position)
|
||||||
if(constIndex>0)
|
if(constIndex in 1..255)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
clc
|
clc
|
||||||
adc #$constIndex
|
adc #$constIndex
|
||||||
bcc +
|
bcc +
|
||||||
iny
|
iny
|
||||||
+""")
|
+""")
|
||||||
|
else if(constIndex>=256) {
|
||||||
|
asmgen.out("""
|
||||||
|
clc
|
||||||
|
adc #<$constIndex
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
adc #>$constIndex
|
||||||
|
tay
|
||||||
|
pla""")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(constIndex>0) {
|
if(constIndex>0) {
|
||||||
@@ -2637,6 +2649,23 @@ $endLabel""")
|
|||||||
assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position)
|
assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position)
|
||||||
asmgen.saveRegisterStack(CpuRegister.A, false)
|
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||||
|
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)
|
assignExpressionToVariable(arrayIndexExpr, "P8ZP_SCRATCH_REG", DataType.UBYTE)
|
||||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||||
@@ -2647,6 +2676,7 @@ $endLabel""")
|
|||||||
iny
|
iny
|
||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
assignExpressionToRegister(arrayIndexExpr, RegisterOrPair.A, false)
|
assignExpressionToRegister(arrayIndexExpr, RegisterOrPair.A, false)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package prog8.codegen.intermediate
|
package prog8.codegen.intermediate
|
||||||
|
|
||||||
import prog8.code.StExtSub
|
import prog8.code.*
|
||||||
import prog8.code.StNode
|
|
||||||
import prog8.code.StNodeType
|
|
||||||
import prog8.code.StSub
|
|
||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.AssemblyError
|
import prog8.code.core.AssemblyError
|
||||||
import prog8.code.core.BaseDataType
|
import prog8.code.core.BaseDataType
|
||||||
@@ -184,12 +181,19 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
|||||||
if(expr.isFromArrayElement) {
|
if(expr.isFromArrayElement) {
|
||||||
val indexTr = translateExpression(expr.arrayIndexExpr!!)
|
val indexTr = translateExpression(expr.arrayIndexExpr!!)
|
||||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||||
val indexWordReg = codeGen.registers.next(IRDataType.WORD)
|
val indexWordReg = if(indexTr.dt==IRDataType.BYTE) {
|
||||||
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=indexWordReg, reg2=indexTr.resultReg), null)
|
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) {
|
if(expr.identifier.type.isUnsignedWord) {
|
||||||
require(!expr.isMsbForSplitArray)
|
require(!expr.isMsbForSplitArray)
|
||||||
result += IRCodeChunk(null, null).also {
|
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)
|
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -429,5 +429,24 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
|||||||
}
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun after(addressOf: AddressOf, parent: Node): Iterable<IAstModification> {
|
||||||
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import io.kotest.matchers.shouldBe
|
|||||||
import io.kotest.matchers.shouldNotBe
|
import io.kotest.matchers.shouldNotBe
|
||||||
import io.kotest.matchers.string.shouldContain
|
import io.kotest.matchers.string.shouldContain
|
||||||
import io.kotest.matchers.types.instanceOf
|
import io.kotest.matchers.types.instanceOf
|
||||||
import prog8.ast.expressions.AddressOf
|
|
||||||
import prog8.ast.expressions.BinaryExpression
|
import prog8.ast.expressions.BinaryExpression
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.expressions.NumericLiteral
|
import prog8.ast.expressions.NumericLiteral
|
||||||
@@ -261,16 +260,16 @@ main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("const address-of memory mapped arrays") {
|
test("const address-of memory mapped arrays") {
|
||||||
val src="""
|
val src= """
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
&uword[30] @nosplit wb = ${'$'}2000
|
&uword[30] @nosplit wb = $2000
|
||||||
&uword[100] @nosplit array1 = ${'$'}9e00
|
&uword[100] @nosplit array1 = $9e00
|
||||||
&uword[30] @nosplit array2 = &array1[len(wb)]
|
&uword[30] @nosplit array2 = &array1[len(wb)]
|
||||||
|
|
||||||
cx16.r0 = &array1 ; ${'$'}9e00
|
cx16.r0 = &array1 ; $9e00
|
||||||
cx16.r1 = &array1[len(wb)] ; ${'$'}9e3c
|
cx16.r1 = &array1[len(wb)] ; $9e3c
|
||||||
cx16.r2 = &array2 ; ${'$'}9e3c
|
cx16.r2 = &array2 ; $9e3c
|
||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
val result = compileText(Cx16Target(), false, src, outputDir, writeAssembly = false)!!
|
val result = compileText(Cx16Target(), false, src, outputDir, writeAssembly = false)!!
|
||||||
@@ -307,10 +306,10 @@ main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("address of a const uword pointer array expression") {
|
test("address of a const uword pointer array expression") {
|
||||||
val src="""
|
val src= """
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
const uword buffer = ${'$'}2000
|
const uword buffer = 2000
|
||||||
uword @shared addr = &buffer[2]
|
uword @shared addr = &buffer[2]
|
||||||
|
|
||||||
const ubyte width = 100
|
const ubyte width = 100
|
||||||
@@ -323,10 +322,9 @@ main {
|
|||||||
val st = result.compilerAst.entrypoint.statements
|
val st = result.compilerAst.entrypoint.statements
|
||||||
st.size shouldBe 11
|
st.size shouldBe 11
|
||||||
val assignAddr = (st[2] as Assignment).value
|
val assignAddr = (st[2] as Assignment).value
|
||||||
(assignAddr as NumericLiteral).number shouldBe 8194.0
|
(assignAddr as NumericLiteral).number shouldBe 2002.0
|
||||||
val assignAddr2 = ((st[9] as Assignment).value as AddressOf)
|
val assignAddr2 = (st[9] as Assignment).value as BinaryExpression
|
||||||
assignAddr2.identifier.nameInSource shouldBe listOf("buffer")
|
assignAddr2.operator shouldBe "+"
|
||||||
assignAddr2.arrayIndex!!.indexExpr shouldBe instanceOf<BinaryExpression>()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test("out of range const byte and word give correct error") {
|
test("out of range const byte and word give correct error") {
|
||||||
|
|||||||
@@ -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].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"
|
((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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -532,4 +532,22 @@ main {
|
|||||||
instructions[10].type shouldBe IRDataType.WORD
|
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)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
@@ -31,6 +31,7 @@ Future Things and Ideas
|
|||||||
|
|
||||||
IR/VM
|
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!)
|
- 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?)
|
- 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)
|
- 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)
|
||||||
|
|||||||
@@ -2,26 +2,7 @@
|
|||||||
|
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
word[5] xpos
|
const uword cbuffer = $2000
|
||||||
|
cx16.r1 = &cbuffer[cx16.r0] ; ERROR
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ class VmProgramLoader {
|
|||||||
// placeholder is not a variable, so it must be a label of a code chunk instead
|
// placeholder is not a variable, so it must be a label of a code chunk instead
|
||||||
val target: IRCodeChunk? = chunks.firstOrNull { it.label==label }
|
val target: IRCodeChunk? = chunks.firstOrNull { it.label==label }
|
||||||
if(target==null)
|
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)
|
else if(instr.opcode in OpcodesThatBranch)
|
||||||
chunk.instructions[line] = instr.copy(branchTarget = target, address = null)
|
chunk.instructions[line] = instr.copy(branchTarget = target, address = null)
|
||||||
else {
|
else {
|
||||||
|
|||||||
Reference in New Issue
Block a user