Files
2026-09-11 20:20:40 +02:00

105 lines
4.0 KiB
Kotlin

package prog8tests.codegen.new6502
import io.kotest.core.spec.style.FunSpec
import io.kotest.engine.spec.tempdir
import io.kotest.matchers.shouldBe
import prog8.code.core.*
import prog8.code.target.Cx16Target
import prog8.codegen.new6502.AsmGen
import prog8.intermediate.*
import prog8tests.helpers.ErrorReporterForTests
import java.nio.file.Path
import kotlin.io.path.exists
import kotlin.io.path.readText
class TestInlineAsmSub : FunSpec({
val tempRoot = tempdir().toPath()
fun buildTestProgramWithInlineAsmSub(
inline: Boolean,
asmBody: String = "lda #42"
): Pair<IRProgram, ICompilationTarget> {
val target = Cx16Target()
val options = CompilationOptions.builder(target)
.output(OutputType.RAW)
.zeropage(ZeropageType.FLOATSAFE)
.floats(false)
.compilerVersion("test")
.memtopAddress(0xffffu)
.build()
val st = IRSymbolTable()
val program = IRProgram("test", st, options, DummyStringEncoder)
// Create the inline/regular asmsub
val asmChunk = IRInlineAsmChunk("main.test", asmBody, isIR = false, next = null)
val asmSub = IRAsmSubroutine(
label = "main.test",
address = null,
clobbers = emptySet(),
parameters = emptyList(),
returns = emptyList(),
asmChunk = asmChunk,
position = Position.DUMMY,
isInline = inline
)
// Create the main subroutine that calls the asmsub
val callChunk = IRCodeChunk(null, null)
callChunk.instructions.add(IRInstructions.call(CallSite(CallTarget.Direct(codeLabel("main.test")))))
val mainSub = IRSubroutine("main.start", emptyList(), emptyList(), Position.DUMMY)
mainSub.chunks.add(callChunk)
val block = IRBlock("main", false, IRBlock.Options(), Position.DUMMY)
block.children.add(mainSub)
block.children.add(asmSub)
program.blocks.add(block)
return Pair(program, target)
}
fun generateAsm(outputDir: Path, program: IRProgram, target: ICompilationTarget): String {
val codegen = AsmGen(program, target, ErrorReporterForTests())
codegen.generate()
val asmFile = outputDir.resolve("${program.name}.asm")
check(asmFile.exists()) { "Assembly file not written: $asmFile" }
return asmFile.readText()
}
test("inline asmsub is inlined at call site, no jsr") {
val (program, target) = buildTestProgramWithInlineAsmSub(inline = true)
val outputDir = tempRoot.resolve("test-inline-asmsub-new")
outputDir.toFile().deleteRecursively()
outputDir.toFile().mkdirs()
program.options.outputDir = outputDir
val asm = generateAsm(outputDir, program, target)
val lines = asm.lines().map { it.trim() }
// The assembly body should appear in the caller
lines.any { it == "lda #42" } shouldBe true
// Should NOT have a jsr to the inline asmsub
lines.count { it.startsWith("jsr") && it.contains("main.test") } shouldBe 0
// Should NOT have a separate .proc block for the inline asmsub
lines.count { it.contains("main.test") && it.contains(".proc") } shouldBe 0
}
test("regular asmsub is called via jsr, not inlined") {
val (program, target) = buildTestProgramWithInlineAsmSub(inline = false, asmBody = "lda #99\nrts")
val outputDir = tempRoot.resolve("test-regular-asmsub-new")
outputDir.toFile().deleteRecursively()
outputDir.toFile().mkdirs()
program.options.outputDir = outputDir
val asm = generateAsm(outputDir, program, target)
val lines = asm.lines().map { it.trim() }
// Should have a jsr to the regular asmsub
lines.any { it.startsWith("jsr") && it.contains("main.test") } shouldBe true
// The body should be in a separate .proc block
lines.any { it.contains("main.test") && it.contains(".proc") } shouldBe true
}
})