mirror of
https://github.com/irmen/prog8.git
synced 2025-03-14 11:34:32 +00:00
IR: mem mapped vars and memory slabs
This commit is contained in:
parent
c8f3bfa726
commit
627ed51a1b
@ -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.
|
||||
|
@ -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 -> {
|
||||
|
@ -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() }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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}\"")
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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?")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 ...
|
||||
|
166
examples/test.p8
166
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
|
||||
;
|
||||
;}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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,
|
||||
|
55
intermediate/src/prog8/intermediate/Utils.kt
Normal file
55
intermediate/src/prog8/intermediate/Utils.kt
Normal 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")
|
||||
}
|
||||
}
|
@ -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")
|
||||
|
Loading…
x
Reference in New Issue
Block a user