mirror of
https://github.com/irmen/prog8.git
synced 2024-11-25 19:31:36 +00:00
fix golden ram area for x16, remove romsub restriction
note: romsubs still won't work in the VM but at least they compile again
This commit is contained in:
parent
f47498888c
commit
c3d74f2ae9
2
.idea/kotlinc.xml
generated
2
.idea/kotlinc.xml
generated
@ -4,6 +4,6 @@
|
||||
<option name="jvmTarget" value="11" />
|
||||
</component>
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="1.8.20" />
|
||||
<option name="version" value="1.8.0-release-345" />
|
||||
</component>
|
||||
</project>
|
@ -131,6 +131,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||
}
|
||||
|
||||
|
||||
// TODO: this class is not yet used
|
||||
class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAllocator(options) {
|
||||
private var nextLocation: UInt = region.first
|
||||
|
||||
|
@ -64,7 +64,7 @@ class CX16MachineDefinition: IMachineDefinition {
|
||||
|
||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||
zeropage = CX16Zeropage(compilerOptions)
|
||||
golden = GoldenRam(compilerOptions, 0x0600u until 0x0800u)
|
||||
golden = GoldenRam(compilerOptions, 0x0400u until ESTACK_LO)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1465,9 +1465,12 @@ class IRCodeGen(
|
||||
}
|
||||
} else {
|
||||
// regular asmsub
|
||||
val assemblyChild = child.children.single() as PtInlineAssembly
|
||||
if(child.children.map { (it as PtInlineAssembly).isIR }.toSet().size>1)
|
||||
errors.err("asmsub mixes IR and non-IR assembly code (could be compiler-generated)", child.position)
|
||||
val asmblocks = child.children.map { (it as PtInlineAssembly).assembly.trimEnd() }
|
||||
val assembly = asmblocks.joinToString("\n")
|
||||
val asmChunk = IRInlineAsmChunk(
|
||||
child.name, assemblyChild.assembly, assemblyChild.isIR, null
|
||||
child.name, assembly, (child.children[0] as PtInlineAssembly).isIR , null
|
||||
)
|
||||
irBlock += IRAsmSubroutine(
|
||||
child.name,
|
||||
|
@ -8,6 +8,7 @@ import prog8.code.target.VMTarget
|
||||
import prog8.codegen.vm.VmAssemblyProgram
|
||||
import prog8.codegen.vm.VmCodeGen
|
||||
import prog8.intermediate.IRSubroutine
|
||||
import prog8.intermediate.Opcode
|
||||
|
||||
class TestVmCodeGen: FunSpec({
|
||||
|
||||
@ -440,4 +441,33 @@ class TestVmCodeGen: FunSpec({
|
||||
irChunks.size shouldBe 1
|
||||
}
|
||||
|
||||
test("romsub allowed in codegen") {
|
||||
//main {
|
||||
// romsub $5000 = routine()
|
||||
//
|
||||
// sub start() {
|
||||
// routine()
|
||||
// }
|
||||
//}
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
|
||||
val romsub = PtAsmSub("routine", 0x5000u, emptySet(), emptyList(), emptyList(), false, Position.DUMMY)
|
||||
block.add(romsub)
|
||||
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
|
||||
val call = PtFunctionCall("main.routine", true, DataType.UNDEFINED, Position.DUMMY)
|
||||
sub.add(call)
|
||||
block.add(sub)
|
||||
program.add(block)
|
||||
|
||||
val options = getTestOptions()
|
||||
val st = SymbolTableMaker(program, options).make()
|
||||
val errors = ErrorReporterForTests()
|
||||
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||
irChunks.size shouldBe 1
|
||||
val callInstr = irChunks.single().instructions.single()
|
||||
callInstr.opcode shouldBe Opcode.CALL
|
||||
callInstr.value shouldBe 0x5000
|
||||
}
|
||||
})
|
@ -326,8 +326,6 @@ internal class AstChecker(private val program: Program,
|
||||
err("subroutines can only be defined in the scope of a block or within another subroutine")
|
||||
|
||||
if(subroutine.isAsmSubroutine) {
|
||||
if(compilerOptions.compTarget.name==VMTarget.NAME)
|
||||
err("cannot use asmsub for vm target, use regular subs")
|
||||
if(subroutine.asmParameterRegisters.size != subroutine.parameters.size)
|
||||
err("number of asm parameter registers is not the isSameAs as number of parameters")
|
||||
if(subroutine.asmReturnvaluesRegisters.size != subroutine.returntypes.size)
|
||||
|
@ -355,9 +355,9 @@ class IntermediateAstMaker(private val program: Program, private val options: Co
|
||||
for (asm in srcSub.statements) {
|
||||
asm as InlineAssembly
|
||||
if(asm.isIR)
|
||||
combinedIrAsm += asm.assembly + "\n"
|
||||
combinedIrAsm += asm.assembly.trimEnd() + "\n"
|
||||
else
|
||||
combinedTrueAsm += asm.assembly + "\n"
|
||||
combinedTrueAsm += asm.assembly.trimEnd() + "\n"
|
||||
}
|
||||
|
||||
if(combinedTrueAsm.isNotEmpty()) {
|
||||
|
@ -14,7 +14,6 @@ import prog8.intermediate.IRFileReader
|
||||
import prog8.intermediate.IRSubroutine
|
||||
import prog8.intermediate.Opcode
|
||||
import prog8.vm.VmRunner
|
||||
import prog8tests.helpers.ErrorReporterForTests
|
||||
import prog8tests.helpers.compileText
|
||||
import kotlin.io.path.readText
|
||||
|
||||
@ -219,54 +218,6 @@ main {
|
||||
}
|
||||
}
|
||||
|
||||
test("asmsub for virtual target not supported") {
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
void test(42)
|
||||
}
|
||||
|
||||
asmsub test(ubyte xx @A) -> ubyte @Y {
|
||||
%asm {{
|
||||
lda #99
|
||||
tay
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}"""
|
||||
val othertarget = Cx16Target()
|
||||
compileText(othertarget, true, src, writeAssembly = true) shouldNotBe null
|
||||
|
||||
val target = VMTarget()
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(target, false, src, writeAssembly = false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 1
|
||||
errors.errors[0] shouldContain "cannot use asmsub for vm target"
|
||||
}
|
||||
|
||||
test("asmsub for virtual target not supported even with IR") {
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
void test(42)
|
||||
}
|
||||
|
||||
asmsub test(ubyte xx @A) -> ubyte @Y {
|
||||
%ir {{
|
||||
return
|
||||
}}
|
||||
}
|
||||
}"""
|
||||
val othertarget = Cx16Target()
|
||||
compileText(othertarget, true, src, writeAssembly = true) shouldNotBe null
|
||||
|
||||
val target = VMTarget()
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(target, false, src, writeAssembly = false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 1
|
||||
errors.errors[0] shouldContain "cannot use asmsub for vm target"
|
||||
}
|
||||
|
||||
test("inline asm for virtual target should be IR") {
|
||||
val src = """
|
||||
main {
|
||||
|
@ -40,6 +40,9 @@ Compiler:
|
||||
- ir: peephole opt: reuse registers in chunks (but keep result registers in mind that pass values out! and don't renumber registers above SyscallRegisterBase!)
|
||||
- ir: add more optimizations in IRPeepholeOptimizer
|
||||
- ir: for expressions with array indexes that occur multiple times, can we avoid loading them into new virtualregs everytime and just reuse a single virtualreg as indexer? (simple form of common subexpression elimination)
|
||||
- ir: write arguments for subroutine calls in IR differently?
|
||||
don't load them explicitly into the variables but use a new special instruction to do it transparently.
|
||||
(vm should take care of it based on the subroutine's parameter list)
|
||||
- PtAst/IR: more complex common subexpression eliminations
|
||||
- vm: somehow be able to load a label address as value? (VmProgramLoader) this may require storing the program as bytecodes in actual memory though...
|
||||
- 6502 codegen: see if we can let for loops skip the loop if startvar>endvar, without adding a lot of code size/duplicating the loop condition.
|
||||
|
@ -414,11 +414,14 @@ private fun registersUsedInAssembly(isIR: Boolean, assembly: String): RegistersU
|
||||
|
||||
if(isIR) {
|
||||
assembly.lineSequence().forEach { line ->
|
||||
val result = parseIRCodeLine(line.trim(), null, mutableMapOf())
|
||||
result.fold(
|
||||
ifLeft = { it.addUsedRegistersCounts(readRegsCounts, writeRegsCounts,readFpRegsCounts, writeFpRegsCounts, regsTypes) },
|
||||
ifRight = { /* labels can be skipped */ }
|
||||
)
|
||||
val t = line.trim()
|
||||
if(t.isNotEmpty()) {
|
||||
val result = parseIRCodeLine(t, null, mutableMapOf())
|
||||
result.fold(
|
||||
ifLeft = { it.addUsedRegistersCounts(readRegsCounts, writeRegsCounts,readFpRegsCounts, writeFpRegsCounts, regsTypes) },
|
||||
ifRight = { /* labels can be skipped */ }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return RegistersUsed(readRegsCounts, writeRegsCounts, readFpRegsCounts, writeFpRegsCounts, regsTypes)
|
||||
|
@ -1,6 +1,7 @@
|
||||
package prog8.vm
|
||||
|
||||
import prog8.code.StMemVar
|
||||
import prog8.code.core.toHex
|
||||
import prog8.code.target.virtual.IVirtualMachineRunner
|
||||
import prog8.intermediate.*
|
||||
import java.awt.Color
|
||||
@ -147,7 +148,12 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
pcChunk = target
|
||||
pcIndex = 0
|
||||
}
|
||||
null -> throw IllegalArgumentException("no branchtarget in $i")
|
||||
null -> {
|
||||
if(i.value!=null)
|
||||
throw IllegalArgumentException("vm program can't jump to system memory address (${i.opcode} ${i.value!!.toHex()})")
|
||||
else
|
||||
throw IllegalArgumentException("no branchtarget in $i")
|
||||
}
|
||||
else -> throw IllegalArgumentException("VM can't execute code in a non-codechunk: $target")
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ class VmProgramLoader {
|
||||
|
||||
programChunks.forEach {
|
||||
it.instructions.forEach { ins ->
|
||||
if (ins.labelSymbol != null && ins.opcode !in OpcodesThatBranch)
|
||||
if (ins.labelSymbol != null && ins.opcode !in OpcodesThatBranch && ins.opcode !in OpcodesForCpuRegisters)
|
||||
require(ins.value != null) { "instruction with labelSymbol for a var should have value set to var's memory address" }
|
||||
}
|
||||
}
|
||||
@ -153,8 +153,11 @@ 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 }
|
||||
val opcode = chunk.instructions[line].opcode
|
||||
if(target==null)
|
||||
throw IRParseException("placeholder not found in variables nor labels: $label")
|
||||
if(target==null) {
|
||||
// exception allowed: storecpu/loadcpu instructions that refer to CPU registers
|
||||
if(opcode !in OpcodesForCpuRegisters)
|
||||
throw IRParseException("placeholder not found in variables nor labels: $label")
|
||||
}
|
||||
else if(opcode in OpcodesThatBranch) {
|
||||
chunk.instructions[line] = chunk.instructions[line].copy(branchTarget = target, value = null)
|
||||
} else if(opcode in OpcodesWithMemoryAddressAsValue) {
|
||||
|
Loading…
Reference in New Issue
Block a user