Files
prog8/compiler/test/codegeneration/TestPointerFieldAugmentedAssignment6502.kt
T

204 lines
6.7 KiB
Kotlin

package prog8tests.codegeneration
import io.kotest.core.spec.style.FunSpec
import prog8.code.target.Cx16Target
import prog8tests.helpers.compileText
import prog8tests.helpers.simulate
import java.nio.file.Files
/**
* These tests execute compiled 6502 code in the ksim65 simulator to verify that
* in-place modifications (augmented assignments) on struct fields reached via
* a pointer are correct. This is especially tricky when the pointer variable is
* *not* located in the zeropage: its effective address must live in the shared
* scratch pointer, which evaluation of the source expression may clobber.
*/
class TestPointerFieldAugmentedAssignment6502 : FunSpec({
val outputDir = Files.createTempDirectory("prog8test")
test("word field += via expression source, pointer not in zeropage") {
val src = $$"""
%option no_sysinit
%launcher none
%address $1000
%zeropage dontuse
main {
struct Node {
uword size
^^Node next
}
&ubyte poweroff = $f203
&uword psize = $02
&uword qsize = $04
sub start() {
^^Node p = ^^Node: [0, 0]
^^Node q = ^^Node: [0, 0]
p.size = 100
p.next = q
q.size = 200
q.next = 0
p.size += p.next.size
psize = p.size
qsize = q.size
poweroff = 1
}
}
""".trimIndent()
val compileResult = compileText(Cx16Target(), false, src, outputDir)
val machine = compileResult!!.simulate()
machine.assertMemory(0x02, 0x2c)
machine.assertMemory(0x03, 0x01) // p.size = 300
machine.assertMemory(0x04, 0xc8)
machine.assertMemory(0x05, 0x00) // q.size = 200 (unchanged)
}
test("multi-element pointer chain as target, pointer not in zeropage") {
val src = $$"""
%option no_sysinit
%launcher none
%address $1000
%zeropage dontuse
main {
struct Node {
uword size
uword val
^^Node next
}
&ubyte poweroff = $f203
&uword qsize = $02
sub start() {
^^Node p = ^^Node: [0, 0, 0]
^^Node q = ^^Node: [0, 0, 0]
p.size = 100
p.val = 25
p.next = q
q.size = 400
p.next.size += p.val
qsize = q.size
poweroff = 1
}
}
""".trimIndent()
val compileResult = compileText(Cx16Target(), false, src, outputDir)
val machine = compileResult!!.simulate()
machine.assertMemory(0x02, 0xa9)
machine.assertMemory(0x03, 0x01) // q.size = 425
}
test("byte field += via expression source, pointer not in zeropage") {
val src = $$"""
%option no_sysinit
%launcher none
%address $1000
%zeropage dontuse
main {
struct Node {
ubyte b
^^Node next
}
&ubyte poweroff = $f203
&ubyte pb = $02
&ubyte qb = $03
sub start() {
^^Node p = ^^Node: [0, 0]
^^Node q = ^^Node: [0, 0]
p.b = 10
p.next = q
q.b = 15
p.b += q.b
pb = p.b
qb = q.b
poweroff = 1
}
}
""".trimIndent()
val compileResult = compileText(Cx16Target(), false, src, outputDir)
val machine = compileResult!!.simulate()
machine.assertMemory(0x02, 25) // p.b = 25
machine.assertMemory(0x03, 15) // q.b = 15 (unchanged)
}
test("multi-element chain target with zeropage base pointer") {
// regression: derefUsesScratchPointer returned false for chain.size>1 with a
// zeropage base pointer, so the source expression could clobber the target address.
val src = $$"""
%option no_sysinit
%launcher none
%address $1000
main {
struct Node {
uword size
^^Node next
}
&ubyte poweroff = $f203
&uword qsize = $02
^^Node p = ^^Node: [0, 0]
^^Node q = ^^Node: [0, 0]
^^Node r = ^^Node: [0, 0]
&^^Node rptr = $2000
sub start() {
rptr = r
p.next = q
q.size = 200
r.size = 50
p.next.size += rptr.size
qsize = q.size
poweroff = 1
}
}
""".trimIndent()
val compileResult = compileText(Cx16Target(), false, src, outputDir)
val machine = compileResult!!.simulate()
machine.assertMemory(0x02, 0xfa)
machine.assertMemory(0x03, 0x00) // q.size = 250
}
test("word field /= and %= via expression source, pointer not in zeropage") {
val src = $$"""
%option no_sysinit
%launcher none
%address $1000
%zeropage dontuse
main {
struct Node {
uword size
uword val
^^Node next
}
&ubyte poweroff = $f203
&uword psize = $02
&uword pval = $04
sub start() {
^^Node p = ^^Node: [0, 0, 0]
^^Node q = ^^Node: [0, 0, 0]
p.size = 795
p.val = 13
p.next = q
q.size = 200
p.size /= q.size
p.size += q.size
p.size %= p.val
psize = p.size
pval = p.val
poweroff = 1
}
}
""".trimIndent()
val compileResult = compileText(Cx16Target(), false, src, outputDir)
val machine = compileResult!!.simulate()
machine.assertMemory(0x02, 8)
machine.assertMemory(0x03, 0) // (795/200=3, +200=203, %13=8)
machine.assertMemory(0x04, 13)
machine.assertMemory(0x05, 0) // p.val unchanged
}
})