mirror of
https://github.com/irmen/prog8.git
synced 2026-10-24 03:02:20 +00:00
204 lines
6.7 KiB
Kotlin
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
|
|
}
|
|
}) |