mirror of
https://github.com/irmen/prog8.git
synced 2024-11-25 19:31:36 +00:00
some more old code cleanups
This commit is contained in:
parent
b5d1e8653d
commit
b68f141568
4
.idea/modules.xml
generated
4
.idea/modules.xml
generated
@ -2,11 +2,11 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectModuleManager">
|
<component name="ProjectModuleManager">
|
||||||
<modules>
|
<modules>
|
||||||
<module fileurl="file://$PROJECT_DIR$/DeprecatedStackVm/DeprecatedStackVm.iml" filepath="$PROJECT_DIR$/DeprecatedStackVm/DeprecatedStackVm.iml" />
|
<module fileurl="file://$PROJECT_DIR$/OldCodeGen/OldCodeGen.iml" filepath="$PROJECT_DIR$/OldCodeGen/OldCodeGen.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
|
<module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
|
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
|
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
|
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
|
||||||
</modules>
|
</modules>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
File diff suppressed because it is too large
Load Diff
@ -1,51 +0,0 @@
|
|||||||
package compiler.intermediate
|
|
||||||
|
|
||||||
import prog8.vm.RuntimeValue
|
|
||||||
import prog8.vm.stackvm.Syscall
|
|
||||||
|
|
||||||
open class Instruction(val opcode: Opcode,
|
|
||||||
val arg: RuntimeValue? = null,
|
|
||||||
val arg2: RuntimeValue? = null,
|
|
||||||
val callLabel: String? = null,
|
|
||||||
val callLabel2: String? = null)
|
|
||||||
{
|
|
||||||
var branchAddress: Int? = null
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
val argStr = arg?.toString() ?: ""
|
|
||||||
val result =
|
|
||||||
when {
|
|
||||||
opcode== Opcode.LINE -> "_line $callLabel"
|
|
||||||
opcode== Opcode.INLINE_ASSEMBLY -> {
|
|
||||||
// inline assembly is not written out (it can't be processed as intermediate language)
|
|
||||||
// instead, it is converted into a system call that can be intercepted by the vm
|
|
||||||
if(callLabel!=null)
|
|
||||||
"syscall SYSASM.$callLabel\n return"
|
|
||||||
else
|
|
||||||
"inline_assembly"
|
|
||||||
}
|
|
||||||
opcode== Opcode.INCLUDE_FILE -> {
|
|
||||||
"include_file \"$callLabel\" $arg $arg2"
|
|
||||||
}
|
|
||||||
opcode== Opcode.SYSCALL -> {
|
|
||||||
val syscall = Syscall.values().find { it.callNr==arg!!.numericValue() }
|
|
||||||
"syscall $syscall"
|
|
||||||
}
|
|
||||||
opcode in opcodesWithVarArgument -> {
|
|
||||||
// opcodes that manipulate a variable
|
|
||||||
"${opcode.name.toLowerCase()} ${callLabel?:""} ${callLabel2?:""}".trimEnd()
|
|
||||||
}
|
|
||||||
callLabel==null -> "${opcode.name.toLowerCase()} $argStr"
|
|
||||||
else -> "${opcode.name.toLowerCase()} $callLabel $argStr"
|
|
||||||
}
|
|
||||||
.trimEnd()
|
|
||||||
|
|
||||||
return " $result"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LabelInstr(val name: String, val asmProc: Boolean) : Instruction(Opcode.NOP, null, null) {
|
|
||||||
override fun toString(): String {
|
|
||||||
return "\n$name:"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,548 +0,0 @@
|
|||||||
package compiler.intermediate
|
|
||||||
|
|
||||||
import prog8.ast.antlr.escape
|
|
||||||
import prog8.ast.base.*
|
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
|
||||||
import prog8.ast.expressions.ReferenceLiteralValue
|
|
||||||
import prog8.ast.statements.StructDecl
|
|
||||||
import prog8.ast.statements.VarDecl
|
|
||||||
import prog8.ast.statements.ZeropageWish
|
|
||||||
import prog8.compiler.CompilerException
|
|
||||||
import prog8.compiler.HeapValues
|
|
||||||
import prog8.compiler.Zeropage
|
|
||||||
import prog8.compiler.ZeropageDepletedError
|
|
||||||
import prog8.vm.RuntimeValue
|
|
||||||
import java.io.PrintStream
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
|
|
||||||
class IntermediateProgram(val name: String, var loadAddress: Int, val heap: HeapValues, val source: Path) {
|
|
||||||
|
|
||||||
class VariableParameters (val zp: ZeropageWish, val memberOfStruct: StructDecl?)
|
|
||||||
class Variable(val scopedname: String, val value: RuntimeValue, val params: VariableParameters)
|
|
||||||
|
|
||||||
class ProgramBlock(val name: String,
|
|
||||||
var address: Int?,
|
|
||||||
val instructions: MutableList<Instruction> = mutableListOf(),
|
|
||||||
val variables: MutableList<Variable> = mutableListOf(),
|
|
||||||
val memoryPointers: MutableMap<String, Pair<Int, DataType>> = mutableMapOf(),
|
|
||||||
val labels: MutableMap<String, Instruction> = mutableMapOf(), // names are fully scoped
|
|
||||||
val force_output: Boolean)
|
|
||||||
|
|
||||||
val allocatedZeropageVariables = mutableMapOf<String, Pair<Int, DataType>>()
|
|
||||||
val blocks = mutableListOf<ProgramBlock>()
|
|
||||||
val memory = mutableMapOf<Int, List<RuntimeValue>>()
|
|
||||||
private lateinit var currentBlock: ProgramBlock
|
|
||||||
|
|
||||||
fun allocateZeropage(zeropage: Zeropage) { // TODO not used anymore???
|
|
||||||
// allocates all @zp marked variables on the zeropage (for all blocks, as long as there is space in the ZP)
|
|
||||||
var notAllocated = 0
|
|
||||||
for(block in blocks) {
|
|
||||||
val zpVariables = block.variables.filter { it.params.zp==ZeropageWish.REQUIRE_ZEROPAGE || it.params.zp==ZeropageWish.PREFER_ZEROPAGE }
|
|
||||||
if (zpVariables.isNotEmpty()) {
|
|
||||||
for (variable in zpVariables) {
|
|
||||||
if(variable.params.zp==ZeropageWish.NOT_IN_ZEROPAGE || variable.params.memberOfStruct!=null)
|
|
||||||
throw CompilerException("zp conflict")
|
|
||||||
try {
|
|
||||||
val address = zeropage.allocate(variable.scopedname, variable.value.type, null)
|
|
||||||
allocatedZeropageVariables[variable.scopedname] = Pair(address, variable.value.type)
|
|
||||||
} catch (x: ZeropageDepletedError) {
|
|
||||||
printWarning(x.toString() + " variable ${variable.scopedname} type ${variable.value.type}")
|
|
||||||
notAllocated++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(notAllocated>0)
|
|
||||||
printWarning("$notAllocated variables marked for Zeropage could not be allocated there")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun optimize() {
|
|
||||||
println("Optimizing stackVM code...")
|
|
||||||
// remove nops (that are not a label)
|
|
||||||
for (blk in blocks) {
|
|
||||||
blk.instructions.removeIf { it.opcode== Opcode.NOP && it !is LabelInstr }
|
|
||||||
}
|
|
||||||
|
|
||||||
optimizeDataConversionAndUselessDiscards()
|
|
||||||
optimizeVariableCopying()
|
|
||||||
optimizeMultipleSequentialLineInstrs()
|
|
||||||
optimizeCallReturnIntoJump()
|
|
||||||
optimizeConditionalBranches()
|
|
||||||
// todo: add more optimizations to intermediate code!
|
|
||||||
|
|
||||||
optimizeRemoveNops() // must be done as the last step
|
|
||||||
optimizeMultipleSequentialLineInstrs() // once more
|
|
||||||
optimizeRemoveNops() // once more
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun optimizeConditionalBranches() {
|
|
||||||
// conditional branches that consume the value on the stack
|
|
||||||
// sometimes these are just constant values, so we can statically determine the branch
|
|
||||||
// or, they are preceded by a NOT instruction so we can simply remove that and flip the branch condition
|
|
||||||
val pushvalue = setOf(Opcode.PUSH_BYTE, Opcode.PUSH_WORD)
|
|
||||||
val notvalue = setOf(Opcode.NOT_BYTE, Opcode.NOT_WORD)
|
|
||||||
val branchOpcodes = setOf(Opcode.JZ, Opcode.JNZ, Opcode.JZW, Opcode.JNZW)
|
|
||||||
for(blk in blocks) {
|
|
||||||
val instructionsToReplace = mutableMapOf<Int, Instruction>()
|
|
||||||
blk.instructions.asSequence().withIndex().filter {it.value.opcode!= Opcode.LINE }.windowed(2).toList().forEach {
|
|
||||||
if (it[1].value.opcode in branchOpcodes) {
|
|
||||||
if (it[0].value.opcode in pushvalue) {
|
|
||||||
val value = it[0].value.arg!!.asBoolean
|
|
||||||
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
|
|
||||||
val replacement: Instruction =
|
|
||||||
if (value) {
|
|
||||||
when (it[1].value.opcode) {
|
|
||||||
Opcode.JNZ -> Instruction(Opcode.JUMP, callLabel = it[1].value.callLabel)
|
|
||||||
Opcode.JNZW -> Instruction(Opcode.JUMP, callLabel = it[1].value.callLabel)
|
|
||||||
else -> Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
when (it[1].value.opcode) {
|
|
||||||
Opcode.JZ -> Instruction(Opcode.JUMP, callLabel = it[1].value.callLabel)
|
|
||||||
Opcode.JZW -> Instruction(Opcode.JUMP, callLabel = it[1].value.callLabel)
|
|
||||||
else -> Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
instructionsToReplace[it[1].index] = replacement
|
|
||||||
}
|
|
||||||
else if (it[0].value.opcode in notvalue) {
|
|
||||||
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
|
|
||||||
val replacement: Instruction =
|
|
||||||
when (it[1].value.opcode) {
|
|
||||||
Opcode.JZ -> Instruction(Opcode.JNZ, callLabel = it[1].value.callLabel)
|
|
||||||
Opcode.JZW -> Instruction(Opcode.JNZW, callLabel = it[1].value.callLabel)
|
|
||||||
Opcode.JNZ -> Instruction(Opcode.JZ, callLabel = it[1].value.callLabel)
|
|
||||||
Opcode.JNZW -> Instruction(Opcode.JZW, callLabel = it[1].value.callLabel)
|
|
||||||
else -> Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
instructionsToReplace[it[1].index] = replacement
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (rins in instructionsToReplace) {
|
|
||||||
blk.instructions[rins.key] = rins.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun optimizeRemoveNops() {
|
|
||||||
// remove nops (that are not a label)
|
|
||||||
for (blk in blocks)
|
|
||||||
blk.instructions.removeIf { it.opcode== Opcode.NOP && it !is LabelInstr }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun optimizeCallReturnIntoJump() {
|
|
||||||
// replaces call X followed by return, by jump X
|
|
||||||
for(blk in blocks) {
|
|
||||||
val instructionsToReplace = mutableMapOf<Int, Instruction>()
|
|
||||||
|
|
||||||
blk.instructions.asSequence().withIndex().filter {it.value.opcode!= Opcode.LINE }.windowed(2).toList().forEach {
|
|
||||||
if(it[0].value.opcode== Opcode.CALL && it[1].value.opcode== Opcode.RETURN) {
|
|
||||||
instructionsToReplace[it[1].index] = Instruction(Opcode.JUMP, callLabel = it[0].value.callLabel)
|
|
||||||
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (rins in instructionsToReplace) {
|
|
||||||
blk.instructions[rins.key] = rins.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun optimizeMultipleSequentialLineInstrs() {
|
|
||||||
for(blk in blocks) {
|
|
||||||
val instructionsToReplace = mutableMapOf<Int, Instruction>()
|
|
||||||
|
|
||||||
blk.instructions.asSequence().withIndex().windowed(2).toList().forEach {
|
|
||||||
if (it[0].value.opcode == Opcode.LINE && it[1].value.opcode == Opcode.LINE)
|
|
||||||
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (rins in instructionsToReplace) {
|
|
||||||
blk.instructions[rins.key] = rins.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun optimizeVariableCopying() {
|
|
||||||
for(blk in blocks) {
|
|
||||||
|
|
||||||
val instructionsToReplace = mutableMapOf<Int, Instruction>()
|
|
||||||
|
|
||||||
blk.instructions.asSequence().withIndex().windowed(2).toList().forEach {
|
|
||||||
when (it[0].value.opcode) {
|
|
||||||
Opcode.PUSH_VAR_BYTE ->
|
|
||||||
if (it[1].value.opcode == Opcode.POP_VAR_BYTE) {
|
|
||||||
if (it[0].value.callLabel == it[1].value.callLabel) {
|
|
||||||
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
|
|
||||||
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Opcode.PUSH_VAR_WORD ->
|
|
||||||
if (it[1].value.opcode == Opcode.POP_VAR_WORD) {
|
|
||||||
if (it[0].value.callLabel == it[1].value.callLabel) {
|
|
||||||
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
|
|
||||||
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Opcode.PUSH_VAR_FLOAT ->
|
|
||||||
if (it[1].value.opcode == Opcode.POP_VAR_FLOAT) {
|
|
||||||
if (it[0].value.callLabel == it[1].value.callLabel) {
|
|
||||||
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
|
|
||||||
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Opcode.PUSH_MEM_B, Opcode.PUSH_MEM_UB ->
|
|
||||||
if(it[1].value.opcode == Opcode.POP_MEM_BYTE) {
|
|
||||||
if(it[0].value.arg == it[1].value.arg) {
|
|
||||||
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
|
|
||||||
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Opcode.PUSH_MEM_W, Opcode.PUSH_MEM_UW ->
|
|
||||||
if(it[1].value.opcode == Opcode.POP_MEM_WORD) {
|
|
||||||
if(it[0].value.arg == it[1].value.arg) {
|
|
||||||
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
|
|
||||||
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Opcode.PUSH_MEM_FLOAT ->
|
|
||||||
if(it[1].value.opcode == Opcode.POP_MEM_FLOAT) {
|
|
||||||
if(it[0].value.arg == it[1].value.arg) {
|
|
||||||
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
|
|
||||||
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (rins in instructionsToReplace) {
|
|
||||||
blk.instructions[rins.key] = rins.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun optimizeDataConversionAndUselessDiscards() {
|
|
||||||
// - push value followed by a data type conversion -> push the value in the correct type and remove the conversion
|
|
||||||
// - push something followed by a discard -> remove both
|
|
||||||
val instructionsToReplace = mutableMapOf<Int, Instruction>()
|
|
||||||
|
|
||||||
fun optimizeDiscardAfterPush(index0: Int, index1: Int, ins1: Instruction) {
|
|
||||||
if (ins1.opcode == Opcode.DISCARD_FLOAT || ins1.opcode == Opcode.DISCARD_WORD || ins1.opcode == Opcode.DISCARD_BYTE) {
|
|
||||||
instructionsToReplace[index0] = Instruction(Opcode.NOP)
|
|
||||||
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun optimizeFloatConversion(index0: Int, index1: Int, ins1: Instruction) {
|
|
||||||
when (ins1.opcode) {
|
|
||||||
Opcode.DISCARD_FLOAT -> {
|
|
||||||
instructionsToReplace[index0] = Instruction(Opcode.NOP)
|
|
||||||
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
Opcode.DISCARD_BYTE, Opcode.DISCARD_WORD -> throw CompilerException("invalid discard type following a float")
|
|
||||||
else -> throw CompilerException("invalid conversion opcode ${ins1.opcode} following a float")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun optimizeWordConversion(index0: Int, ins0: Instruction, index1: Int, ins1: Instruction) {
|
|
||||||
when (ins1.opcode) {
|
|
||||||
Opcode.CAST_UW_TO_B, Opcode.CAST_W_TO_B -> {
|
|
||||||
val ins = Instruction(Opcode.PUSH_BYTE, ins0.arg!!.cast(DataType.BYTE))
|
|
||||||
instructionsToReplace[index0] = ins
|
|
||||||
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
Opcode.CAST_W_TO_UB, Opcode.CAST_UW_TO_UB -> {
|
|
||||||
val ins = Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.UBYTE, ins0.arg!!.integerValue() and 255))
|
|
||||||
instructionsToReplace[index0] = ins
|
|
||||||
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
Opcode.MSB -> {
|
|
||||||
val ins = Instruction(Opcode.PUSH_BYTE, RuntimeValue(DataType.UBYTE, ins0.arg!!.integerValue() ushr 8 and 255))
|
|
||||||
instructionsToReplace[index0] = ins
|
|
||||||
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
Opcode.CAST_W_TO_F, Opcode.CAST_UW_TO_F -> {
|
|
||||||
val ins = Instruction(Opcode.PUSH_FLOAT, RuntimeValue(DataType.FLOAT, ins0.arg!!.integerValue().toDouble()))
|
|
||||||
instructionsToReplace[index0] = ins
|
|
||||||
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
Opcode.CAST_UW_TO_W -> {
|
|
||||||
val cv = ins0.arg!!.cast(DataType.WORD)
|
|
||||||
instructionsToReplace[index0] = Instruction(Opcode.PUSH_WORD, cv)
|
|
||||||
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
Opcode.CAST_W_TO_UW -> {
|
|
||||||
val cv = ins0.arg!!.cast(DataType.UWORD)
|
|
||||||
instructionsToReplace[index0] = Instruction(Opcode.PUSH_WORD, cv)
|
|
||||||
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
Opcode.DISCARD_WORD -> {
|
|
||||||
instructionsToReplace[index0] = Instruction(Opcode.NOP)
|
|
||||||
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
Opcode.DISCARD_BYTE, Opcode.DISCARD_FLOAT -> throw CompilerException("invalid discard type following a byte")
|
|
||||||
else -> throw CompilerException("invalid conversion opcode ${ins1.opcode} following a word")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun optimizeByteConversion(index0: Int, ins0: Instruction, index1: Int, ins1: Instruction) {
|
|
||||||
when (ins1.opcode) {
|
|
||||||
Opcode.CAST_B_TO_UB, Opcode.CAST_UB_TO_B,
|
|
||||||
Opcode.CAST_W_TO_B, Opcode.CAST_W_TO_UB,
|
|
||||||
Opcode.CAST_UW_TO_B, Opcode.CAST_UW_TO_UB -> instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
|
||||||
Opcode.MSB -> throw CompilerException("msb of a byte")
|
|
||||||
Opcode.CAST_UB_TO_UW -> {
|
|
||||||
val ins = Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.UWORD, ins0.arg!!.integerValue()))
|
|
||||||
instructionsToReplace[index0] = ins
|
|
||||||
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
Opcode.CAST_B_TO_W -> {
|
|
||||||
val ins = Instruction(Opcode.PUSH_WORD, RuntimeValue(DataType.WORD, ins0.arg!!.integerValue()))
|
|
||||||
instructionsToReplace[index0] = ins
|
|
||||||
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
Opcode.CAST_B_TO_UW -> {
|
|
||||||
val ins = Instruction(Opcode.PUSH_WORD, ins0.arg!!.cast(DataType.UWORD))
|
|
||||||
instructionsToReplace[index0] = ins
|
|
||||||
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
Opcode.CAST_UB_TO_W -> {
|
|
||||||
val ins = Instruction(Opcode.PUSH_WORD, ins0.arg!!.cast(DataType.WORD))
|
|
||||||
instructionsToReplace[index0] = ins
|
|
||||||
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
Opcode.CAST_B_TO_F, Opcode.CAST_UB_TO_F -> {
|
|
||||||
val ins = Instruction(Opcode.PUSH_FLOAT, RuntimeValue(DataType.FLOAT, ins0.arg!!.integerValue().toDouble()))
|
|
||||||
instructionsToReplace[index0] = ins
|
|
||||||
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
Opcode.CAST_W_TO_F, Opcode.CAST_UW_TO_F -> throw CompilerException("invalid conversion following a byte")
|
|
||||||
Opcode.DISCARD_BYTE -> {
|
|
||||||
instructionsToReplace[index0] = Instruction(Opcode.NOP)
|
|
||||||
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
|
||||||
}
|
|
||||||
Opcode.DISCARD_WORD, Opcode.DISCARD_FLOAT -> throw CompilerException("invalid discard type following a byte")
|
|
||||||
Opcode.MKWORD -> {}
|
|
||||||
else -> throw CompilerException("invalid conversion opcode ${ins1.opcode}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(blk in blocks) {
|
|
||||||
instructionsToReplace.clear()
|
|
||||||
|
|
||||||
val typeConversionOpcodes = setOf(
|
|
||||||
Opcode.MSB,
|
|
||||||
Opcode.MKWORD,
|
|
||||||
Opcode.CAST_UB_TO_B,
|
|
||||||
Opcode.CAST_UB_TO_UW,
|
|
||||||
Opcode.CAST_UB_TO_W,
|
|
||||||
Opcode.CAST_UB_TO_F,
|
|
||||||
Opcode.CAST_B_TO_UB,
|
|
||||||
Opcode.CAST_B_TO_UW,
|
|
||||||
Opcode.CAST_B_TO_W,
|
|
||||||
Opcode.CAST_B_TO_F,
|
|
||||||
Opcode.CAST_UW_TO_UB,
|
|
||||||
Opcode.CAST_UW_TO_B,
|
|
||||||
Opcode.CAST_UW_TO_W,
|
|
||||||
Opcode.CAST_UW_TO_F,
|
|
||||||
Opcode.CAST_W_TO_UB,
|
|
||||||
Opcode.CAST_W_TO_B,
|
|
||||||
Opcode.CAST_W_TO_UW,
|
|
||||||
Opcode.CAST_W_TO_F,
|
|
||||||
Opcode.CAST_F_TO_UB,
|
|
||||||
Opcode.CAST_F_TO_B,
|
|
||||||
Opcode.CAST_F_TO_UW,
|
|
||||||
Opcode.CAST_F_TO_W,
|
|
||||||
Opcode.DISCARD_BYTE,
|
|
||||||
Opcode.DISCARD_WORD,
|
|
||||||
Opcode.DISCARD_FLOAT
|
|
||||||
)
|
|
||||||
blk.instructions.asSequence().withIndex().windowed(2).toList().forEach {
|
|
||||||
if (it[1].value.opcode in typeConversionOpcodes) {
|
|
||||||
when (it[0].value.opcode) {
|
|
||||||
Opcode.PUSH_BYTE -> optimizeByteConversion(it[0].index, it[0].value, it[1].index, it[1].value)
|
|
||||||
Opcode.PUSH_WORD -> optimizeWordConversion(it[0].index, it[0].value, it[1].index, it[1].value)
|
|
||||||
Opcode.PUSH_FLOAT -> optimizeFloatConversion(it[0].index, it[1].index, it[1].value)
|
|
||||||
Opcode.PUSH_VAR_FLOAT,
|
|
||||||
Opcode.PUSH_VAR_WORD,
|
|
||||||
Opcode.PUSH_VAR_BYTE,
|
|
||||||
Opcode.PUSH_MEM_B, Opcode.PUSH_MEM_UB,
|
|
||||||
Opcode.PUSH_MEM_W, Opcode.PUSH_MEM_UW,
|
|
||||||
Opcode.PUSH_MEM_FLOAT -> optimizeDiscardAfterPush(it[0].index, it[1].index, it[1].value)
|
|
||||||
else -> {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (rins in instructionsToReplace) {
|
|
||||||
blk.instructions[rins.key] = rins.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun variable(scopedname: String, decl: VarDecl) {
|
|
||||||
when(decl.type) {
|
|
||||||
VarDeclType.VAR -> {
|
|
||||||
// var decls that are defined inside of a StructDecl are skipped in the output
|
|
||||||
// because every occurrence of the members will have a separate mangled vardecl for that occurrence
|
|
||||||
if(decl.parent is StructDecl)
|
|
||||||
return
|
|
||||||
|
|
||||||
val valueparams = VariableParameters(decl.zeropage, decl.struct)
|
|
||||||
val value = when(decl.datatype) {
|
|
||||||
in NumericDatatypes -> {
|
|
||||||
RuntimeValue(decl.datatype, (decl.value as NumericLiteralValue).number)
|
|
||||||
}
|
|
||||||
in StringDatatypes -> {
|
|
||||||
val litval = (decl.value as ReferenceLiteralValue)
|
|
||||||
if(litval.heapId==null)
|
|
||||||
throw CompilerException("string should already be in the heap")
|
|
||||||
RuntimeValue(decl.datatype, heapId = litval.heapId)
|
|
||||||
}
|
|
||||||
in ArrayDatatypes -> {
|
|
||||||
val litval = (decl.value as? ReferenceLiteralValue)
|
|
||||||
if(litval!=null && litval.heapId==null)
|
|
||||||
throw CompilerException("array should already be in the heap")
|
|
||||||
if(litval!=null){
|
|
||||||
RuntimeValue(decl.datatype, heapId = litval.heapId)
|
|
||||||
} else {
|
|
||||||
throw CompilerException("initialization value expected")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.STRUCT -> {
|
|
||||||
// struct variables have been flattened already
|
|
||||||
return
|
|
||||||
}
|
|
||||||
else -> throw CompilerException("weird datatype")
|
|
||||||
}
|
|
||||||
currentBlock.variables.add(Variable(scopedname, value, valueparams))
|
|
||||||
}
|
|
||||||
VarDeclType.MEMORY -> {
|
|
||||||
// note that constants are all folded away, but assembly code may still refer to them
|
|
||||||
val lv = decl.value as NumericLiteralValue
|
|
||||||
if(lv.type!= DataType.UWORD && lv.type!= DataType.UBYTE)
|
|
||||||
throw CompilerException("expected integer memory address $lv")
|
|
||||||
currentBlock.memoryPointers[scopedname] = Pair(lv.number.toInt(), decl.datatype)
|
|
||||||
}
|
|
||||||
VarDeclType.CONST -> {
|
|
||||||
// note that constants are all folded away, but assembly code may still refer to them (if their integers)
|
|
||||||
// floating point constants are not generated at all!!
|
|
||||||
val lv = decl.value as NumericLiteralValue
|
|
||||||
if(lv.type in IntegerDatatypes)
|
|
||||||
currentBlock.memoryPointers[scopedname] = Pair(lv.number.toInt(), decl.datatype)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun instr(opcode: Opcode, arg: RuntimeValue? = null, arg2: RuntimeValue? = null, callLabel: String? = null, callLabel2: String? = null) {
|
|
||||||
currentBlock.instructions.add(Instruction(opcode, arg, arg2, callLabel, callLabel2))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun label(labelname: String, asmProc: Boolean=false) {
|
|
||||||
val instr = LabelInstr(labelname, asmProc)
|
|
||||||
currentBlock.instructions.add(instr)
|
|
||||||
currentBlock.labels[labelname] = instr
|
|
||||||
}
|
|
||||||
|
|
||||||
fun line(position: Position) {
|
|
||||||
currentBlock.instructions.add(Instruction(Opcode.LINE, callLabel = "${position.line} ${position.file}"))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun removeLastInstruction() {
|
|
||||||
currentBlock.instructions.removeAt(currentBlock.instructions.lastIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun memoryPointer(name: String, address: Int, datatype: DataType) {
|
|
||||||
currentBlock.memoryPointers[name] = Pair(address, datatype)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun newBlock(name: String, address: Int?, options: Set<String>) {
|
|
||||||
currentBlock = ProgramBlock(name, address, force_output = "force_output" in options)
|
|
||||||
blocks.add(currentBlock)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun writeCode(out: PrintStream, embeddedLabels: Boolean=true) {
|
|
||||||
out.println("; stackVM program code for '$name'")
|
|
||||||
writeMemory(out)
|
|
||||||
writeHeap(out)
|
|
||||||
for(blk in blocks) {
|
|
||||||
writeBlock(out, blk, embeddedLabels)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun writeHeap(out: PrintStream) {
|
|
||||||
out.println("%heap")
|
|
||||||
heap.allEntries().forEach {
|
|
||||||
out.print("${it.key} ${it.value.type.name.toLowerCase()} ")
|
|
||||||
when {
|
|
||||||
it.value.str!=null ->
|
|
||||||
out.println("\"${escape(it.value.str!!)}\"")
|
|
||||||
it.value.array!=null -> {
|
|
||||||
// this array can contain both normal integers, and pointer values
|
|
||||||
val arrayvalues = it.value.array!!.map { av ->
|
|
||||||
when {
|
|
||||||
av.integer!=null -> av.integer.toString()
|
|
||||||
av.addressOf!=null -> {
|
|
||||||
if(av.addressOf.scopedname==null)
|
|
||||||
throw CompilerException("AddressOf scopedname should have been set")
|
|
||||||
else
|
|
||||||
"&${av.addressOf.scopedname}"
|
|
||||||
}
|
|
||||||
else -> throw CompilerException("weird array value")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out.println(arrayvalues)
|
|
||||||
}
|
|
||||||
it.value.doubleArray!=null ->
|
|
||||||
out.println(it.value.doubleArray!!.toList())
|
|
||||||
else -> throw CompilerException("invalid heap entry $it")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out.println("%end_heap")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun writeBlock(out: PrintStream, blk: ProgramBlock, embeddedLabels: Boolean) {
|
|
||||||
out.println("\n%block ${blk.name} ${blk.address?.toString(16) ?: ""}")
|
|
||||||
|
|
||||||
out.println("%variables")
|
|
||||||
for (variable in blk.variables) {
|
|
||||||
if(variable.params.zp==ZeropageWish.REQUIRE_ZEROPAGE)
|
|
||||||
throw CompilerException("zp conflict")
|
|
||||||
val valuestr = variable.value.toString()
|
|
||||||
val struct = if(variable.params.memberOfStruct==null) "" else "struct=${variable.params.memberOfStruct.name}"
|
|
||||||
out.println("${variable.scopedname} ${variable.value.type.name.toLowerCase()} $valuestr zp=${variable.params.zp} s=$struct")
|
|
||||||
}
|
|
||||||
out.println("%end_variables")
|
|
||||||
out.println("%memorypointers")
|
|
||||||
for (iconst in blk.memoryPointers) {
|
|
||||||
out.println("${iconst.key} ${iconst.value.second.name.toLowerCase()} uw:${iconst.value.first.toString(16)}")
|
|
||||||
}
|
|
||||||
out.println("%end_memorypointers")
|
|
||||||
out.println("%instructions")
|
|
||||||
val labels = blk.labels.entries.associateBy({ it.value }) { it.key }
|
|
||||||
for (instr in blk.instructions) {
|
|
||||||
if (!embeddedLabels) {
|
|
||||||
val label = labels[instr]
|
|
||||||
if (label != null)
|
|
||||||
out.println("$label:")
|
|
||||||
} else {
|
|
||||||
out.println(instr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out.println("%end_instructions")
|
|
||||||
|
|
||||||
out.println("%end_block")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun writeMemory(out: PrintStream) {
|
|
||||||
out.println("%memory")
|
|
||||||
if (memory.isNotEmpty())
|
|
||||||
TODO("add support for writing/reading initial memory values")
|
|
||||||
out.println("%end_memory")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,291 +0,0 @@
|
|||||||
package compiler.intermediate
|
|
||||||
|
|
||||||
enum class Opcode {
|
|
||||||
|
|
||||||
// pushing values on the (evaluation) stack
|
|
||||||
PUSH_BYTE, // push byte value
|
|
||||||
PUSH_WORD, // push word value (or 'address' of string / array)
|
|
||||||
PUSH_FLOAT, // push float value
|
|
||||||
PUSH_MEM_B, // push byte value from memory to stack
|
|
||||||
PUSH_MEM_UB, // push unsigned byte value from memory to stack
|
|
||||||
PUSH_MEM_W, // push word value from memory to stack
|
|
||||||
PUSH_MEM_UW, // push unsigned word value from memory to stack
|
|
||||||
PUSH_MEM_FLOAT, // push float value from memory to stack
|
|
||||||
PUSH_MEMREAD, // push memory value from address that's on the stack
|
|
||||||
PUSH_VAR_BYTE, // push byte variable (ubyte, byte)
|
|
||||||
PUSH_VAR_WORD, // push word variable (uword, word)
|
|
||||||
PUSH_VAR_FLOAT, // push float variable
|
|
||||||
PUSH_REGAX_WORD, // push registers A/X as a 16-bit word
|
|
||||||
PUSH_REGAY_WORD, // push registers A/Y as a 16-bit word
|
|
||||||
PUSH_REGXY_WORD, // push registers X/Y as a 16-bit word
|
|
||||||
PUSH_ADDR_HEAPVAR, // push the address of the variable that's on the heap (string or array)
|
|
||||||
DUP_B, // duplicate the top byte on the stack
|
|
||||||
DUP_W, // duplicate the top word on the stack
|
|
||||||
|
|
||||||
// popping values off the (evaluation) stack, possibly storing them in another location
|
|
||||||
DISCARD_BYTE, // discard top byte value
|
|
||||||
DISCARD_WORD, // discard top word value
|
|
||||||
DISCARD_FLOAT, // discard top float value
|
|
||||||
POP_MEM_BYTE, // pop (u)byte value into destination memory address
|
|
||||||
POP_MEM_WORD, // pop (u)word value into destination memory address
|
|
||||||
POP_MEM_FLOAT, // pop float value into destination memory address
|
|
||||||
POP_MEMWRITE, // pop address and byte stack and write the byte to the memory address
|
|
||||||
POP_VAR_BYTE, // pop (u)byte value into variable
|
|
||||||
POP_VAR_WORD, // pop (u)word value into variable
|
|
||||||
POP_VAR_FLOAT, // pop float value into variable
|
|
||||||
POP_REGAX_WORD, // pop uword from stack into A/X registers
|
|
||||||
POP_REGAY_WORD, // pop uword from stack into A/Y registers
|
|
||||||
POP_REGXY_WORD, // pop uword from stack into X/Y registers
|
|
||||||
|
|
||||||
// numeric arithmetic
|
|
||||||
ADD_UB,
|
|
||||||
ADD_B,
|
|
||||||
ADD_UW,
|
|
||||||
ADD_W,
|
|
||||||
ADD_F,
|
|
||||||
SUB_UB,
|
|
||||||
SUB_B,
|
|
||||||
SUB_UW,
|
|
||||||
SUB_W,
|
|
||||||
SUB_F,
|
|
||||||
MUL_UB,
|
|
||||||
MUL_B,
|
|
||||||
MUL_UW,
|
|
||||||
MUL_W,
|
|
||||||
MUL_F,
|
|
||||||
IDIV_UB,
|
|
||||||
IDIV_B,
|
|
||||||
IDIV_UW,
|
|
||||||
IDIV_W,
|
|
||||||
DIV_F,
|
|
||||||
REMAINDER_UB, // signed remainder is undefined/unimplemented
|
|
||||||
REMAINDER_UW, // signed remainder is undefined/unimplemented
|
|
||||||
POW_F,
|
|
||||||
NEG_B,
|
|
||||||
NEG_W,
|
|
||||||
NEG_F,
|
|
||||||
ABS_B,
|
|
||||||
ABS_W,
|
|
||||||
ABS_F,
|
|
||||||
|
|
||||||
// bit shifts and bitwise arithmetic
|
|
||||||
SHIFTEDL_BYTE, // shifts stack value rather than in-place mem/var
|
|
||||||
SHIFTEDL_WORD, // shifts stack value rather than in-place mem/var
|
|
||||||
SHIFTEDR_UBYTE, // shifts stack value rather than in-place mem/var
|
|
||||||
SHIFTEDR_SBYTE, // shifts stack value rather than in-place mem/var
|
|
||||||
SHIFTEDR_UWORD, // shifts stack value rather than in-place mem/var
|
|
||||||
SHIFTEDR_SWORD, // shifts stack value rather than in-place mem/var
|
|
||||||
SHL_BYTE,
|
|
||||||
SHL_WORD,
|
|
||||||
SHL_MEM_BYTE,
|
|
||||||
SHL_MEM_WORD,
|
|
||||||
SHL_VAR_BYTE,
|
|
||||||
SHL_VAR_WORD,
|
|
||||||
SHR_UBYTE,
|
|
||||||
SHR_SBYTE,
|
|
||||||
SHR_UWORD,
|
|
||||||
SHR_SWORD,
|
|
||||||
SHR_MEM_UBYTE,
|
|
||||||
SHR_MEM_SBYTE,
|
|
||||||
SHR_MEM_UWORD,
|
|
||||||
SHR_MEM_SWORD,
|
|
||||||
SHR_VAR_UBYTE,
|
|
||||||
SHR_VAR_SBYTE,
|
|
||||||
SHR_VAR_UWORD,
|
|
||||||
SHR_VAR_SWORD,
|
|
||||||
ROL_BYTE,
|
|
||||||
ROL_WORD,
|
|
||||||
ROL_MEM_BYTE,
|
|
||||||
ROL_MEM_WORD,
|
|
||||||
ROL_VAR_BYTE,
|
|
||||||
ROL_VAR_WORD,
|
|
||||||
ROR_BYTE,
|
|
||||||
ROR_WORD,
|
|
||||||
ROR_MEM_BYTE,
|
|
||||||
ROR_MEM_WORD,
|
|
||||||
ROR_VAR_BYTE,
|
|
||||||
ROR_VAR_WORD,
|
|
||||||
ROL2_BYTE,
|
|
||||||
ROL2_WORD,
|
|
||||||
ROL2_MEM_BYTE,
|
|
||||||
ROL2_MEM_WORD,
|
|
||||||
ROL2_VAR_BYTE,
|
|
||||||
ROL2_VAR_WORD,
|
|
||||||
ROR2_BYTE,
|
|
||||||
ROR2_WORD,
|
|
||||||
ROR2_MEM_BYTE,
|
|
||||||
ROR2_MEM_WORD,
|
|
||||||
ROR2_VAR_BYTE,
|
|
||||||
ROR2_VAR_WORD,
|
|
||||||
BITAND_BYTE,
|
|
||||||
BITAND_WORD,
|
|
||||||
BITOR_BYTE,
|
|
||||||
BITOR_WORD,
|
|
||||||
BITXOR_BYTE,
|
|
||||||
BITXOR_WORD,
|
|
||||||
INV_BYTE,
|
|
||||||
INV_WORD,
|
|
||||||
|
|
||||||
// numeric type conversions
|
|
||||||
MSB, // note: lsb is equivalent to CAST_UW_TO_UB or CAST_W_TO_UB
|
|
||||||
MKWORD, // create a word from lsb + msb
|
|
||||||
CAST_UB_TO_B,
|
|
||||||
CAST_UB_TO_UW,
|
|
||||||
CAST_UB_TO_W,
|
|
||||||
CAST_UB_TO_F,
|
|
||||||
CAST_B_TO_UB,
|
|
||||||
CAST_B_TO_UW,
|
|
||||||
CAST_B_TO_W,
|
|
||||||
CAST_B_TO_F,
|
|
||||||
CAST_W_TO_UB,
|
|
||||||
CAST_W_TO_B,
|
|
||||||
CAST_W_TO_UW,
|
|
||||||
CAST_W_TO_F,
|
|
||||||
CAST_UW_TO_UB,
|
|
||||||
CAST_UW_TO_B,
|
|
||||||
CAST_UW_TO_W,
|
|
||||||
CAST_UW_TO_F,
|
|
||||||
CAST_F_TO_UB,
|
|
||||||
CAST_F_TO_B,
|
|
||||||
CAST_F_TO_UW,
|
|
||||||
CAST_F_TO_W,
|
|
||||||
|
|
||||||
// logical operations
|
|
||||||
AND_BYTE,
|
|
||||||
AND_WORD,
|
|
||||||
OR_BYTE,
|
|
||||||
OR_WORD,
|
|
||||||
XOR_BYTE,
|
|
||||||
XOR_WORD,
|
|
||||||
NOT_BYTE,
|
|
||||||
NOT_WORD,
|
|
||||||
|
|
||||||
// increment, decrement
|
|
||||||
INC_VAR_B,
|
|
||||||
INC_VAR_UB,
|
|
||||||
INC_VAR_W,
|
|
||||||
INC_VAR_UW,
|
|
||||||
INC_VAR_F,
|
|
||||||
DEC_VAR_B,
|
|
||||||
DEC_VAR_UB,
|
|
||||||
DEC_VAR_W,
|
|
||||||
DEC_VAR_UW,
|
|
||||||
DEC_VAR_F,
|
|
||||||
INC_MEMORY, // increment direct address
|
|
||||||
DEC_MEMORY, // decrement direct address
|
|
||||||
POP_INC_MEMORY, // increment address from stack
|
|
||||||
POP_DEC_MEMORY, // decrement address from address
|
|
||||||
|
|
||||||
// comparisons
|
|
||||||
LESS_B,
|
|
||||||
LESS_UB,
|
|
||||||
LESS_W,
|
|
||||||
LESS_UW,
|
|
||||||
LESS_F,
|
|
||||||
GREATER_B,
|
|
||||||
GREATER_UB,
|
|
||||||
GREATER_W,
|
|
||||||
GREATER_UW,
|
|
||||||
GREATER_F,
|
|
||||||
LESSEQ_B,
|
|
||||||
LESSEQ_UB,
|
|
||||||
LESSEQ_W,
|
|
||||||
LESSEQ_UW,
|
|
||||||
LESSEQ_F,
|
|
||||||
GREATEREQ_B,
|
|
||||||
GREATEREQ_UB,
|
|
||||||
GREATEREQ_W,
|
|
||||||
GREATEREQ_UW,
|
|
||||||
GREATEREQ_F,
|
|
||||||
EQUAL_BYTE,
|
|
||||||
EQUAL_WORD,
|
|
||||||
EQUAL_F,
|
|
||||||
NOTEQUAL_BYTE,
|
|
||||||
NOTEQUAL_WORD,
|
|
||||||
NOTEQUAL_F,
|
|
||||||
CMP_B, // sets processor status flags based on comparison, instead of pushing a result value
|
|
||||||
CMP_UB, // sets processor status flags based on comparison, instead of pushing a result value
|
|
||||||
CMP_W, // sets processor status flags based on comparison, instead of pushing a result value
|
|
||||||
CMP_UW, // sets processor status flags based on comparison, instead of pushing a result value
|
|
||||||
|
|
||||||
// array access and simple manipulations
|
|
||||||
READ_INDEXED_VAR_BYTE,
|
|
||||||
READ_INDEXED_VAR_WORD,
|
|
||||||
READ_INDEXED_VAR_FLOAT,
|
|
||||||
WRITE_INDEXED_VAR_BYTE,
|
|
||||||
WRITE_INDEXED_VAR_WORD,
|
|
||||||
WRITE_INDEXED_VAR_FLOAT,
|
|
||||||
INC_INDEXED_VAR_B,
|
|
||||||
INC_INDEXED_VAR_UB,
|
|
||||||
INC_INDEXED_VAR_W,
|
|
||||||
INC_INDEXED_VAR_UW,
|
|
||||||
INC_INDEXED_VAR_FLOAT,
|
|
||||||
DEC_INDEXED_VAR_B,
|
|
||||||
DEC_INDEXED_VAR_UB,
|
|
||||||
DEC_INDEXED_VAR_W,
|
|
||||||
DEC_INDEXED_VAR_UW,
|
|
||||||
DEC_INDEXED_VAR_FLOAT,
|
|
||||||
|
|
||||||
// branching, without consuming a value from the stack
|
|
||||||
JUMP,
|
|
||||||
BCS, // branch if carry set
|
|
||||||
BCC, // branch if carry clear
|
|
||||||
BZ, // branch if zero flag
|
|
||||||
BNZ, // branch if not zero flag
|
|
||||||
BNEG, // branch if negative flag
|
|
||||||
BPOS, // branch if not negative flag
|
|
||||||
BVS, // branch if overflow flag
|
|
||||||
BVC, // branch if not overflow flag
|
|
||||||
// branching, based on value on the stack (which is consumed)
|
|
||||||
JZ, // branch if value is zero (byte)
|
|
||||||
JNZ, // branch if value is not zero (byte)
|
|
||||||
JZW, // branch if value is zero (word)
|
|
||||||
JNZW, // branch if value is not zero (word)
|
|
||||||
|
|
||||||
// subroutines
|
|
||||||
CALL,
|
|
||||||
RETURN,
|
|
||||||
SYSCALL,
|
|
||||||
START_PROCDEF,
|
|
||||||
END_PROCDEF,
|
|
||||||
|
|
||||||
// misc
|
|
||||||
SEC, // set carry status flag NOTE: is mostly fake, carry flag is not affected by any numeric operations
|
|
||||||
CLC, // clear carry status flag NOTE: is mostly fake, carry flag is not affected by any numeric operations
|
|
||||||
SEI, // set irq-disable status flag
|
|
||||||
CLI, // clear irq-disable status flag
|
|
||||||
CARRY_TO_A, // load var/register A with carry status bit
|
|
||||||
RSAVE, // save all internal registers and status flags
|
|
||||||
RSAVEX, // save just X (the evaluation stack pointer)
|
|
||||||
RRESTORE, // restore all internal registers and status flags
|
|
||||||
RRESTOREX, // restore just X (the evaluation stack pointer)
|
|
||||||
|
|
||||||
NOP, // do nothing
|
|
||||||
BREAKPOINT, // breakpoint
|
|
||||||
TERMINATE, // end the program
|
|
||||||
LINE, // track source file line number
|
|
||||||
INLINE_ASSEMBLY, // container to hold inline raw assembly code
|
|
||||||
INCLUDE_FILE // directive to include a file at this position in the memory of the program
|
|
||||||
}
|
|
||||||
|
|
||||||
val opcodesWithVarArgument = setOf(
|
|
||||||
Opcode.INC_VAR_B, Opcode.INC_VAR_W, Opcode.DEC_VAR_B, Opcode.DEC_VAR_W,
|
|
||||||
Opcode.INC_VAR_UB, Opcode.INC_VAR_UW, Opcode.DEC_VAR_UB, Opcode.DEC_VAR_UW,
|
|
||||||
Opcode.SHR_VAR_SBYTE, Opcode.SHR_VAR_UBYTE, Opcode.SHR_VAR_SWORD, Opcode.SHR_VAR_UWORD,
|
|
||||||
Opcode.SHL_VAR_BYTE, Opcode.SHL_VAR_WORD,
|
|
||||||
Opcode.ROL_VAR_BYTE, Opcode.ROL_VAR_WORD, Opcode.ROR_VAR_BYTE, Opcode.ROR_VAR_WORD,
|
|
||||||
Opcode.ROL2_VAR_BYTE, Opcode.ROL2_VAR_WORD, Opcode.ROR2_VAR_BYTE, Opcode.ROR2_VAR_WORD,
|
|
||||||
Opcode.POP_VAR_BYTE, Opcode.POP_VAR_WORD, Opcode.POP_VAR_FLOAT,
|
|
||||||
Opcode.PUSH_VAR_BYTE, Opcode.PUSH_VAR_WORD, Opcode.PUSH_VAR_FLOAT, Opcode.PUSH_ADDR_HEAPVAR,
|
|
||||||
Opcode.READ_INDEXED_VAR_BYTE, Opcode.READ_INDEXED_VAR_WORD, Opcode.READ_INDEXED_VAR_FLOAT,
|
|
||||||
Opcode.WRITE_INDEXED_VAR_BYTE, Opcode.WRITE_INDEXED_VAR_WORD, Opcode.WRITE_INDEXED_VAR_FLOAT,
|
|
||||||
Opcode.INC_INDEXED_VAR_UB, Opcode.INC_INDEXED_VAR_B, Opcode.INC_INDEXED_VAR_UW,
|
|
||||||
Opcode.INC_INDEXED_VAR_W, Opcode.INC_INDEXED_VAR_FLOAT,
|
|
||||||
Opcode.DEC_INDEXED_VAR_UB, Opcode.DEC_INDEXED_VAR_B, Opcode.DEC_INDEXED_VAR_UW,
|
|
||||||
Opcode.DEC_INDEXED_VAR_W, Opcode.DEC_INDEXED_VAR_FLOAT
|
|
||||||
)
|
|
||||||
|
|
||||||
val branchOpcodes = setOf(
|
|
||||||
Opcode.BCS, Opcode.BCC, Opcode.BZ, Opcode.BNZ,
|
|
||||||
Opcode.BNEG, Opcode.BPOS, Opcode.BVS, Opcode.BVC
|
|
||||||
)
|
|
@ -1,47 +0,0 @@
|
|||||||
package prog8.vm.stackvm
|
|
||||||
|
|
||||||
import prog8.printSoftwareHeader
|
|
||||||
import prog8.vm.astvm.ScreenDialog
|
|
||||||
import java.awt.EventQueue
|
|
||||||
import javax.swing.Timer
|
|
||||||
import kotlin.system.exitProcess
|
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
|
||||||
stackVmMain(args)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun stackVmMain(args: Array<String>) {
|
|
||||||
printSoftwareHeader("StackVM")
|
|
||||||
|
|
||||||
if(args.size != 1) {
|
|
||||||
System.err.println("requires one argument: name of stackvm sourcecode file")
|
|
||||||
exitProcess(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
val program = Program.load(args.first())
|
|
||||||
val vm = StackVm(traceOutputFile = null)
|
|
||||||
val dialog = ScreenDialog("StackVM")
|
|
||||||
vm.load(program, dialog.canvas)
|
|
||||||
EventQueue.invokeLater {
|
|
||||||
dialog.pack()
|
|
||||||
dialog.isVisible = true
|
|
||||||
dialog.start()
|
|
||||||
|
|
||||||
val programTimer = Timer(10) { a ->
|
|
||||||
try {
|
|
||||||
vm.step()
|
|
||||||
} catch(bp: VmBreakpointException) {
|
|
||||||
println("Breakpoint: execution halted. Press enter to resume.")
|
|
||||||
readLine()
|
|
||||||
} catch (tx: VmTerminationException) {
|
|
||||||
println("Execution halted: ${tx.message}")
|
|
||||||
(a.source as Timer).stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val irqTimer = Timer(1000/60) { a -> vm.irq(a.`when`) }
|
|
||||||
|
|
||||||
programTimer.start()
|
|
||||||
irqTimer.start()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,302 +0,0 @@
|
|||||||
package prog8.vm.stackvm
|
|
||||||
|
|
||||||
import prog8.ast.antlr.unescape
|
|
||||||
import prog8.ast.base.*
|
|
||||||
import prog8.ast.expressions.AddressOf
|
|
||||||
import prog8.ast.expressions.IdentifierReference
|
|
||||||
import prog8.compiler.HeapValues
|
|
||||||
import prog8.compiler.IntegerOrAddressOf
|
|
||||||
import prog8.compiler.intermediate.Instruction
|
|
||||||
import prog8.compiler.intermediate.LabelInstr
|
|
||||||
import prog8.compiler.intermediate.Opcode
|
|
||||||
import prog8.compiler.intermediate.opcodesWithVarArgument
|
|
||||||
import prog8.vm.RuntimeValue
|
|
||||||
import java.io.File
|
|
||||||
import java.util.regex.Pattern
|
|
||||||
|
|
||||||
|
|
||||||
class Program (val name: String,
|
|
||||||
val program: MutableList<Instruction>,
|
|
||||||
val variables: Map<String, RuntimeValue>,
|
|
||||||
val memoryPointers: Map<String, Pair<Int, DataType>>,
|
|
||||||
val labels: Map<String, Int>,
|
|
||||||
val memory: Map<Int, List<RuntimeValue>>,
|
|
||||||
val heap: HeapValues)
|
|
||||||
{
|
|
||||||
init {
|
|
||||||
// add end of program marker and some sentinel instructions, to correctly connect all others
|
|
||||||
program.add(LabelInstr("____program_end", false))
|
|
||||||
program.add(Instruction(Opcode.TERMINATE))
|
|
||||||
program.add(Instruction(Opcode.NOP))
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun load(filename: String): Program {
|
|
||||||
val lines = File(filename).readLines().withIndex().iterator()
|
|
||||||
val memory = mutableMapOf<Int, List<RuntimeValue>>()
|
|
||||||
val heap = HeapValues()
|
|
||||||
val program = mutableListOf<Instruction>()
|
|
||||||
val variables = mutableMapOf<String, RuntimeValue>()
|
|
||||||
val memoryPointers = mutableMapOf<String, Pair<Int, DataType>>()
|
|
||||||
val labels = mutableMapOf<String, Int>()
|
|
||||||
|
|
||||||
while(lines.hasNext()) {
|
|
||||||
val (lineNr, line) = lines.next()
|
|
||||||
if(line.startsWith(';') || line.isEmpty())
|
|
||||||
continue
|
|
||||||
else if(line=="%memory")
|
|
||||||
loadMemory(lines, memory)
|
|
||||||
else if(line=="%heap")
|
|
||||||
loadHeap(lines, heap)
|
|
||||||
else if(line.startsWith("%block "))
|
|
||||||
loadBlock(lines, heap, program, variables, memoryPointers, labels)
|
|
||||||
else throw VmExecutionException("syntax error at line ${lineNr + 1}")
|
|
||||||
}
|
|
||||||
return Program(filename, program, variables, memoryPointers, labels, memory, heap)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadBlock(lines: Iterator<IndexedValue<String>>,
|
|
||||||
heap: HeapValues,
|
|
||||||
program: MutableList<Instruction>,
|
|
||||||
variables: MutableMap<String, RuntimeValue>,
|
|
||||||
memoryPointers: MutableMap<String, Pair<Int, DataType>>,
|
|
||||||
labels: MutableMap<String, Int>)
|
|
||||||
{
|
|
||||||
while(true) {
|
|
||||||
val (_, line) = lines.next()
|
|
||||||
if(line.isEmpty())
|
|
||||||
continue
|
|
||||||
else if(line=="%end_block")
|
|
||||||
return
|
|
||||||
else if(line=="%variables")
|
|
||||||
loadVars(lines, variables)
|
|
||||||
else if(line=="%memorypointers")
|
|
||||||
loadMemoryPointers(lines, memoryPointers, heap)
|
|
||||||
else if(line=="%instructions") {
|
|
||||||
val (blockInstructions, blockLabels) = loadInstructions(lines, heap)
|
|
||||||
val baseIndex = program.size
|
|
||||||
program.addAll(blockInstructions)
|
|
||||||
val labelsWithIndex = blockLabels.mapValues { baseIndex+blockInstructions.indexOf(it.value) }
|
|
||||||
labels.putAll(labelsWithIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadHeap(lines: Iterator<IndexedValue<String>>, heap: HeapValues) {
|
|
||||||
val splitpattern = Pattern.compile("\\s+")
|
|
||||||
val heapvalues = mutableListOf<Triple<Int, DataType, String>>()
|
|
||||||
while(true) {
|
|
||||||
val (_, line) = lines.next()
|
|
||||||
if (line == "%end_heap")
|
|
||||||
break
|
|
||||||
val parts = line.split(splitpattern, limit=3)
|
|
||||||
val value = Triple(parts[0].toInt(), DataType.valueOf(parts[1].toUpperCase()), parts[2])
|
|
||||||
heapvalues.add(value)
|
|
||||||
}
|
|
||||||
heapvalues.sortedBy { it.first }.forEach {
|
|
||||||
when(it.second) {
|
|
||||||
DataType.STR, DataType.STR_S -> heap.addString(it.second, unescape(it.third.substring(1, it.third.length - 1), Position("<stackvmsource>", 0, 0, 0)))
|
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B,
|
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
|
||||||
val numbers = it.third.substring(1, it.third.length-1).split(',')
|
|
||||||
val intarray = numbers.map{number->
|
|
||||||
val num=number.trim()
|
|
||||||
if(num.startsWith("&")) {
|
|
||||||
// it's AddressOf
|
|
||||||
val scopedname = num.substring(1)
|
|
||||||
val iref = IdentifierReference(scopedname.split('.'), Position("<intermediate>", 0, 0, 0))
|
|
||||||
val addrOf = AddressOf(iref, Position("<intermediate>", 0, 0, 0))
|
|
||||||
addrOf.scopedname=scopedname
|
|
||||||
IntegerOrAddressOf(null, addrOf)
|
|
||||||
} else {
|
|
||||||
IntegerOrAddressOf(num.toInt(), null)
|
|
||||||
}
|
|
||||||
}.toTypedArray()
|
|
||||||
heap.addIntegerArray(it.second, intarray)
|
|
||||||
}
|
|
||||||
DataType.ARRAY_F -> {
|
|
||||||
val numbers = it.third.substring(1, it.third.length-1).split(',')
|
|
||||||
val doublearray = numbers.map{number->number.trim().toDouble()}.toDoubleArray()
|
|
||||||
heap.addDoublesArray(doublearray)
|
|
||||||
}
|
|
||||||
in NumericDatatypes -> throw VmExecutionException("invalid heap value type ${it.second}")
|
|
||||||
else -> throw VmExecutionException("weird datatype")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadInstructions(lines: Iterator<IndexedValue<String>>, heap: HeapValues): Pair<MutableList<Instruction>, Map<String, Instruction>> {
|
|
||||||
val instructions = mutableListOf<Instruction>()
|
|
||||||
val labels = mutableMapOf<String, Instruction>()
|
|
||||||
val splitpattern = Pattern.compile("\\s+")
|
|
||||||
val nextInstructionLabels = Stack<String>() // more than one label can occur on the isSameAs line
|
|
||||||
|
|
||||||
while(true) {
|
|
||||||
val (lineNr, line) = lines.next()
|
|
||||||
if(line.isEmpty())
|
|
||||||
continue
|
|
||||||
if(line=="%end_instructions")
|
|
||||||
return Pair(instructions, labels)
|
|
||||||
if(!line.startsWith(' ') && line.endsWith(':')) {
|
|
||||||
nextInstructionLabels.push(line.substring(0, line.length-1))
|
|
||||||
} else if(line.startsWith(' ')) {
|
|
||||||
val parts = line.trimStart().split(splitpattern, limit = 2)
|
|
||||||
val opcodeStr = parts[0].toUpperCase()
|
|
||||||
val opcode= Opcode.valueOf(if(opcodeStr.startsWith('_')) opcodeStr.substring(1) else opcodeStr)
|
|
||||||
val args = if(parts.size==2) parts[1] else null
|
|
||||||
val instruction = when(opcode) {
|
|
||||||
Opcode.LINE -> Instruction(opcode, null, callLabel = args)
|
|
||||||
Opcode.JUMP, Opcode.CALL, Opcode.BNEG, Opcode.BPOS,
|
|
||||||
Opcode.BZ, Opcode.BNZ, Opcode.BCS, Opcode.BCC,
|
|
||||||
Opcode.JZ, Opcode.JNZ, Opcode.JZW, Opcode.JNZW -> {
|
|
||||||
if(args!!.startsWith('$')) {
|
|
||||||
Instruction(opcode, RuntimeValue(DataType.UWORD, args.substring(1).toInt(16)))
|
|
||||||
} else {
|
|
||||||
Instruction(opcode, callLabel = args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
in opcodesWithVarArgument -> {
|
|
||||||
val withoutQuotes =
|
|
||||||
if(args!!.startsWith('"') && args.endsWith('"'))
|
|
||||||
args.substring(1, args.length-1) else args
|
|
||||||
|
|
||||||
Instruction(opcode, callLabel = withoutQuotes)
|
|
||||||
}
|
|
||||||
Opcode.SYSCALL -> {
|
|
||||||
if(args!! in syscallNames) {
|
|
||||||
val call = Syscall.valueOf(args)
|
|
||||||
Instruction(opcode, RuntimeValue(DataType.UBYTE, call.callNr))
|
|
||||||
} else {
|
|
||||||
val args2 = args.replace('.', '_')
|
|
||||||
if(args2 in syscallNames) {
|
|
||||||
val call = Syscall.valueOf(args2)
|
|
||||||
Instruction(opcode, RuntimeValue(DataType.UBYTE, call.callNr))
|
|
||||||
} else {
|
|
||||||
// the syscall is not yet implemented. emit a stub.
|
|
||||||
Instruction(Opcode.SYSCALL, RuntimeValue(DataType.UBYTE, Syscall.SYSCALLSTUB.callNr), callLabel = args2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Opcode.INCLUDE_FILE -> {
|
|
||||||
val argparts = args!!.split(' ')
|
|
||||||
val filename = argparts[0]
|
|
||||||
val offset = if(argparts.size>=2 && argparts[1]!="null") getArgValue(argparts[1], heap) else null
|
|
||||||
val length = if(argparts.size>=3 && argparts[2]!="null") getArgValue(argparts[2], heap) else null
|
|
||||||
Instruction(opcode, offset, length, filename)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
Instruction(opcode, getArgValue(args, heap))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
instructions.add(instruction)
|
|
||||||
while(nextInstructionLabels.isNotEmpty()) {
|
|
||||||
val label = nextInstructionLabels.pop()
|
|
||||||
labels[label] = instruction
|
|
||||||
}
|
|
||||||
} else throw VmExecutionException("syntax error at line ${lineNr + 1}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getArgValue(args: String?, heap: HeapValues): RuntimeValue? {
|
|
||||||
if(args==null)
|
|
||||||
return null
|
|
||||||
if(args[0]=='"' && args[args.length-1]=='"') {
|
|
||||||
throw VmExecutionException("encountered a string arg value, but all strings should already have been moved into the heap")
|
|
||||||
}
|
|
||||||
val (type, valueStr) = args.split(':')
|
|
||||||
return when(type) {
|
|
||||||
"b" -> RuntimeValue(DataType.BYTE, valueStr.toShort(16))
|
|
||||||
"ub" -> RuntimeValue(DataType.UBYTE, valueStr.toShort(16))
|
|
||||||
"w" -> RuntimeValue(DataType.WORD, valueStr.toInt(16))
|
|
||||||
"uw" -> RuntimeValue(DataType.UWORD, valueStr.toInt(16))
|
|
||||||
"f" -> RuntimeValue(DataType.FLOAT, valueStr.toDouble())
|
|
||||||
"heap" -> {
|
|
||||||
val heapId = valueStr.toInt()
|
|
||||||
RuntimeValue(heap.get(heapId).type, heapId = heapId)
|
|
||||||
}
|
|
||||||
else -> throw VmExecutionException("invalid datatype $type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadVars(lines: Iterator<IndexedValue<String>>,
|
|
||||||
vars: MutableMap<String, RuntimeValue>) {
|
|
||||||
val splitpattern = Pattern.compile("\\s+")
|
|
||||||
while(true) {
|
|
||||||
val (_, line) = lines.next()
|
|
||||||
if(line=="%end_variables")
|
|
||||||
return
|
|
||||||
val (name, typeStr, valueStr) = line.split(splitpattern, limit = 3)
|
|
||||||
if(valueStr[0] !='"' && ':' !in valueStr)
|
|
||||||
throw VmExecutionException("missing value type character")
|
|
||||||
val value = when(val type = DataType.valueOf(typeStr.toUpperCase())) {
|
|
||||||
DataType.UBYTE -> RuntimeValue(DataType.UBYTE, valueStr.substring(3).substringBefore(' ').toShort(16))// TODO process ZP and struct info?
|
|
||||||
DataType.BYTE -> RuntimeValue(DataType.BYTE, valueStr.substring(2).substringBefore(' ').toShort(16))// TODO process ZP and struct info?
|
|
||||||
DataType.UWORD -> RuntimeValue(DataType.UWORD, valueStr.substring(3).substringBefore(' ').toInt(16))// TODO process ZP and struct info?
|
|
||||||
DataType.WORD -> RuntimeValue(DataType.WORD, valueStr.substring(2).substringBefore(' ').toInt(16))// TODO process ZP and struct info?
|
|
||||||
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, valueStr.substring(2).substringBefore(' ').toDouble())// TODO process ZP and struct info?
|
|
||||||
in StringDatatypes -> {
|
|
||||||
if(valueStr.startsWith('"') && valueStr.endsWith('"'))
|
|
||||||
throw VmExecutionException("encountered a var with a string value, but all string values should already have been moved into the heap")
|
|
||||||
else if(!valueStr.startsWith("heap:"))
|
|
||||||
throw VmExecutionException("invalid string value, should be a heap reference")
|
|
||||||
else {
|
|
||||||
val heapId = valueStr.substring(5).substringBefore(' ').toInt() // TODO process ZP and struct info?
|
|
||||||
RuntimeValue(type, heapId = heapId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
in ArrayDatatypes -> {
|
|
||||||
if(!valueStr.startsWith("heap:"))
|
|
||||||
throw VmExecutionException("invalid array value, should be a heap reference")
|
|
||||||
else {
|
|
||||||
val heapId = valueStr.substring(5).substringBefore(' ').toInt() // TODO process ZP and struct info?
|
|
||||||
RuntimeValue(type, heapId = heapId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw VmExecutionException("weird datatype")
|
|
||||||
}
|
|
||||||
vars[name] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadMemoryPointers(lines: Iterator<IndexedValue<String>>,
|
|
||||||
pointers: MutableMap<String, Pair<Int, DataType>>,
|
|
||||||
heap: HeapValues) {
|
|
||||||
val splitpattern = Pattern.compile("\\s+")
|
|
||||||
while(true) {
|
|
||||||
val (_, line) = lines.next()
|
|
||||||
if(line=="%end_memorypointers")
|
|
||||||
return
|
|
||||||
val (name, typeStr, valueStr) = line.split(splitpattern, limit = 3)
|
|
||||||
if(valueStr[0] !='"' && ':' !in valueStr)
|
|
||||||
throw VmExecutionException("missing value type character")
|
|
||||||
val type = DataType.valueOf(typeStr.toUpperCase())
|
|
||||||
val value = getArgValue(valueStr, heap)!!.integerValue()
|
|
||||||
pointers[name] = Pair(value, type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadMemory(lines: Iterator<IndexedValue<String>>, memory: MutableMap<Int, List<RuntimeValue>>): Map<Int, List<RuntimeValue>> {
|
|
||||||
while(true) {
|
|
||||||
val (lineNr, line) = lines.next()
|
|
||||||
if(line=="%end_memory")
|
|
||||||
return memory
|
|
||||||
val address = line.substringBefore(' ').toInt(16)
|
|
||||||
val rest = line.substringAfter(' ').trim()
|
|
||||||
if(rest.startsWith('"')) {
|
|
||||||
TODO("memory init with char/string")
|
|
||||||
} else {
|
|
||||||
val valueStrings = rest.split(' ')
|
|
||||||
val values = mutableListOf<RuntimeValue>()
|
|
||||||
valueStrings.forEach {
|
|
||||||
when(it.length) {
|
|
||||||
2 -> values.add(RuntimeValue(DataType.UBYTE, it.toShort(16)))
|
|
||||||
4 -> values.add(RuntimeValue(DataType.UWORD, it.toInt(16)))
|
|
||||||
else -> throw VmExecutionException("invalid value at line $lineNr+1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
memory[address] = values
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,6 @@
|
|||||||
package compiler.target.c64.codegen
|
package oldcodegen
|
||||||
|
|
||||||
|
/** OLD STACK-VM CODE GEN -- NO LONGER USED **/
|
||||||
|
|
||||||
// note: to put stuff on the stack, we use Absolute,X addressing mode which is 3 bytes / 4 cycles
|
// note: to put stuff on the stack, we use Absolute,X addressing mode which is 3 bytes / 4 cycles
|
||||||
// possible space optimization is to use zeropage (indirect),Y which is 2 bytes, but 5 cycles
|
// possible space optimization is to use zeropage (indirect),Y which is 2 bytes, but 5 cycles
|
@ -1,4 +1,7 @@
|
|||||||
package compiler.target.c64.codegen
|
package oldcodegen
|
||||||
|
|
||||||
|
/** OLD STACK-VM CODE GEN -- NO LONGER USED **/
|
||||||
|
|
||||||
|
|
||||||
import prog8.ast.base.printWarning
|
import prog8.ast.base.printWarning
|
||||||
import prog8.compiler.intermediate.Instruction
|
import prog8.compiler.intermediate.Instruction
|
@ -1,4 +1,7 @@
|
|||||||
package compiler.target.c64.codegen
|
package oldcodegen
|
||||||
|
|
||||||
|
/** OLD STACK-VM CODE GEN -- NO LONGER USED **/
|
||||||
|
|
||||||
|
|
||||||
import prog8.compiler.CompilerException
|
import prog8.compiler.CompilerException
|
||||||
import prog8.compiler.intermediate.Instruction
|
import prog8.compiler.intermediate.Instruction
|
Loading…
Reference in New Issue
Block a user