IR: mem mapped vars and memory slabs

This commit is contained in:
Irmen de Jong 2022-09-18 16:04:49 +02:00
parent c8f3bfa726
commit 627ed51a1b
14 changed files with 387 additions and 278 deletions

View File

@ -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<Triple<IRCodeChunkBase, Int, UInt>>()
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.

View File

@ -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 <symbol> gets you the address of the symbol, whereas LOADM <symbol> would get you the value stored at that location
code += IRCodeInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, labelSymbol = symbol)
}
is PtMemoryByte -> {

View File

@ -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() }
}
}

View File

@ -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<String>) = allocations.getValue(name)
fun asVmMemory(): List<Pair<List<String>, String>> {
val mm = mutableListOf<Pair<List<String>, 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
}

View File

@ -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}\"")

View File

@ -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
}

View File

@ -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?")
}
}
}

View File

@ -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<start, like other programming languages. Without adding a lot of code size/duplicating the loop condition.
this is documented behavior to now loop around but it's too easy to forget about!
Lot of work because of so many special cases in ForLoopsAsmgen.....
How is it for the vm target? -> 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 ...

View File

@ -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
;
;}

View File

@ -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 <symbol>")
else
return value.toFloat()
}

View File

@ -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("</MEMORYMAPPEDVARIABLES>\n")
out.write("\n<MEMORYSLABS>\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("</MEMORYSLABS>\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")

View File

@ -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 <symbol> gets you the address of the symbol, whereas LOADM <symbol> would get you the value stored at that location
LOADM,
LOADI,
LOADX,

View File

@ -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")
}
}

View File

@ -5,7 +5,7 @@ import prog8.intermediate.*
class Assembler {
private val labels = mutableMapOf<String, Int>()
private val symbolAddresses = mutableMapOf<String, Int>()
private val placeholders = mutableMapOf<Int, String>()
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<Instruction>) {
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")