2022-09-26 17:03:54 +00:00
|
|
|
package prog8.vm
|
|
|
|
|
2022-09-27 00:42:01 +00:00
|
|
|
import prog8.code.core.DataType
|
2022-09-26 17:03:54 +00:00
|
|
|
import prog8.intermediate.*
|
|
|
|
|
2022-10-06 22:34:56 +00:00
|
|
|
/*
|
2022-09-26 17:03:54 +00:00
|
|
|
class VmProgramLoader {
|
|
|
|
|
2022-09-26 23:50:00 +00:00
|
|
|
private val placeholders = mutableMapOf<Int, String>() // program index to symbolname
|
|
|
|
|
|
|
|
fun load(irProgram: IRProgram, memory: Memory): List<IRInstruction> {
|
2022-09-26 17:03:54 +00:00
|
|
|
|
|
|
|
// at long last, allocate the variables in memory.
|
2022-09-26 23:50:00 +00:00
|
|
|
placeholders.clear()
|
2022-09-26 17:03:54 +00:00
|
|
|
val allocations = VmVariableAllocator(irProgram.st, irProgram.encoding, irProgram.options.compTarget)
|
2022-09-26 23:50:00 +00:00
|
|
|
val symbolAddresses = allocations.allocations.toMutableMap()
|
2022-10-06 22:34:56 +00:00
|
|
|
val programChunks = mutableListOf<IRCodeChunkBase>()
|
2022-09-26 17:03:54 +00:00
|
|
|
|
2022-09-27 00:42:01 +00:00
|
|
|
varsToMemory(irProgram, allocations, symbolAddresses, memory)
|
|
|
|
|
2022-09-26 23:50:00 +00:00
|
|
|
if(!irProgram.options.dontReinitGlobals)
|
2022-10-06 22:34:56 +00:00
|
|
|
addToProgram(irProgram.globalInits, programChunks, symbolAddresses)
|
2022-09-26 17:03:54 +00:00
|
|
|
|
|
|
|
// make sure that if there is a "main.start" entrypoint, we jump to it
|
|
|
|
irProgram.blocks.firstOrNull()?.let {
|
|
|
|
if(it.subroutines.any { sub -> sub.name=="main.start" }) {
|
2022-09-27 15:45:26 +00:00
|
|
|
placeholders[program.size] = "main.start"
|
2022-09-26 23:50:00 +00:00
|
|
|
program += IRInstruction(Opcode.JUMP, labelSymbol = "main.start")
|
2022-09-26 17:03:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
irProgram.blocks.forEach { block ->
|
|
|
|
if(block.address!=null)
|
2022-09-27 01:32:39 +00:00
|
|
|
throw IRParseException("blocks cannot have a load address for vm: ${block.name}")
|
2022-09-26 17:03:54 +00:00
|
|
|
|
2022-09-27 15:45:26 +00:00
|
|
|
block.inlineAssembly.forEach { addAssemblyToProgram(it, program, symbolAddresses) }
|
2022-09-26 17:03:54 +00:00
|
|
|
block.subroutines.forEach {
|
2022-09-26 23:50:00 +00:00
|
|
|
symbolAddresses[it.name] = program.size
|
2022-09-26 17:03:54 +00:00
|
|
|
it.chunks.forEach { chunk ->
|
2022-10-03 22:47:51 +00:00
|
|
|
when (chunk) {
|
|
|
|
is IRInlineAsmChunk -> addAssemblyToProgram(chunk, program, symbolAddresses)
|
|
|
|
is IRInlineBinaryChunk -> program += IRInstruction(Opcode.BINARYDATA, binaryData = chunk.data)
|
2022-10-04 20:54:14 +00:00
|
|
|
else -> addToProgram(chunk.instructions, program, symbolAddresses)
|
2022-10-03 22:47:51 +00:00
|
|
|
}
|
2022-09-26 23:50:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if(block.asmSubroutines.any())
|
2022-09-27 01:32:39 +00:00
|
|
|
throw IRParseException("vm currently does not support asmsubs: ${block.asmSubroutines.first().name}")
|
2022-09-26 23:50:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pass2replaceLabelsByProgIndex(program, symbolAddresses)
|
|
|
|
|
|
|
|
program.forEach {
|
|
|
|
if(it.opcode in OpcodesWithAddress && it.value==null) {
|
2022-09-27 01:32:39 +00:00
|
|
|
throw IRParseException("instruction missing numeric value, label not replaced? $it")
|
2022-09-26 23:50:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return program
|
|
|
|
}
|
|
|
|
|
2022-09-27 00:42:01 +00:00
|
|
|
private fun varsToMemory(
|
|
|
|
program: IRProgram,
|
|
|
|
allocations: VmVariableAllocator,
|
|
|
|
symbolAddresses: MutableMap<String, Int>,
|
|
|
|
memory: Memory
|
|
|
|
) {
|
|
|
|
program.st.allVariables().forEach { variable ->
|
|
|
|
var addr = allocations.allocations.getValue(variable.name)
|
|
|
|
variable.onetimeInitializationNumericValue?.let {
|
|
|
|
when(variable.dt) {
|
|
|
|
DataType.UBYTE -> memory.setUB(addr, it.toInt().toUByte())
|
|
|
|
DataType.BYTE -> memory.setSB(addr, it.toInt().toByte())
|
|
|
|
DataType.UWORD -> memory.setUW(addr, it.toInt().toUShort())
|
|
|
|
DataType.WORD -> memory.setSW(addr, it.toInt().toShort())
|
|
|
|
DataType.FLOAT -> memory.setFloat(addr, it.toFloat())
|
2022-09-27 01:32:39 +00:00
|
|
|
else -> throw IRParseException("invalid dt")
|
2022-09-27 00:42:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
variable.onetimeInitializationArrayValue?.let {
|
2022-09-28 13:58:58 +00:00
|
|
|
require(variable.length==it.size || it.size==1 || it.size==0)
|
|
|
|
if(it.isEmpty() || it.size==1) {
|
|
|
|
val value = if(it.isEmpty()) {
|
|
|
|
require(variable.bss)
|
|
|
|
0.0
|
|
|
|
} else {
|
|
|
|
require(!variable.bss)
|
|
|
|
it[0].number!!
|
|
|
|
}
|
2022-09-27 00:42:01 +00:00
|
|
|
when(variable.dt) {
|
|
|
|
DataType.STR, DataType.ARRAY_UB -> {
|
|
|
|
repeat(variable.length!!) {
|
|
|
|
memory.setUB(addr, value.toInt().toUByte())
|
|
|
|
addr++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DataType.ARRAY_B -> {
|
|
|
|
repeat(variable.length!!) {
|
|
|
|
memory.setSB(addr, value.toInt().toByte())
|
|
|
|
addr++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DataType.ARRAY_UW -> {
|
|
|
|
repeat(variable.length!!) {
|
|
|
|
memory.setUW(addr, value.toInt().toUShort())
|
|
|
|
addr+=2
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DataType.ARRAY_W -> {
|
|
|
|
repeat(variable.length!!) {
|
|
|
|
memory.setSW(addr, value.toInt().toShort())
|
|
|
|
addr+=2
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DataType.ARRAY_F -> {
|
|
|
|
repeat(variable.length!!) {
|
|
|
|
memory.setFloat(addr, value.toFloat())
|
|
|
|
addr += program.options.compTarget.machine.FLOAT_MEM_SIZE
|
|
|
|
}
|
|
|
|
}
|
2022-09-27 01:32:39 +00:00
|
|
|
else -> throw IRParseException("invalid dt")
|
2022-09-27 00:42:01 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
when(variable.dt) {
|
|
|
|
DataType.STR, DataType.ARRAY_UB -> {
|
|
|
|
for(elt in it) {
|
|
|
|
memory.setUB(addr, elt.number!!.toInt().toUByte())
|
|
|
|
addr++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DataType.ARRAY_B -> {
|
|
|
|
for(elt in it) {
|
|
|
|
memory.setSB(addr, elt.number!!.toInt().toByte())
|
|
|
|
addr++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DataType.ARRAY_UW -> {
|
|
|
|
for(elt in it) {
|
|
|
|
if(elt.addressOf!=null) {
|
|
|
|
val symbolAddress = symbolAddresses.getValue(elt.addressOf!!.joinToString("."))
|
|
|
|
memory.setUW(addr, symbolAddress.toUShort())
|
|
|
|
} else {
|
|
|
|
memory.setUW(addr, elt.number!!.toInt().toUShort())
|
|
|
|
}
|
|
|
|
addr+=2
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DataType.ARRAY_W -> {
|
|
|
|
for(elt in it) {
|
|
|
|
memory.setSW(addr, elt.number!!.toInt().toShort())
|
|
|
|
addr+=2
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DataType.ARRAY_F -> {
|
|
|
|
for(elt in it) {
|
|
|
|
memory.setSW(addr, elt.number!!.toInt().toShort())
|
|
|
|
addr+=program.options.compTarget.machine.FLOAT_MEM_SIZE
|
|
|
|
}
|
|
|
|
}
|
2022-09-27 01:32:39 +00:00
|
|
|
else -> throw IRParseException("invalid dt")
|
2022-09-27 00:42:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
require(variable.onetimeInitializationStringValue==null) { "in vm/ir, strings should have been converted into bytearrays." }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-26 23:50:00 +00:00
|
|
|
private fun pass2replaceLabelsByProgIndex(
|
|
|
|
program: MutableList<IRInstruction>,
|
|
|
|
symbolAddresses: MutableMap<String, Int>
|
|
|
|
) {
|
|
|
|
for((line, label) in placeholders) {
|
|
|
|
val replacement = symbolAddresses[label]
|
|
|
|
if(replacement==null) {
|
|
|
|
// it could be an address + index: symbol+42
|
|
|
|
if('+' in label) {
|
|
|
|
val (symbol, indexStr) = label.split('+')
|
|
|
|
val index = indexStr.toInt()
|
|
|
|
val address = symbolAddresses.getValue(symbol) + index
|
|
|
|
program[line] = program[line].copy(value = address)
|
|
|
|
} else {
|
2022-09-27 01:32:39 +00:00
|
|
|
throw IRParseException("placeholder not found in labels: $label")
|
2022-09-26 23:50:00 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
program[line] = program[line].copy(value = replacement)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun addToProgram(
|
2022-10-06 22:34:56 +00:00
|
|
|
chunks: Iterable<IRCodeChunkBase>,
|
|
|
|
program: MutableList<IRCodeChunkBase>,
|
2022-09-26 23:50:00 +00:00
|
|
|
symbolAddresses: MutableMap<String, Int>
|
|
|
|
) {
|
2022-10-04 20:54:14 +00:00
|
|
|
instructions.map {
|
2022-09-26 23:50:00 +00:00
|
|
|
when(it) {
|
|
|
|
is IRInstruction -> {
|
2022-09-27 15:45:26 +00:00
|
|
|
it.labelSymbol?.let { symbol -> placeholders[program.size]=symbol }
|
2022-09-27 00:42:01 +00:00
|
|
|
if(it.opcode==Opcode.SYSCALL) {
|
|
|
|
// convert IR Syscall to VM Syscall
|
|
|
|
val vmSyscall = when(it.value!!) {
|
|
|
|
IMSyscall.SORT_UBYTE.ordinal -> Syscall.SORT_UBYTE
|
|
|
|
IMSyscall.SORT_BYTE.ordinal -> Syscall.SORT_BYTE
|
|
|
|
IMSyscall.SORT_UWORD.ordinal -> Syscall.SORT_UWORD
|
|
|
|
IMSyscall.SORT_WORD.ordinal -> Syscall.SORT_WORD
|
|
|
|
IMSyscall.ANY_BYTE.ordinal -> Syscall.ANY_BYTE
|
|
|
|
IMSyscall.ANY_WORD.ordinal -> Syscall.ANY_WORD
|
|
|
|
IMSyscall.ANY_FLOAT.ordinal -> Syscall.ANY_FLOAT
|
|
|
|
IMSyscall.ALL_BYTE.ordinal -> Syscall.ALL_BYTE
|
|
|
|
IMSyscall.ALL_WORD.ordinal -> Syscall.ALL_WORD
|
|
|
|
IMSyscall.ALL_FLOAT.ordinal -> Syscall.ALL_FLOAT
|
|
|
|
IMSyscall.REVERSE_BYTES.ordinal -> Syscall.REVERSE_BYTES
|
|
|
|
IMSyscall.REVERSE_WORDS.ordinal -> Syscall.REVERSE_WORDS
|
|
|
|
IMSyscall.REVERSE_FLOATS.ordinal -> Syscall.REVERSE_FLOATS
|
2022-09-27 01:32:39 +00:00
|
|
|
else -> throw IRParseException("invalid IM syscall number $it")
|
2022-09-27 00:42:01 +00:00
|
|
|
}
|
|
|
|
program += it.copy(value=vmSyscall.ordinal)
|
|
|
|
} else {
|
|
|
|
program += it
|
|
|
|
}
|
2022-09-26 23:50:00 +00:00
|
|
|
}
|
|
|
|
is IRCodeLabel -> { symbolAddresses[it.name] = program.size }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun addAssemblyToProgram(
|
|
|
|
asmChunk: IRInlineAsmChunk,
|
|
|
|
program: MutableList<IRInstruction>,
|
2022-09-27 15:45:26 +00:00
|
|
|
symbolAddresses: MutableMap<String, Int>,
|
2022-09-26 23:50:00 +00:00
|
|
|
) {
|
2022-09-30 12:05:11 +00:00
|
|
|
if(asmChunk.isIR) {
|
|
|
|
asmChunk.assembly.lineSequence().forEach {
|
|
|
|
val parsed = parseIRCodeLine(it.trim(), program.size, placeholders)
|
|
|
|
if (parsed is IRInstruction)
|
|
|
|
program += parsed
|
|
|
|
else if (parsed is IRCodeLabel)
|
|
|
|
symbolAddresses[parsed.name] = program.size
|
|
|
|
}
|
|
|
|
} else {
|
2022-09-30 18:18:14 +00:00
|
|
|
throw IRParseException("vm currently does not support real inlined assembly (only IR): ${asmChunk.position}")
|
2022-09-26 17:03:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-10-06 22:34:56 +00:00
|
|
|
*/
|