diff --git a/codeGenExperimental/src/prog8/codegen/experimental/CodeGen.kt b/codeGenExperimental/src/prog8/codegen/experimental/CodeGen.kt index 50ed6c1e8..d394fecd4 100644 --- a/codeGenExperimental/src/prog8/codegen/experimental/CodeGen.kt +++ b/codeGenExperimental/src/prog8/codegen/experimental/CodeGen.kt @@ -153,8 +153,11 @@ class CodeGen(internal val program: PtProgram, parent.children.remove(sub) if (sub === entrypoint) { // entrypoint sub must be first sub - val firstsub = parent.children.withIndex().first { it.value is PtSub || it.value is PtAsmSub } - parent.add(firstsub.index, renamedSub) + val firstsub = parent.children.withIndex().firstOrNull() { it.value is PtSub || it.value is PtAsmSub } + if(firstsub!=null) + parent.add(firstsub.index, renamedSub) + else + parent.add(renamedSub) } else { parent.add(renamedSub) } diff --git a/codeGenExperimental/src/prog8/codegen/experimental/VmAssemblyProgram.kt b/codeGenExperimental/src/prog8/codegen/experimental/VmAssemblyProgram.kt index 37247f9a6..8fa952b8b 100644 --- a/codeGenExperimental/src/prog8/codegen/experimental/VmAssemblyProgram.kt +++ b/codeGenExperimental/src/prog8/codegen/experimental/VmAssemblyProgram.kt @@ -23,8 +23,7 @@ class VmAssemblyProgram(override val name: String, val irProgram: IRProgram): IA outfile.bufferedWriter().use { out -> allocations.asVmMemory().forEach { (name, alloc) -> - out.write("; ${name.joinToString(".")}\n") - out.write(alloc + "\n") + out.write("var ${name.joinToString(".")} $alloc\n") } out.write("------PROGRAM------\n") diff --git a/codeGenExperimental/src/prog8/codegen/experimental/VmVariableAllocator.kt b/codeGenExperimental/src/prog8/codegen/experimental/VmVariableAllocator.kt index 4d5aa1464..0f3d3d6ae 100644 --- a/codeGenExperimental/src/prog8/codegen/experimental/VmVariableAllocator.kt +++ b/codeGenExperimental/src/prog8/codegen/experimental/VmVariableAllocator.kt @@ -67,7 +67,7 @@ class VmVariableAllocator(val st: SymbolTable, val encoding: IStringEncoding, me } else -> throw InternalCompilerException("weird dt") } - mm.add(Pair(variable.scopedName, "$location $typeStr $value")) + mm.add(Pair(variable.scopedName, "@$location $typeStr $value")) } return mm } diff --git a/codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt b/codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt index 73615fdba..b61a29436 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt @@ -24,8 +24,7 @@ class AssemblyProgram(override val name: String, private val allocations: Variab println("write code to $outfile") outfile.bufferedWriter().use { out -> allocations.asVmMemory().forEach { (name, alloc) -> - out.write("; ${name.joinToString(".")}\n") - out.write(alloc + "\n") + out.write("var ${name.joinToString(".")} $alloc\n") } out.write("------PROGRAM------\n") diff --git a/codeGenVirtual/src/prog8/codegen/virtual/VariableAllocator.kt b/codeGenVirtual/src/prog8/codegen/virtual/VariableAllocator.kt index 1cda40d44..93fbff928 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/VariableAllocator.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/VariableAllocator.kt @@ -81,7 +81,7 @@ class VariableAllocator(private val st: SymbolTable, private val program: PtProg } else -> throw InternalCompilerException("weird dt") } - mm.add(Pair(variable.scopedName, "$location $typeStr $value")) + mm.add(Pair(variable.scopedName, "@$location $typeStr $value")) } for (variable in st.allMemMappedVariables) { val location = allocations.getValue(variable.scopedName) @@ -100,7 +100,7 @@ class VariableAllocator(private val st: SymbolTable, private val program: PtProg in ArrayDatatypes -> (1..variable.length!!).joinToString(",") { "0" } else -> throw InternalCompilerException("weird dt for mem mapped var") } - mm.add(Pair(variable.scopedName, "$location $typeStr $value")) + mm.add(Pair(variable.scopedName, "@$location $typeStr $value")) } return mm } diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 0c6a9a310..f3e5c38c0 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -399,8 +399,8 @@ private fun createAssemblyAndAssemble(program: Program, // to help clean up the code that still depends on them. // removeAllVardeclsFromAst(program) - println("*********** AST RIGHT BEFORE ASM GENERATION *************") - printProgram(program) +// println("*********** AST RIGHT BEFORE ASM GENERATION *************") +// printProgram(program) val assembly = asmGeneratorFor(program, errors, symbolTable, compilerOptions).compileToAssembly() errors.report() diff --git a/docs/source/todo.rst b/docs/source/todo.rst index a4c5bb90f..c50325a69 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,10 +3,11 @@ TODO For next release ^^^^^^^^^^^^^^^^ -- VM Assembler: add support for translating symbols to address search for "TODO do we have to replace variable names by their allocated address" load.w r0,{_}txt.clear_screen.sequence +- IR/VM: add address calculation for simple addition: conv.string_out+42 - IR/VM: add proper memory mapped variables support - replace the symbol by the memory address in the IR code. - IR/VM: check that the above works ok now with the cx16 virtual registers. - IR/VM: add proper memory slabs support +- IR/VM: improve unit tests ... diff --git a/intermediate/src/prog8/intermediate/Instructions.kt b/intermediate/src/prog8/intermediate/Instructions.kt index a61c56f85..9d8e1360f 100644 --- a/intermediate/src/prog8/intermediate/Instructions.kt +++ b/intermediate/src/prog8/intermediate/Instructions.kt @@ -383,6 +383,12 @@ val OpcodesWithAddress = setOf( Opcode.ROXRM ) +val OpcodesForCpuRegisters = setOf( + Opcode.LOADCPU, + Opcode.STORECPU, + Opcode.STOREZCPU +) + enum class VmDataType { BYTE, diff --git a/virtualmachine/src/prog8/vm/Assembler.kt b/virtualmachine/src/prog8/vm/Assembler.kt index 2922dcf12..9f40439b0 100644 --- a/virtualmachine/src/prog8/vm/Assembler.kt +++ b/virtualmachine/src/prog8/vm/Assembler.kt @@ -15,7 +15,8 @@ class Assembler { } fun initializeMemory(memsrc: String, memory: Memory) { - val instrPattern = Regex("""(.+?)\s+([a-z]+)\s+(.+)""", RegexOption.IGNORE_CASE) + labels.clear() + val instrPattern = Regex("""var (.+) @([0-9]+) ([a-z]+) (.+)""", RegexOption.IGNORE_CASE) for(line in memsrc.lines()) { if(line.isBlank() || line.startsWith(';')) continue @@ -23,9 +24,10 @@ class Assembler { if(match==null) throw IllegalArgumentException("invalid line $line") else { - val (_, addr, what, values) = match.groupValues - var address = parseValue(addr, 0).toInt() - when(what) { + val (name, addrStr, datatype, values) = match.destructured + var address = parseValue(Opcode.LOADCPU, addrStr, 0).toInt() + labels[name] = address + when(datatype) { "str" -> { val string = values.trim('"').unescape() memory.setString(address, string, false) @@ -35,14 +37,14 @@ class Assembler { memory.setString(address, string, true) } "ubyte", "byte" -> { - val array = values.split(',').map { parseValue(it.trim(), 0).toInt() } + val array = values.split(',').map { parseValue(Opcode.LOADCPU, it.trim(), 0).toInt() } for (value in array) { memory.setUB(address, value.toUByte()) address++ } } "uword", "word" -> { - val array = values.split(',').map { parseValue(it.trim(), 0).toInt() } + val array = values.split(',').map { parseValue(Opcode.LOADCPU, it.trim(), 0).toInt() } for (value in array) { memory.setUW(address, value.toUShort()) address += 2 @@ -55,14 +57,13 @@ class Assembler { address += 4 // 32-bits floats } } - else -> throw IllegalArgumentException("mem instr $what") + else -> throw IllegalArgumentException("invalid datatype $datatype") } } } } fun assembleProgram(source: CharSequence): List { - labels.clear() placeholders.clear() val program = mutableListOf() val instructionPattern = Regex("""([a-z]+)(\.b|\.w|\.f)?(.*)""", RegexOption.IGNORE_CASE) @@ -134,9 +135,9 @@ class Assembler { value = if(operand.startsWith('_')) { // it's a label, keep the original case! val labelname = rest.split(",").first().trim() - parseValue(labelname, program.size) + parseValue(opcode, labelname, program.size) } else { - parseValue(operand, program.size) + parseValue(opcode, operand, program.size) } operands.clear() } @@ -147,7 +148,7 @@ class Assembler { else if(operand[0]=='f' && operand[1]=='r') fpReg2 = operand.substring(2).toInt() else { - value = parseValue(operand, program.size) + value = parseValue(opcode, operand, program.size) operands.clear() } if(operands.isNotEmpty()) { @@ -157,12 +158,13 @@ class Assembler { else if(operand[0]=='f' && operand[1]=='r') fpReg3 = operand.substring(2).toInt() else { - value = parseValue(operand, program.size) + val symbol=rest.split(',').last() + value = parseValue(opcode, symbol, program.size) operands.clear() } if(operands.isNotEmpty()) { - operand = operands.removeFirst().trim() - value = parseValue(operand, program.size) + val symbol=rest.split(',').last() + value = parseValue(opcode, symbol, program.size) operands.clear() } } @@ -219,7 +221,21 @@ class Assembler { if(format.fpValue) floatValue = value!! - program.add(Instruction(opcode, type, reg1, reg2, fpReg1, fpReg2, intValue, floatValue)) + if(opcode in OpcodesForCpuRegisters) { + val reg=rest.split(',').last().lowercase() + if(reg !in setOf( + "_a", "_x", "_y", + "_ax", "_ay", "_xy", + "_r0", "_r1", "_r2", "_r3", + "_r4", "_r5", "_r6", "_r7", + "_r8", "_r9", "_r10","_r11", + "_r12", "_r13", "_r14", "_r15", + "_pc", "_pz", "_pv","_pn")) + throw IllegalArgumentException("invalid cpu reg: $reg") + program.add(Instruction(opcode, type, reg1, labelSymbol = listOf(reg.substring(1)))) + } else { + program.add(Instruction(opcode, type, reg1, reg2, fpReg1, fpReg2, intValue, floatValue)) + } } } @@ -229,14 +245,18 @@ class Assembler { private fun pass2replaceLabels(program: MutableList) { for((line, label) in placeholders) { - val replacement = labels.getValue(label) - program[line] = program[line].copy(value = replacement) + val replacement = labels[label] + if(replacement==null) { + println("TODO: find address of symbol $label") // TODO + } else { + program[line] = program[line].copy(value = replacement) + } } } - private fun parseValue(value: String, pc: Int): Float { + private fun parseValue(opcode: Opcode, value: String, pc: Int): Float { if(value.startsWith("-")) { - return -parseValue(value.substring(1), pc) + return -parseValue(opcode, value.substring(1), pc) } if(value.startsWith('$')) return value.substring(1).toInt(16).toFloat() @@ -245,7 +265,13 @@ class Assembler { if(value.startsWith("0x")) return value.substring(2).toInt(16).toFloat() if(value.startsWith('_')) { - placeholders[pc] = value.substring(1) + if(opcode !in OpcodesForCpuRegisters) + placeholders[pc] = value.substring(1) + return 0f + } + if(value[0].isLetter()) { + if(opcode !in OpcodesForCpuRegisters) + placeholders[pc] = value return 0f } return value.toFloat() diff --git a/virtualmachine/src/prog8/vm/VirtualMachine.kt b/virtualmachine/src/prog8/vm/VirtualMachine.kt index db6925ad8..78b015d41 100644 --- a/virtualmachine/src/prog8/vm/VirtualMachine.kt +++ b/virtualmachine/src/prog8/vm/VirtualMachine.kt @@ -218,6 +218,9 @@ class VirtualMachine(val memory: Memory, program: List) { Opcode.CLC -> { statusCarry = false; pc++ } Opcode.SEC -> { statusCarry = true; pc++ } Opcode.BINARYDATA -> TODO("BINARYDATA not yet supported in VM") + Opcode.LOADCPU -> InsLOADCPU(ins) + Opcode.STORECPU -> InsSTORECPU(ins) + Opcode.STOREZCPU -> InsSTOREZCPU(ins) Opcode.FFROMUB -> InsFFROMUB(ins) Opcode.FFROMSB -> InsFFROMSB(ins) @@ -301,6 +304,30 @@ class VirtualMachine(val memory: Memory, program: List) { throw BreakpointException(pc) } + private fun InsLOADCPU(i: Instruction) { + println("VM:TODO: load reg ${i.reg1} from cpu register ${i.labelSymbol}") // TODO + pc++ + } + + private fun InsSTORECPU(i: Instruction) { + val value: UShort = when(i.type!!) { + VmDataType.BYTE -> registers.getUB(i.reg1!!).toUShort() + VmDataType.WORD -> registers.getUW(i.reg1!!) + VmDataType.FLOAT -> throw IllegalArgumentException("there are no float cpu registers") + } + StoreCPU(value, i.type!!, i.labelSymbol!!.single()) + pc++ + } + + private fun InsSTOREZCPU(i: Instruction) { + StoreCPU(0u, i.type!!, i.labelSymbol!!.single()) + pc++ + } + + private fun StoreCPU(value: UShort, dt: VmDataType, regStr: String) { + println("VM:TODO: store a value into cpu register $regStr") // TODO + } + private fun InsLOAD(i: Instruction) { if(i.type==VmDataType.FLOAT) registers.setFloat(i.fpReg1!!, i.fpValue!!)