mirror of
https://github.com/irmen/prog8.git
synced 2024-12-26 14:29:35 +00:00
IR support for storing incbins and romsubs
This commit is contained in:
parent
0e831d4b92
commit
2f3e7d1c27
@ -238,7 +238,11 @@ class StSub(name: String, val parameters: List<StSubroutineParameter>, val retur
|
||||
}
|
||||
|
||||
|
||||
class StRomSub(name: String, val address: UInt, val parameters: List<StSubroutineParameter>, val returnTypes: List<DataType>, position: Position) :
|
||||
class StRomSub(name: String,
|
||||
val address: UInt,
|
||||
val parameters: List<StRomSubParameter>,
|
||||
val returns: List<RegisterOrStatusflag>,
|
||||
position: Position) :
|
||||
StNode(name, StNodeType.ROMSUB, position) {
|
||||
override fun printProperties() {
|
||||
print("$name address=${address.toHex()}")
|
||||
@ -247,6 +251,7 @@ class StRomSub(name: String, val address: UInt, val parameters: List<StSubroutin
|
||||
|
||||
|
||||
class StSubroutineParameter(val name: String, val type: DataType)
|
||||
class StRomSubParameter(val register: RegisterOrStatusflag, val type: DataType)
|
||||
class StArrayElement(val number: Double?, val addressOf: List<String>?)
|
||||
|
||||
typealias StString = Pair<String, Encoding>
|
||||
|
@ -913,7 +913,31 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
||||
return code
|
||||
}
|
||||
is StRomSub -> {
|
||||
throw AssemblyError("virtual machine doesn't yet support calling romsub $fcall")
|
||||
val code = IRCodeChunk(fcall.position)
|
||||
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
||||
val paramDt = codeGen.vmType(parameter.type)
|
||||
val paramRegStr = if(parameter.register.registerOrPair!=null) parameter.register.registerOrPair.toString() else parameter.register.statusflag.toString()
|
||||
if(codeGen.isZero(arg)) {
|
||||
code += IRCodeInstruction(Opcode.STOREZCPU, paramDt, labelSymbol = paramRegStr)
|
||||
} else {
|
||||
if (paramDt == VmDataType.FLOAT)
|
||||
throw AssemblyError("doesn't support float register argument in asm romsub")
|
||||
val argReg = codeGen.vmRegisters.nextFree()
|
||||
code += translateExpression(arg, argReg, -1)
|
||||
code += IRCodeInstruction(Opcode.STORECPU, paramDt, reg1 = argReg, labelSymbol = paramRegStr)
|
||||
}
|
||||
}
|
||||
code += IRCodeInstruction(Opcode.CALL, value=callTarget.address.toInt())
|
||||
if(!fcall.void) {
|
||||
if(callTarget.returns.size!=1)
|
||||
throw AssemblyError("expect precisely 1 return value")
|
||||
if(fcall.type==DataType.FLOAT)
|
||||
throw AssemblyError("doesn't support float register result in asm romsub")
|
||||
val returns = callTarget.returns.single()
|
||||
val regStr = if(returns.registerOrPair!=null) returns.registerOrPair.toString() else returns.statusflag.toString()
|
||||
code += IRCodeInstruction(Opcode.LOADCPU, codeGen.vmType(fcall.type), reg1=resultRegister, labelSymbol = regStr)
|
||||
}
|
||||
return code
|
||||
}
|
||||
else -> throw AssemblyError("invalid node type")
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -230,7 +230,8 @@ class IntermediateAstMaker(val program: Program) {
|
||||
val type = srcCall.inferType(program).getOrElse {
|
||||
throw FatalAstException("unknown dt $srcCall")
|
||||
}
|
||||
val call = PtFunctionCall(target, type==DataType.UNDEFINED, type, srcCall.position)
|
||||
val isVoid = type==DataType.UNDEFINED
|
||||
val call = PtFunctionCall(target, isVoid, type, srcCall.position)
|
||||
for (arg in srcCall.args)
|
||||
call.add(transformExpression(arg))
|
||||
return call
|
||||
|
@ -36,12 +36,13 @@ internal class SymbolTableMaker: IAstVisitor {
|
||||
}
|
||||
|
||||
override fun visit(subroutine: Subroutine) {
|
||||
val parameters = subroutine.parameters.map { StSubroutineParameter(it.name, it.type) }
|
||||
if(subroutine.asmAddress!=null) {
|
||||
val node = StRomSub(subroutine.name, subroutine.asmAddress!!, parameters, subroutine.returntypes, subroutine.position)
|
||||
val parameters = subroutine.parameters.zip(subroutine.asmParameterRegisters).map { StRomSubParameter(it.second, it.first.type) }
|
||||
val node = StRomSub(subroutine.name, subroutine.asmAddress!!, parameters, subroutine.asmParameterRegisters, subroutine.position)
|
||||
scopestack.peek().add(node)
|
||||
// st.origAstLinks[subroutine] = node
|
||||
} else {
|
||||
val parameters = subroutine.parameters.map { StSubroutineParameter(it.name, it.type) }
|
||||
val returnType = if(subroutine.returntypes.isEmpty()) null else subroutine.returntypes.first()
|
||||
val node = StSub(subroutine.name, parameters, returnType, subroutine.position)
|
||||
scopestack.peek().add(node)
|
||||
|
@ -3,8 +3,9 @@ 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: add support for incbin (!binary)
|
||||
- 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 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
|
||||
|
||||
...
|
||||
@ -21,9 +22,6 @@ Future Things and Ideas
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Compiler:
|
||||
- vm: implement remaining sin/cos functions in math.p8 and merge tables
|
||||
- vm: implement memory mapped variables properly in VariableAllocator
|
||||
- vm: find a solution for the cx16.r0..r15 that "overlap" (r0, r0L, r0H etc) but in the vm each get their own separate variable location now. Maybe this gets solved by the previous item?
|
||||
- vm: encode romsub & romsub call in VM IR (can just crash in virtualmachine itself because program is not in the simulated memory) ExpressionGen.kt
|
||||
- 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
|
||||
|
@ -12,6 +12,9 @@ main {
|
||||
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
|
||||
@ -20,6 +23,8 @@ main {
|
||||
}
|
||||
|
||||
sub start() {
|
||||
void myromsubmulti(44)
|
||||
global1 = myromsub(44)
|
||||
sys.wait(1)
|
||||
|
||||
%asm {{
|
||||
|
@ -240,7 +240,7 @@ class IRFileReader(outputDir: Path, programName: String) {
|
||||
|
||||
private val blockPattern = Regex("<BLOCK NAME=(.+) ADDRESS=(.+) ALIGN=(.+) POS=(.+)>")
|
||||
private val inlineAsmPattern = Regex("<INLINEASM POS=(.+)>")
|
||||
private val asmsubPattern = Regex("<ASMSUB NAME=(.+) ADDRESS=(.+) CLOBBERS=(.+) RETURNS=(.+) POS=(.+)>")
|
||||
private val asmsubPattern = Regex("<ASMSUB NAME=(.+) ADDRESS=(.+) CLOBBERS=(.*) RETURNS=(.*) POS=(.+)>")
|
||||
private val subPattern = Regex("<SUB NAME=(.+) RETURNTYPE=(.+) POS=(.+)>")
|
||||
private val posPattern = Regex("\\[(.+): line (.+) col (.+)-(.+)\\]")
|
||||
private val instructionPattern = Regex("""([a-z]+)(\.b|\.w|\.f)?(.*)""", RegexOption.IGNORE_CASE)
|
||||
@ -309,7 +309,7 @@ class IRFileReader(outputDir: Path, programName: String) {
|
||||
val asm = parseInlineAssembly(line, lines)
|
||||
while(line!="</ASMSUB>")
|
||||
line = lines.next()
|
||||
val clobberRegs = clobbers.split(',').map { CpuRegister.valueOf(it) }
|
||||
val clobberRegs = if(clobbers.isBlank()) emptyList() else clobbers.split(',').map { CpuRegister.valueOf(it) }
|
||||
val returns = mutableListOf<Pair<DataType, RegisterOrStatusflag>>()
|
||||
returnSpec.split(',').forEach{ rs ->
|
||||
val (regstr, dtstr) = rs.split(':')
|
||||
|
@ -29,12 +29,15 @@ loadi reg1, reg2 - load reg1 with value at memory indirect,
|
||||
loadx reg1, reg2, address - load reg1 with value at memory address indexed by value in reg2
|
||||
loadix reg1, reg2, pointeraddr - load reg1 with value at memory indirect, pointed to by pointeraddr indexed by value in reg2
|
||||
loadr reg1, reg2 - load reg1 with value in register reg2
|
||||
loadcpu reg1, cpureg - load reg1 with value from cpu register (register/registerpair/statusflag)
|
||||
|
||||
storem reg1, address - store reg1 at memory address
|
||||
storecpu reg1, cpureg - store reg1 in cpu register (register/registerpair/statusflag)
|
||||
storei reg1, reg2 - store reg1 at memory indirect, memory pointed to by reg2
|
||||
storex reg1, reg2, address - store reg1 at memory address, indexed by value in reg2
|
||||
storeix reg1, reg2, pointeraddr - store reg1 at memory indirect, pointed to by pointeraddr indexed by value in reg2
|
||||
storezm address - store zero at memory address
|
||||
storezcpu cpureg - store zero in cpu register (register/registerpair/statusflag)
|
||||
storezi reg1 - store zero at memory pointed to by reg1
|
||||
storezx reg1, address - store zero at memory address, indexed by value in reg
|
||||
|
||||
@ -196,7 +199,7 @@ msig [b, w] reg1, reg2 - reg1 becomes the most significant by
|
||||
concat [b, w] reg1, reg2 - reg1 = concatenated lsb/lsw of reg1 (as lsb) and lsb/lsw of reg2 (as msb) into word or int (int not yet implemented; requires 32bits regs)
|
||||
push [b, w] reg1 - push value in reg1 on the stack
|
||||
pop [b, w] reg1 - pop value from stack into reg1
|
||||
|
||||
binarydata - 'instruction' to hold inlined binary data bytes
|
||||
*/
|
||||
|
||||
enum class Opcode {
|
||||
@ -207,11 +210,14 @@ enum class Opcode {
|
||||
LOADX,
|
||||
LOADIX,
|
||||
LOADR,
|
||||
LOADCPU,
|
||||
STOREM,
|
||||
STORECPU,
|
||||
STOREI,
|
||||
STOREX,
|
||||
STOREIX,
|
||||
STOREZM,
|
||||
STOREZCPU,
|
||||
STOREZI,
|
||||
STOREZX,
|
||||
|
||||
@ -338,7 +344,8 @@ enum class Opcode {
|
||||
POP,
|
||||
MSIG,
|
||||
CONCAT,
|
||||
BREAKPOINT
|
||||
BREAKPOINT,
|
||||
BINARYDATA
|
||||
}
|
||||
|
||||
val OpcodesWithAddress = setOf(
|
||||
@ -393,7 +400,8 @@ data class Instruction(
|
||||
val fpReg2: Int?=null, // 0-$ffff
|
||||
val value: Int?=null, // 0-$ffff
|
||||
val fpValue: Float?=null,
|
||||
val labelSymbol: List<String>?=null // symbolic label name as alternative to value (so only for Branch/jump/call Instructions!)
|
||||
val labelSymbol: List<String>?=null, // symbolic label name as alternative to value (so only for Branch/jump/call Instructions!)
|
||||
val binaryData: ByteArray?=null
|
||||
) {
|
||||
// reg1 and fpreg1 can be IN/OUT/INOUT (all others are readonly INPUT)
|
||||
// This knowledge is useful in IL assembly optimizers to see how registers are used.
|
||||
@ -401,6 +409,9 @@ data class Instruction(
|
||||
val fpReg1direction: OperandDirection
|
||||
|
||||
init {
|
||||
if(opcode==Opcode.BINARYDATA && binaryData==null || binaryData!=null && opcode!=Opcode.BINARYDATA)
|
||||
throw IllegalArgumentException("binarydata inconsistency")
|
||||
|
||||
val formats = instructionFormats.getValue(opcode)
|
||||
if(type==null && !formats.containsKey(null))
|
||||
throw IllegalArgumentException("missing type")
|
||||
@ -560,11 +571,14 @@ val instructionFormats = mutableMapOf(
|
||||
Opcode.LOADX to InstructionFormat.from("BW,>r1,<r2,<v | F,>fr1,<r1,<v"),
|
||||
Opcode.LOADIX to InstructionFormat.from("BW,>r1,<r2,<v | F,>fr1,<r1,<v"),
|
||||
Opcode.LOADR to InstructionFormat.from("BW,>r1,<r2 | F,>fr1,<fr2"),
|
||||
Opcode.LOADCPU to InstructionFormat.from("BW,>r1"),
|
||||
Opcode.STOREM to InstructionFormat.from("BW,<r1,<v | F,<fr1,<v"),
|
||||
Opcode.STORECPU to InstructionFormat.from("BW,<r1"),
|
||||
Opcode.STOREI to InstructionFormat.from("BW,<r1,<r2 | F,<fr1,<r1"),
|
||||
Opcode.STOREX to InstructionFormat.from("BW,<r1,<r2,<v | F,<fr1,<r1,<v"),
|
||||
Opcode.STOREIX to InstructionFormat.from("BW,<r1,<r2,<v | F,<fr1,<r1,<v"),
|
||||
Opcode.STOREZM to InstructionFormat.from("BW,<v | F,<v"),
|
||||
Opcode.STOREZCPU to InstructionFormat.from("BW"),
|
||||
Opcode.STOREZI to InstructionFormat.from("BW,<r1 | F,<r1"),
|
||||
Opcode.STOREZX to InstructionFormat.from("BW,<r1,<v | F,<r1,<v"),
|
||||
Opcode.JUMP to InstructionFormat.from("N,<v"),
|
||||
@ -688,4 +702,5 @@ val instructionFormats = mutableMapOf(
|
||||
Opcode.CLC to InstructionFormat.from("N"),
|
||||
Opcode.SEC to InstructionFormat.from("N"),
|
||||
Opcode.BREAKPOINT to InstructionFormat.from("N"),
|
||||
Opcode.BINARYDATA to InstructionFormat.from("N"),
|
||||
)
|
||||
|
@ -76,7 +76,10 @@ class Assembler {
|
||||
val binarymatch = binaryPattern.matchEntire(line.trim())
|
||||
if(binarymatch!=null) {
|
||||
val hex = binarymatch.groups[1]!!.value
|
||||
TODO("binary ${hex}")
|
||||
val binary = hex.windowed(size=2, step=2).map {
|
||||
it.toString().toByte(16)
|
||||
}.toByteArray()
|
||||
program.add(Instruction(Opcode.BINARYDATA, binaryData = binary))
|
||||
} else {
|
||||
val labelmatch = labelPattern.matchEntire(line.trim())
|
||||
if (labelmatch == null)
|
||||
|
@ -217,6 +217,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
|
||||
Opcode.BREAKPOINT -> InsBREAKPOINT()
|
||||
Opcode.CLC -> { statusCarry = false; pc++ }
|
||||
Opcode.SEC -> { statusCarry = true; pc++ }
|
||||
Opcode.BINARYDATA -> TODO("BINARYDATA not yet supported in VM")
|
||||
|
||||
Opcode.FFROMUB -> InsFFROMUB(ins)
|
||||
Opcode.FFROMSB -> InsFFROMSB(ins)
|
||||
|
Loading…
Reference in New Issue
Block a user