From 627ed51a1ba4ee3d941a778a23c9d5ccd89b195e Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 18 Sep 2022 16:04:49 +0200 Subject: [PATCH] IR: mem mapped vars and memory slabs --- .../src/prog8/codegen/experimental/CodeGen.kt | 48 +++++ .../codegen/experimental/ExpressionGen.kt | 1 + .../codegen/experimental/VmAssemblyProgram.kt | 5 - .../experimental/VmVariableAllocator.kt | 39 +++- .../prog8/codegen/virtual/AssemblyProgram.kt | 7 +- compiler/res/prog8lib/virtual/syslib.p8 | 192 +++++++++--------- .../compiler/astprocessing/AstChecker.kt | 2 +- docs/source/todo.rst | 18 +- examples/test.p8 | 166 ++++++++------- .../src/prog8/intermediate/IRFileReader.kt | 12 +- .../src/prog8/intermediate/IRFileWriter.kt | 52 +---- .../src/prog8/intermediate/Instructions.kt | 4 +- intermediate/src/prog8/intermediate/Utils.kt | 55 +++++ virtualmachine/src/prog8/vm/Assembler.kt | 64 ++++-- 14 files changed, 387 insertions(+), 278 deletions(-) create mode 100644 intermediate/src/prog8/intermediate/Utils.kt diff --git a/codeGenExperimental/src/prog8/codegen/experimental/CodeGen.kt b/codeGenExperimental/src/prog8/codegen/experimental/CodeGen.kt index d394fecd4..9b587a2ee 100644 --- a/codeGenExperimental/src/prog8/codegen/experimental/CodeGen.kt +++ b/codeGenExperimental/src/prog8/codegen/experimental/CodeGen.kt @@ -1,5 +1,6 @@ package prog8.codegen.experimental +import prog8.code.StMemVar import prog8.code.StMemorySlab import prog8.code.StStaticVariable import prog8.code.SymbolTable @@ -69,6 +70,8 @@ class CodeGen(internal val program: PtProgram, irProg.addBlock(translate(block)) } + replaceMemoryMappedVars(irProg) + if(options.optimize) { val optimizer = IRPeepholeOptimizer(irProg) optimizer.optimize() @@ -81,6 +84,51 @@ class CodeGen(internal val program: PtProgram, return VmAssemblyProgram(irProgFromDisk.name, irProgFromDisk) } + private fun replaceMemoryMappedVars(irProg: IRProgram) { + // replace memory mapped variable symbols with the memory address directly. + // note: we do still export the memory mapped symbols so a code generator can use those + // for instance when a piece of inlined assembly references them. + val replacements = mutableListOf>() + irProg.blocks.asSequence().flatMap { it.subroutines }.flatMap { it.chunks }.forEach { chunk -> + chunk.lines.withIndex().forEach { + (lineIndex, line)-> if(line is IRCodeInstruction) { + val symbolExpr = line.ins.labelSymbol?.single() + if(symbolExpr!=null) { + val symbol: String + val index: UInt + if('+' in symbolExpr) { + val operands = symbolExpr.split('+', ) + symbol = operands[0] + index = operands[1].toUInt() + } else { + symbol = symbolExpr + index = 0u + } + val target = symbolTable.flat[symbol.split('.')] + if (target is StMemVar) { + replacements.add(Triple(chunk, lineIndex, target.address+index)) + } + } + } + } + } + + replacements.forEach { + val old = it.first.lines[it.second] as IRCodeInstruction + it.first.lines[it.second] = IRCodeInstruction( + old.ins.opcode, + old.ins.type, + old.ins.reg1, + old.ins.reg2, + old.ins.fpReg1, + old.ins.fpReg2, + it.third.toInt(), + null, + null + ) + } + } + private fun flattenNestedSubroutines() { // this moves all nested subroutines up to the block scope. // also changes the name to be the fully scoped one so it becomes unique at the top level. diff --git a/codeGenExperimental/src/prog8/codegen/experimental/ExpressionGen.kt b/codeGenExperimental/src/prog8/codegen/experimental/ExpressionGen.kt index 03327a9e2..b073029dd 100644 --- a/codeGenExperimental/src/prog8/codegen/experimental/ExpressionGen.kt +++ b/codeGenExperimental/src/prog8/codegen/experimental/ExpressionGen.kt @@ -44,6 +44,7 @@ internal class ExpressionGen(private val codeGen: CodeGen) { is PtAddressOf -> { val vmDt = codeGen.vmType(expr.type) val symbol = expr.identifier.targetName.joinToString(".") + // note: LOAD gets you the address of the symbol, whereas LOADM would get you the value stored at that location code += IRCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, labelSymbol = symbol) } is PtMemoryByte -> { diff --git a/codeGenExperimental/src/prog8/codegen/experimental/VmAssemblyProgram.kt b/codeGenExperimental/src/prog8/codegen/experimental/VmAssemblyProgram.kt index 8fa952b8b..aad0851c5 100644 --- a/codeGenExperimental/src/prog8/codegen/experimental/VmAssemblyProgram.kt +++ b/codeGenExperimental/src/prog8/codegen/experimental/VmAssemblyProgram.kt @@ -79,11 +79,6 @@ class VmAssemblyProgram(override val name: String, val irProgram: IRProgram): IA private fun processInlinedAsm(asm: String, allocations: VmVariableAllocator): String { // TODO do we have to replace variable names by their allocated address??? return asm -// // need to replace &X by address of X. TODO: this actually needs to be done by the vm assembler/loader. Then this can be omitted -// return asm.replace("""&[a-zA-Z\d_\.]+""".toRegex()) { matchResult -> -// // replace "&X" with the address of X -// val name = matchResult.value.substring(1, matchResult.value.length).split('.') -// allocations.get(name).toString() } } } diff --git a/codeGenExperimental/src/prog8/codegen/experimental/VmVariableAllocator.kt b/codeGenExperimental/src/prog8/codegen/experimental/VmVariableAllocator.kt index 0f3d3d6ae..c6f2c56bf 100644 --- a/codeGenExperimental/src/prog8/codegen/experimental/VmVariableAllocator.kt +++ b/codeGenExperimental/src/prog8/codegen/experimental/VmVariableAllocator.kt @@ -2,6 +2,7 @@ package prog8.codegen.experimental import prog8.code.SymbolTable import prog8.code.core.* +import prog8.intermediate.getTypeString class VmVariableAllocator(val st: SymbolTable, val encoding: IStringEncoding, memsizer: IMemSizer) { @@ -26,24 +27,21 @@ class VmVariableAllocator(val st: SymbolTable, val encoding: IStringEncoding, me allocations[variable.scopedName] = nextLocation nextLocation += memsize } + for(slab in st.allMemorySlabs) { + // we ignore the alignment for the VM. + allocations[slab.scopedName] = nextLocation + nextLocation += slab.size.toInt() + } freeMemoryStart = nextLocation } - fun get(name: List) = allocations.getValue(name) - fun asVmMemory(): List, String>> { val mm = mutableListOf, String>>() + + // normal variables for (variable in st.allVariables) { val location = allocations.getValue(variable.scopedName) - val typeStr = when(variable.dt) { - DataType.UBYTE, DataType.ARRAY_UB, DataType.STR -> "ubyte" - DataType.BYTE, DataType.ARRAY_B -> "byte" - DataType.UWORD, DataType.ARRAY_UW -> "uword" - DataType.WORD, DataType.ARRAY_W -> "word" - DataType.FLOAT, DataType.ARRAY_F -> "float" - else -> throw InternalCompilerException("weird dt") - } val value = when(variable.dt) { DataType.FLOAT -> (variable.onetimeInitializationNumericValue ?: 0.0).toString() in NumericDatatypes -> (variable.onetimeInitializationNumericValue ?: 0).toHex() @@ -67,8 +65,27 @@ 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 ${getTypeString(variable)} $value")) } + + // memory mapped variables + for (variable in st.allMemMappedVariables) { + val value = when(variable.dt) { + DataType.FLOAT -> "0.0" + in NumericDatatypes -> "0" + DataType.ARRAY_F -> (1..variable.length!!).joinToString(",") { "0.0" } + in ArrayDatatypes -> (1..variable.length!!).joinToString(",") { "0" } + else -> throw InternalCompilerException("weird dt for mem mapped var") + } + mm.add(Pair(variable.scopedName, "@${variable.address} ${getTypeString(variable)} $value")) + } + + // memory slabs. + for(slab in st.allMemorySlabs) { + val address = allocations.getValue(slab.scopedName) + mm.add(Pair(slab.scopedName, "@$address ubyte[${slab.size}] 0")) + } + return mm } diff --git a/codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt b/codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt index b61a29436..b5c983a4b 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/AssemblyProgram.kt @@ -48,12 +48,7 @@ class AssemblyProgram(override val name: String, private val allocations: Variab is VmCodeLabel -> write("_" + line.name.joinToString(".") + ":\n") is VmCodeInlineAsm -> { // TODO do we have to replace variable names by their allocated address??? - val asm = line.assembly -// val asm = line.assembly.replace("""&[a-zA-Z\d_\.]+""".toRegex()) { matchResult -> -// // need to replace &X by address of X. TODO: this actually needs to be done by the vm assembler/loader. Then this can be omitted -// val name = matchResult.value.substring(1, matchResult.value.length).split('.') -// allocations.get(name).toString() } - write(asm+"\n") + write(line.assembly+"\n") } is VmCodeInlineBinary -> { write("incbin \"${line.file}\"") diff --git a/compiler/res/prog8lib/virtual/syslib.p8 b/compiler/res/prog8lib/virtual/syslib.p8 index 6795969c3..f2ab7d541 100644 --- a/compiler/res/prog8lib/virtual/syslib.p8 +++ b/compiler/res/prog8lib/virtual/syslib.p8 @@ -110,105 +110,105 @@ cx16 { ; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage ; they are simulated on the VirtualMachine as well but their location in memory is different - &uword r0 = $0002 - &uword r1 = $0004 - &uword r2 = $0006 - &uword r3 = $0008 - &uword r4 = $000a - &uword r5 = $000c - &uword r6 = $000e - &uword r7 = $0010 - &uword r8 = $0012 - &uword r9 = $0014 - &uword r10 = $0016 - &uword r11 = $0018 - &uword r12 = $001a - &uword r13 = $001c - &uword r14 = $001e - &uword r15 = $0020 + &uword r0 = $ff02 + &uword r1 = $ff04 + &uword r2 = $ff06 + &uword r3 = $ff08 + &uword r4 = $ff0a + &uword r5 = $ff0c + &uword r6 = $ff0e + &uword r7 = $ff10 + &uword r8 = $ff12 + &uword r9 = $ff14 + &uword r10 = $ff16 + &uword r11 = $ff18 + &uword r12 = $ff1a + &uword r13 = $ff1c + &uword r14 = $ff1e + &uword r15 = $ff20 - &word r0s = $0002 - &word r1s = $0004 - &word r2s = $0006 - &word r3s = $0008 - &word r4s = $000a - &word r5s = $000c - &word r6s = $000e - &word r7s = $0010 - &word r8s = $0012 - &word r9s = $0014 - &word r10s = $0016 - &word r11s = $0018 - &word r12s = $001a - &word r13s = $001c - &word r14s = $001e - &word r15s = $0020 + &word r0s = $ff02 + &word r1s = $ff04 + &word r2s = $ff06 + &word r3s = $ff08 + &word r4s = $ff0a + &word r5s = $ff0c + &word r6s = $ff0e + &word r7s = $ff10 + &word r8s = $ff12 + &word r9s = $ff14 + &word r10s = $ff16 + &word r11s = $ff18 + &word r12s = $ff1a + &word r13s = $ff1c + &word r14s = $ff1e + &word r15s = $ff20 - &ubyte r0L = $0002 - &ubyte r1L = $0004 - &ubyte r2L = $0006 - &ubyte r3L = $0008 - &ubyte r4L = $000a - &ubyte r5L = $000c - &ubyte r6L = $000e - &ubyte r7L = $0010 - &ubyte r8L = $0012 - &ubyte r9L = $0014 - &ubyte r10L = $0016 - &ubyte r11L = $0018 - &ubyte r12L = $001a - &ubyte r13L = $001c - &ubyte r14L = $001e - &ubyte r15L = $0020 + &ubyte r0L = $ff02 + &ubyte r1L = $ff04 + &ubyte r2L = $ff06 + &ubyte r3L = $ff08 + &ubyte r4L = $ff0a + &ubyte r5L = $ff0c + &ubyte r6L = $ff0e + &ubyte r7L = $ff10 + &ubyte r8L = $ff12 + &ubyte r9L = $ff14 + &ubyte r10L = $ff16 + &ubyte r11L = $ff18 + &ubyte r12L = $ff1a + &ubyte r13L = $ff1c + &ubyte r14L = $ff1e + &ubyte r15L = $ff20 - &ubyte r0H = $0003 - &ubyte r1H = $0005 - &ubyte r2H = $0007 - &ubyte r3H = $0009 - &ubyte r4H = $000b - &ubyte r5H = $000d - &ubyte r6H = $000f - &ubyte r7H = $0011 - &ubyte r8H = $0013 - &ubyte r9H = $0015 - &ubyte r10H = $0017 - &ubyte r11H = $0019 - &ubyte r12H = $001b - &ubyte r13H = $001d - &ubyte r14H = $001f - &ubyte r15H = $0021 + &ubyte r0H = $ff03 + &ubyte r1H = $ff05 + &ubyte r2H = $ff07 + &ubyte r3H = $ff09 + &ubyte r4H = $ff0b + &ubyte r5H = $ff0d + &ubyte r6H = $ff0f + &ubyte r7H = $ff11 + &ubyte r8H = $ff13 + &ubyte r9H = $ff15 + &ubyte r10H = $ff17 + &ubyte r11H = $ff19 + &ubyte r12H = $ff1b + &ubyte r13H = $ff1d + &ubyte r14H = $ff1f + &ubyte r15H = $ff21 - &byte r0sL = $0002 - &byte r1sL = $0004 - &byte r2sL = $0006 - &byte r3sL = $0008 - &byte r4sL = $000a - &byte r5sL = $000c - &byte r6sL = $000e - &byte r7sL = $0010 - &byte r8sL = $0012 - &byte r9sL = $0014 - &byte r10sL = $0016 - &byte r11sL = $0018 - &byte r12sL = $001a - &byte r13sL = $001c - &byte r14sL = $001e - &byte r15sL = $0020 + &byte r0sL = $ff02 + &byte r1sL = $ff04 + &byte r2sL = $ff06 + &byte r3sL = $ff08 + &byte r4sL = $ff0a + &byte r5sL = $ff0c + &byte r6sL = $ff0e + &byte r7sL = $ff10 + &byte r8sL = $ff12 + &byte r9sL = $ff14 + &byte r10sL = $ff16 + &byte r11sL = $ff18 + &byte r12sL = $ff1a + &byte r13sL = $ff1c + &byte r14sL = $ff1e + &byte r15sL = $ff20 - &byte r0sH = $0003 - &byte r1sH = $0005 - &byte r2sH = $0007 - &byte r3sH = $0009 - &byte r4sH = $000b - &byte r5sH = $000d - &byte r6sH = $000f - &byte r7sH = $0011 - &byte r8sH = $0013 - &byte r9sH = $0015 - &byte r10sH = $0017 - &byte r11sH = $0019 - &byte r12sH = $001b - &byte r13sH = $001d - &byte r14sH = $001f - &byte r15sH = $0021 + &byte r0sH = $ff03 + &byte r1sH = $ff05 + &byte r2sH = $ff07 + &byte r3sH = $ff09 + &byte r4sH = $ff0b + &byte r5sH = $ff0d + &byte r6sH = $ff0f + &byte r7sH = $ff11 + &byte r8sH = $ff13 + &byte r9sH = $ff15 + &byte r10sH = $ff17 + &byte r11sH = $ff19 + &byte r12sH = $ff1b + &byte r13sH = $ff1d + &byte r14sH = $ff1f + &byte r15sH = $ff21 } diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index e0334bfdf..7518bd6d2 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -617,7 +617,7 @@ internal class AstChecker(private val program: Program, err("memory address must be valid integer 0..\$ffff") } } else { - err("value of memory mapped variable can only be a fixed number, perhaps you meant to use an address pointer type instead?") + err("value of memory mapped variable can only be a constant, maybe use an address pointer type instead?") } } } diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 78e403219..4aef57979 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,10 +3,11 @@ TODO For next release ^^^^^^^^^^^^^^^^ -- 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: actually support the physical cpu registers and status flags in the STORECPU and LOADCPU opcodes. -- IR/VM: add proper memory slabs support +- IR: option to save IR in file +- Replace existing vm codegen by expericodegen, expericodegen just stops at saving IR in file. +- vm: implement remaining sin/cos functions in virtual/math.p8 and merge tables +- write some documentation about the compiler architecture and where to plug a code generator onto. - IR/VM: improve unit tests ... @@ -22,20 +23,17 @@ Need help with Future Things and Ideas ^^^^^^^^^^^^^^^^^^^^^^^ Compiler: -- vm: implement remaining sin/cos functions in math.p8 and merge tables +- vm: Jumps go to a code block rather than a specific address(label) -> also helps future dead code elimination? +- vm: the above means that every label introduces a new code block. This eliminates the use of actual labels altogether. +- vm: add more optimizations in IRPeepholeOptimizer - vm: how to remove all unused subroutines? (the 6502 assembly codegen relies on 64tass solve this for us) -- vm: rather than being able to jump to any 'address' (IPTR), use 'blocks' that have entry and exit points -> even better dead code elimination possible too -- vm: add more optimizations in VmPeepholeOptimizer - see if we can let for loops skip the loop if end just 2 special cases in CodeGen. -- when the vm is stable and *if* its language can get promoted to prog8 IL, the variable allocation should be changed. - It's now done before the vm code generation, but the IL should probably not depend on the allocations already performed. - So the CodeGen doesn't do VariableAlloc *before* the codegen, but as a last step instead. - createAssemblyAndAssemble(): make it possible to actually get rid of the VarDecl nodes by fixing the rest of the code mentioned there. but probably better to rewrite the 6502 codegen on top of the new Ast. -- generate WASM from the new ast (or from vm code?) to eventually run prog8 on a browser canvas? +- generate WASM to eventually run prog8 on a browser canvas? - make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as ``p8v_``? Or not worth it (most 3 letter opcodes as variables are nonsensical anyway) then we can get rid of the instruction lists in the machinedefinitions as well? - [problematic due to using 64tass:] add a compiler option to not remove unused subroutines. this allows for building library programs. But this won't work with 64tass's .proc ... diff --git a/examples/test.p8 b/examples/test.p8 index 07fa921b8..a2aeeb8db 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,83 +1,103 @@ -%zpreserved 10,20 -%zpreserved 30,40 main { - uword @shared global1 = 1234 - str @shared globalstring = "irmen" - - %asm {{ - nop - nop - return - }} - - romsub $ee33 = myromsub(ubyte arg1 @A) clobbers() -> ubyte @Y - romsub $ee44 = myromsubmulti(ubyte arg1 @A) clobbers() -> ubyte @Y, ubyte @X, ubyte @Pc - - asmsub testasmsub(ubyte arg1 @A) clobbers(Y) -> uword @AX { - %asm {{ - nop - return - }} - } - sub start() { - void myromsubmulti(44) - global1 = myromsub(44) - sys.wait(1) - %asm {{ - nop - jump _a_label - }} -a_label: + uword @shared slab1 = memory("slab 1", 2000, 0) + uword @shared slab2 = memory("slab 1", 2000, 0) + uword @shared slab3 = memory("other # slab", 2000, 64) - %asmbinary "LICENSE", 200, 513 + uword total = slab1+slab2+slab3 + txt.print_uw(slab1) + txt.nl() + txt.print_uw(slab2) + txt.nl() + txt.print_uw(slab3) + txt.nl() + txt.print_uw(total) + txt.nl() + } +} -; TODO add proper memory SLAB support to IR+VM +; uword @shared global1 = 1234 +; str @shared globalstring = "irmen" +; +; %asm {{ +; nop +; nop +; return +; }} +; +; romsub $ee33 = myromsub(ubyte arg1 @A) clobbers() -> ubyte @Y +; romsub $ee44 = myromsubmulti(ubyte arg1 @A) clobbers() -> ubyte @Y, ubyte @X, ubyte @Pc +; +; asmsub testasmsub(ubyte arg1 @A) clobbers(Y) -> uword @AX { +; %asm {{ +; nop +; return +; }} +; } +; +; sub start() { +; void myromsubmulti(44) +; global1 = myromsub(44) +; sys.wait(1) +; +; cx16.r0 = 11111 +; cx16.r0L = 22 +; cx16.r0H = 33 +; +; %asm {{ +; nop +; jump main.start.a_label +; }} +;a_label: +; +; %asmbinary "LICENSE", 200, 513 +; +;; TODO add proper memory SLAB support to IR+VM ; uword @shared slab1 = memory("slab 1", 2000, 0) ; uword @shared slab2 = memory("slab 1", 2000, 0) ; uword @shared slab3 = memory("other # slab", 2000, 64) - - &uword mapped = $c000 - &ubyte[20] mappedarray = $c100 - - uword @shared zz - ; TODO zz = slab1+slab2+slab3 - - uword @shared @zp qq = zz - uword @shared @zp qq2 = &zz - - str @shared namestring = "irmen" - uword[] @shared wordarray1 = [1111,2222,3333,4444] - uword[4] @shared wordarray2 = 12345 - uword[4] @shared wordzeroarray - - qq=4242 ; TODO should generate symbol not allocated address - mapped = 99 ; TODO wrong VMASM code generated... should generate mapped memory address - mappedarray[1]=99 - - qq=global1 - qq=other.global2 - nested(11,22) - main.start.nested.nested2() - - sub nested(ubyte a1, ubyte a2) { - qq++ - txt.print("zzz") - nested2() - - sub nested2() { - txt.print("zzz2") - qq++ - } - } - } -} - -other { - - uword global2 = 9999 - -} +; +; &uword mapped = $c000 +; &ubyte[20] mappedarray = $c100 +; +; uword @shared zz +; zz = slab1+slab2+slab3 +; +; uword @shared @zp qq = zz +; uword @shared @zp qq2 = &zz +; +; str @shared namestring = "irmen" +; uword[] @shared wordarray1 = [1111,2222,3333,4444] +; uword[4] @shared wordarray2 = 12345 +; uword[4] @shared wordzeroarray +; +; qq=4242 ; TODO should generate symbol not allocated address???? +; mapped = 99 ; TODO wrong VMASM code generated... should generate mapped memory address????? +; mappedarray[1]=99 ; TODO check code???? +; +; qq=global1 +; qq=other.global2 +; nested(11,22) +; main.start.nested.nested2() +; +; sub nested(ubyte a1, ubyte a2) { +; qq++ +; txt.print("zzz") +; nested2() +; +; sub nested2() { +; txt.print("zzz2") +; qq++ +; } +; } +; } +;} +; +;other { +; +; uword global2 = 9999 +; +;} diff --git a/intermediate/src/prog8/intermediate/IRFileReader.kt b/intermediate/src/prog8/intermediate/IRFileReader.kt index 586b33766..4db965f70 100644 --- a/intermediate/src/prog8/intermediate/IRFileReader.kt +++ b/intermediate/src/prog8/intermediate/IRFileReader.kt @@ -446,7 +446,7 @@ class IRFileReader(outputDir: Path, programName: String) { fpReg1 = operand.substring(2).toInt() else { if(operand.startsWith('_')) { - // it's a label. + // it's a label labelSymbol = rest.split(",")[0].trim().substring(1) // keep the original case value = null } else { @@ -462,7 +462,7 @@ class IRFileReader(outputDir: Path, programName: String) { fpReg2 = operand.substring(2).toInt() else { if(operand.startsWith('_')) { - // it's a label. + // it's a label labelSymbol = rest.split(",")[1].trim().substring(1) // keep the original case value = null } else { @@ -478,7 +478,7 @@ class IRFileReader(outputDir: Path, programName: String) { fpReg3 = operand.substring(2).toInt() else { if(operand.startsWith('_')) { - // it's a label. + // it's a label labelSymbol = rest.split(",")[2].trim().substring(1) // keep the original case value = null } else { @@ -489,7 +489,7 @@ class IRFileReader(outputDir: Path, programName: String) { if(operands.isNotEmpty()) { operand = operands.removeFirst().trim() if(operand.startsWith('_')) { - // it's a label. + // it's a label labelSymbol = rest.split(",")[3].trim().substring(1) // keep the original case value = null } else { @@ -573,7 +573,9 @@ class IRFileReader(outputDir: Path, programName: String) { else if(value.startsWith("0x")) value.substring(2).toInt(16).toFloat() else if(value.startsWith('_')) - throw IRParseException("attempt to parse a label as value") + throw IRParseException("attempt to parse a label as numeric value") + else if(value.startsWith('&')) + throw IRParseException("address-of should be done with normal LOAD ") else return value.toFloat() } diff --git a/intermediate/src/prog8/intermediate/IRFileWriter.kt b/intermediate/src/prog8/intermediate/IRFileWriter.kt index 020f6523d..50f1aa741 100644 --- a/intermediate/src/prog8/intermediate/IRFileWriter.kt +++ b/intermediate/src/prog8/intermediate/IRFileWriter.kt @@ -1,7 +1,5 @@ package prog8.intermediate -import prog8.code.StMemVar -import prog8.code.StStaticVariable import prog8.code.core.* import java.io.BufferedWriter import kotlin.io.path.bufferedWriter @@ -139,58 +137,10 @@ class IRFileWriter(private val irProgram: IRProgram) { out.write("\n") out.write("\n\n") - irProgram.st.allMemorySlabs.forEach{ slab -> out.write("SLAB _${slab.name} ${slab.size} ${slab.align}\n") } + irProgram.st.allMemorySlabs.forEach{ slab -> out.write("SLAB ${slab.name} ${slab.size} ${slab.align}\n") } out.write("\n") } - private fun getTypeString(dt : DataType): String { - return when(dt) { - DataType.UBYTE -> "ubyte" - DataType.BYTE -> "byte" - DataType.UWORD -> "uword" - DataType.WORD -> "word" - DataType.FLOAT -> "float" - DataType.ARRAY_UB, DataType.STR -> "ubyte[]" - DataType.ARRAY_B -> "byte[]" - DataType.ARRAY_UW -> "uword[]" - DataType.ARRAY_W -> "word[]" - DataType.ARRAY_F -> "float[]" - else -> throw InternalCompilerException("weird dt") - } - } - - private fun getTypeString(memvar: StMemVar): String { - return when(memvar.dt) { - DataType.UBYTE -> "ubyte" - DataType.BYTE -> "byte" - DataType.UWORD -> "uword" - DataType.WORD -> "word" - DataType.FLOAT -> "float" - DataType.ARRAY_UB, DataType.STR -> "ubyte[${memvar.length}]" - DataType.ARRAY_B -> "byte[${memvar.length}]" - DataType.ARRAY_UW -> "uword[${memvar.length}]" - DataType.ARRAY_W -> "word[${memvar.length}]" - DataType.ARRAY_F -> "float[${memvar.length}]" - else -> throw InternalCompilerException("weird dt") - } - } - - private fun getTypeString(variable : StStaticVariable): String { - return when(variable.dt) { - DataType.UBYTE -> "ubyte" - DataType.BYTE -> "byte" - DataType.UWORD -> "uword" - DataType.WORD -> "word" - DataType.FLOAT -> "float" - DataType.ARRAY_UB, DataType.STR -> "ubyte[${variable.length}]" - DataType.ARRAY_B -> "byte[${variable.length}]" - DataType.ARRAY_UW -> "uword[${variable.length}]" - DataType.ARRAY_W -> "word[${variable.length}]" - DataType.ARRAY_F -> "float[${variable.length}]" - else -> throw InternalCompilerException("weird dt") - } - } - private fun BufferedWriter.writeLine(line: IRCodeLine) { when(line) { is IRCodeComment -> write("; ${line.comment}\n") diff --git a/intermediate/src/prog8/intermediate/Instructions.kt b/intermediate/src/prog8/intermediate/Instructions.kt index 9d8e1360f..f36b4457b 100644 --- a/intermediate/src/prog8/intermediate/Instructions.kt +++ b/intermediate/src/prog8/intermediate/Instructions.kt @@ -23,7 +23,7 @@ LOAD/STORE ---------- All have type b or w or f. -load reg1, value - load immediate value into register +load reg1, value - load immediate value into register. If you supply a symbol, loads the address of the symbol. loadm reg1, address - load reg1 with value at memory address loadi reg1, reg2 - load reg1 with value at memory indirect, memory pointed to by reg2 loadx reg1, reg2, address - load reg1 with value at memory address indexed by value in reg2 @@ -204,7 +204,7 @@ binarydata - 'instruction' to hold inlined binary enum class Opcode { NOP, - LOAD, + LOAD, // note: LOAD gets you the address of the symbol, whereas LOADM would get you the value stored at that location LOADM, LOADI, LOADX, diff --git a/intermediate/src/prog8/intermediate/Utils.kt b/intermediate/src/prog8/intermediate/Utils.kt new file mode 100644 index 000000000..5fbc5a61c --- /dev/null +++ b/intermediate/src/prog8/intermediate/Utils.kt @@ -0,0 +1,55 @@ +package prog8.intermediate + +import prog8.code.StMemVar +import prog8.code.StStaticVariable +import prog8.code.core.DataType +import prog8.code.core.InternalCompilerException + + +public fun getTypeString(dt : DataType): String { + return when(dt) { + DataType.UBYTE -> "ubyte" + DataType.BYTE -> "byte" + DataType.UWORD -> "uword" + DataType.WORD -> "word" + DataType.FLOAT -> "float" + DataType.ARRAY_UB, DataType.STR -> "ubyte[]" + DataType.ARRAY_B -> "byte[]" + DataType.ARRAY_UW -> "uword[]" + DataType.ARRAY_W -> "word[]" + DataType.ARRAY_F -> "float[]" + else -> throw InternalCompilerException("weird dt") + } +} + +public fun getTypeString(memvar: StMemVar): String { + return when(memvar.dt) { + DataType.UBYTE -> "ubyte" + DataType.BYTE -> "byte" + DataType.UWORD -> "uword" + DataType.WORD -> "word" + DataType.FLOAT -> "float" + DataType.ARRAY_UB, DataType.STR -> "ubyte[${memvar.length}]" + DataType.ARRAY_B -> "byte[${memvar.length}]" + DataType.ARRAY_UW -> "uword[${memvar.length}]" + DataType.ARRAY_W -> "word[${memvar.length}]" + DataType.ARRAY_F -> "float[${memvar.length}]" + else -> throw InternalCompilerException("weird dt") + } +} + +public fun getTypeString(variable : StStaticVariable): String { + return when(variable.dt) { + DataType.UBYTE -> "ubyte" + DataType.BYTE -> "byte" + DataType.UWORD -> "uword" + DataType.WORD -> "word" + DataType.FLOAT -> "float" + DataType.ARRAY_UB, DataType.STR -> "ubyte[${variable.length}]" + DataType.ARRAY_B -> "byte[${variable.length}]" + DataType.ARRAY_UW -> "uword[${variable.length}]" + DataType.ARRAY_W -> "word[${variable.length}]" + DataType.ARRAY_F -> "float[${variable.length}]" + else -> throw InternalCompilerException("weird dt") + } +} diff --git a/virtualmachine/src/prog8/vm/Assembler.kt b/virtualmachine/src/prog8/vm/Assembler.kt index ea4ea2add..45e880fa5 100644 --- a/virtualmachine/src/prog8/vm/Assembler.kt +++ b/virtualmachine/src/prog8/vm/Assembler.kt @@ -5,7 +5,7 @@ import prog8.intermediate.* class Assembler { - private val labels = mutableMapOf() + private val symbolAddresses = mutableMapOf() private val placeholders = mutableMapOf() init { @@ -15,8 +15,8 @@ class Assembler { } fun initializeMemory(memsrc: String, memory: Memory) { - labels.clear() - val instrPattern = Regex("""var (.+) @([0-9]+) ([a-z]+) (.+)""", RegexOption.IGNORE_CASE) + symbolAddresses.clear() + val instrPattern = Regex("""var (.+) @([0-9]+) ([a-z]+)(\[[0-9]+\])? (.+)""", RegexOption.IGNORE_CASE) for(line in memsrc.lines()) { if(line.isBlank() || line.startsWith(';')) continue @@ -24,9 +24,10 @@ class Assembler { if(match==null) throw IllegalArgumentException("invalid line $line") else { - val (name, addrStr, datatype, values) = match.destructured + val (name, addrStr, datatype, arrayspec, values) = match.destructured + val numArrayElts = if(arrayspec.isBlank()) 1 else arrayspec.substring(1, arrayspec.length-1).toInt() var address = parseValue(Opcode.LOADCPU, addrStr, 0).toInt() - labels[name] = address + symbolAddresses[name] = address when(datatype) { "str" -> { val string = values.trim('"').unescape() @@ -38,23 +39,50 @@ class Assembler { } "ubyte", "byte" -> { val array = values.split(',').map { parseValue(Opcode.LOADCPU, it.trim(), 0).toInt() } - for (value in array) { - memory.setUB(address, value.toUByte()) - address++ + require(array.size==numArrayElts || array.size==1) + if(numArrayElts > array.size) { + val value = array.single().toUByte() + repeat(numArrayElts) { + memory.setUB(address, value) + address++ + } + } else { + for (value in array) { + memory.setUB(address, value.toUByte()) + address++ + } } } "uword", "word" -> { val array = values.split(',').map { parseValue(Opcode.LOADCPU, it.trim(), 0).toInt() } - for (value in array) { - memory.setUW(address, value.toUShort()) - address += 2 + require(array.size==numArrayElts || array.size==1) + if(numArrayElts>array.size) { + val value = array.single().toUShort() + repeat(numArrayElts) { + memory.setUW(address, value) + address += 2 + } + } else { + for (value in array) { + memory.setUW(address, value.toUShort()) + address += 2 + } } } "float" -> { val array = values.split(',').map { it.toFloat() } - for (value in array) { - memory.setFloat(address, value) - address += 4 // 32-bits floats + require(array.size==numArrayElts || array.size==1) + if(numArrayElts>array.size) { + val value = array.single() + repeat(numArrayElts) { + memory.setFloat(address, value) + address += 4 // 32-bits floats + } + } else { + for (value in array) { + memory.setFloat(address, value) + address += 4 // 32-bits floats + } } } else -> throw IllegalArgumentException("invalid datatype $datatype") @@ -87,9 +115,9 @@ class Assembler { throw IllegalArgumentException("invalid line $line at line ${program.size + 1}") else { val label = labelmatch.groupValues[1] - if (label in labels) + if (label in symbolAddresses) throw IllegalArgumentException("label redefined $label") - labels[label] = program.size + symbolAddresses[label] = program.size } } } else { @@ -245,13 +273,13 @@ class Assembler { private fun pass2replaceLabels(program: MutableList) { for((line, label) in placeholders) { - val replacement = labels[label] + 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 = labels.getValue(symbol) + index + val address = symbolAddresses.getValue(symbol) + index program[line] = program[line].copy(value = address) } else { throw IllegalArgumentException("placeholder not found in labels: $label")