mirror of
https://github.com/irmen/prog8.git
synced 2024-11-26 11:49:22 +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" />
|
<option name="jvmTarget" value="11" />
|
||||||
</component>
|
</component>
|
||||||
<component name="KotlinJpsPluginSettings">
|
<component name="KotlinJpsPluginSettings">
|
||||||
<option name="version" value="1.8.20" />
|
<option name="version" value="1.8.0-release-345" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</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) {
|
class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAllocator(options) {
|
||||||
private var nextLocation: UInt = region.first
|
private var nextLocation: UInt = region.first
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ class CX16MachineDefinition: IMachineDefinition {
|
|||||||
|
|
||||||
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
|
||||||
zeropage = CX16Zeropage(compilerOptions)
|
zeropage = CX16Zeropage(compilerOptions)
|
||||||
golden = GoldenRam(compilerOptions, 0x0600u until 0x0800u)
|
golden = GoldenRam(compilerOptions, 0x0400u until ESTACK_LO)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1465,9 +1465,12 @@ class IRCodeGen(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// regular asmsub
|
// 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(
|
val asmChunk = IRInlineAsmChunk(
|
||||||
child.name, assemblyChild.assembly, assemblyChild.isIR, null
|
child.name, assembly, (child.children[0] as PtInlineAssembly).isIR , null
|
||||||
)
|
)
|
||||||
irBlock += IRAsmSubroutine(
|
irBlock += IRAsmSubroutine(
|
||||||
child.name,
|
child.name,
|
||||||
|
@ -8,6 +8,7 @@ import prog8.code.target.VMTarget
|
|||||||
import prog8.codegen.vm.VmAssemblyProgram
|
import prog8.codegen.vm.VmAssemblyProgram
|
||||||
import prog8.codegen.vm.VmCodeGen
|
import prog8.codegen.vm.VmCodeGen
|
||||||
import prog8.intermediate.IRSubroutine
|
import prog8.intermediate.IRSubroutine
|
||||||
|
import prog8.intermediate.Opcode
|
||||||
|
|
||||||
class TestVmCodeGen: FunSpec({
|
class TestVmCodeGen: FunSpec({
|
||||||
|
|
||||||
@ -440,4 +441,33 @@ class TestVmCodeGen: FunSpec({
|
|||||||
irChunks.size shouldBe 1
|
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")
|
err("subroutines can only be defined in the scope of a block or within another subroutine")
|
||||||
|
|
||||||
if(subroutine.isAsmSubroutine) {
|
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)
|
if(subroutine.asmParameterRegisters.size != subroutine.parameters.size)
|
||||||
err("number of asm parameter registers is not the isSameAs as number of parameters")
|
err("number of asm parameter registers is not the isSameAs as number of parameters")
|
||||||
if(subroutine.asmReturnvaluesRegisters.size != subroutine.returntypes.size)
|
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) {
|
for (asm in srcSub.statements) {
|
||||||
asm as InlineAssembly
|
asm as InlineAssembly
|
||||||
if(asm.isIR)
|
if(asm.isIR)
|
||||||
combinedIrAsm += asm.assembly + "\n"
|
combinedIrAsm += asm.assembly.trimEnd() + "\n"
|
||||||
else
|
else
|
||||||
combinedTrueAsm += asm.assembly + "\n"
|
combinedTrueAsm += asm.assembly.trimEnd() + "\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
if(combinedTrueAsm.isNotEmpty()) {
|
if(combinedTrueAsm.isNotEmpty()) {
|
||||||
|
@ -14,7 +14,6 @@ import prog8.intermediate.IRFileReader
|
|||||||
import prog8.intermediate.IRSubroutine
|
import prog8.intermediate.IRSubroutine
|
||||||
import prog8.intermediate.Opcode
|
import prog8.intermediate.Opcode
|
||||||
import prog8.vm.VmRunner
|
import prog8.vm.VmRunner
|
||||||
import prog8tests.helpers.ErrorReporterForTests
|
|
||||||
import prog8tests.helpers.compileText
|
import prog8tests.helpers.compileText
|
||||||
import kotlin.io.path.readText
|
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") {
|
test("inline asm for virtual target should be IR") {
|
||||||
val src = """
|
val src = """
|
||||||
main {
|
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: 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: 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: 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
|
- 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...
|
- 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.
|
- 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,12 +414,15 @@ private fun registersUsedInAssembly(isIR: Boolean, assembly: String): RegistersU
|
|||||||
|
|
||||||
if(isIR) {
|
if(isIR) {
|
||||||
assembly.lineSequence().forEach { line ->
|
assembly.lineSequence().forEach { line ->
|
||||||
val result = parseIRCodeLine(line.trim(), null, mutableMapOf())
|
val t = line.trim()
|
||||||
|
if(t.isNotEmpty()) {
|
||||||
|
val result = parseIRCodeLine(t, null, mutableMapOf())
|
||||||
result.fold(
|
result.fold(
|
||||||
ifLeft = { it.addUsedRegistersCounts(readRegsCounts, writeRegsCounts,readFpRegsCounts, writeFpRegsCounts, regsTypes) },
|
ifLeft = { it.addUsedRegistersCounts(readRegsCounts, writeRegsCounts,readFpRegsCounts, writeFpRegsCounts, regsTypes) },
|
||||||
ifRight = { /* labels can be skipped */ }
|
ifRight = { /* labels can be skipped */ }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return RegistersUsed(readRegsCounts, writeRegsCounts, readFpRegsCounts, writeFpRegsCounts, regsTypes)
|
return RegistersUsed(readRegsCounts, writeRegsCounts, readFpRegsCounts, writeFpRegsCounts, regsTypes)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package prog8.vm
|
package prog8.vm
|
||||||
|
|
||||||
import prog8.code.StMemVar
|
import prog8.code.StMemVar
|
||||||
|
import prog8.code.core.toHex
|
||||||
import prog8.code.target.virtual.IVirtualMachineRunner
|
import prog8.code.target.virtual.IVirtualMachineRunner
|
||||||
import prog8.intermediate.*
|
import prog8.intermediate.*
|
||||||
import java.awt.Color
|
import java.awt.Color
|
||||||
@ -147,7 +148,12 @@ class VirtualMachine(irProgram: IRProgram) {
|
|||||||
pcChunk = target
|
pcChunk = target
|
||||||
pcIndex = 0
|
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")
|
else -> throw IllegalArgumentException("VM can't execute code in a non-codechunk: $target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ class VmProgramLoader {
|
|||||||
|
|
||||||
programChunks.forEach {
|
programChunks.forEach {
|
||||||
it.instructions.forEach { ins ->
|
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" }
|
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
|
// 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 }
|
||||||
val opcode = chunk.instructions[line].opcode
|
val opcode = chunk.instructions[line].opcode
|
||||||
if(target==null)
|
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")
|
throw IRParseException("placeholder not found in variables nor labels: $label")
|
||||||
|
}
|
||||||
else if(opcode in OpcodesThatBranch) {
|
else if(opcode in OpcodesThatBranch) {
|
||||||
chunk.instructions[line] = chunk.instructions[line].copy(branchTarget = target, value = null)
|
chunk.instructions[line] = chunk.instructions[line].copy(branchTarget = target, value = null)
|
||||||
} else if(opcode in OpcodesWithMemoryAddressAsValue) {
|
} else if(opcode in OpcodesWithMemoryAddressAsValue) {
|
||||||
|
Loading…
Reference in New Issue
Block a user