mirror of
https://github.com/irmen/prog8.git
synced 2025-02-25 04:29:36 +00:00
IR: fix romsub encoding
This commit is contained in:
parent
905921a684
commit
630c8a5faa
@ -235,7 +235,7 @@ class StSub(name: String, val parameters: List<StSubroutineParameter>, val retur
|
||||
|
||||
|
||||
class StRomSub(name: String,
|
||||
val address: UInt,
|
||||
val address: UInt?, // null in case of asmsub, specified in case of romsub
|
||||
val parameters: List<StRomSubParameter>,
|
||||
val returns: List<StRomSubParameter>,
|
||||
astNode: PtNode) :
|
||||
|
@ -40,14 +40,9 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
||||
private fun addToSt(node: PtNode, scope: Stack<StNode>) {
|
||||
val stNode = when(node) {
|
||||
is PtAsmSub -> {
|
||||
if(node.address==null) {
|
||||
val params = node.parameters.map { StSubroutineParameter(it.second.name, it.second.type) }
|
||||
StSub(node.name, params, node.returns.singleOrNull()?.second, node)
|
||||
} else {
|
||||
val parameters = node.parameters.map { StRomSubParameter(it.first, it.second.type) }
|
||||
val returns = node.returns.map { StRomSubParameter(it.first, it.second) }
|
||||
StRomSub(node.name, node.address, parameters, returns, node)
|
||||
}
|
||||
val parameters = node.parameters.map { StRomSubParameter(it.first, it.second.type) }
|
||||
val returns = node.returns.map { StRomSubParameter(it.first, it.second) }
|
||||
StRomSub(node.name, node.address, parameters, returns, node)
|
||||
}
|
||||
is PtBlock -> {
|
||||
StNode(node.name, StNodeType.BLOCK, node)
|
||||
|
@ -4,7 +4,10 @@ import prog8.code.StRomSub
|
||||
import prog8.code.StStaticVariable
|
||||
import prog8.code.StSub
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.PassByValueDatatypes
|
||||
import prog8.code.core.SignedDatatypes
|
||||
import prog8.intermediate.*
|
||||
|
||||
internal class ExpressionCodeResult(val chunks: IRCodeChunks, val dt: IRDataType, val resultReg: Int, val resultFpReg: Int) {
|
||||
@ -361,10 +364,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
addInstr(result, call, null)
|
||||
return if(fcall.void)
|
||||
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
else if(call.fcallArgs!!.returns!!.dt==IRDataType.FLOAT)
|
||||
ExpressionCodeResult(result, codeGen.irType(fcall.type), -1, call.fcallArgs!!.returns!!.registerNum)
|
||||
else if(fcall.type==DataType.FLOAT)
|
||||
ExpressionCodeResult(result, returnRegSpec!!.dt, -1, returnRegSpec.registerNum)
|
||||
else
|
||||
ExpressionCodeResult(result, codeGen.irType(fcall.type), call.fcallArgs!!.returns!!.registerNum, -1)
|
||||
ExpressionCodeResult(result, returnRegSpec!!.dt, returnRegSpec.registerNum, -1)
|
||||
}
|
||||
is StRomSub -> {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
@ -401,30 +404,18 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
}
|
||||
// create the call
|
||||
val call = IRInstruction(Opcode.CALL, address = callTarget.address.toInt(), fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec))
|
||||
val call =
|
||||
if(callTarget.address==null)
|
||||
IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec))
|
||||
else
|
||||
IRInstruction(Opcode.CALL, address = callTarget.address!!.toInt(), fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec))
|
||||
addInstr(result, call, null)
|
||||
if(fcall.void) {
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
} else {
|
||||
val regStr = if(callTarget.returns.isEmpty())
|
||||
throw AssemblyError("expect a return value")
|
||||
else if(callTarget.returns.size==1) {
|
||||
val returns = callTarget.returns.single()
|
||||
if(returns.register.registerOrPair!=null) returns.register.registerOrPair.toString() else returns.register.statusflag.toString()
|
||||
} else {
|
||||
// multiple return values: take the first *register* (not status flag) return value and ignore the rest.
|
||||
callTarget.returns.first { it.register.registerOrPair!=null }.toString()
|
||||
}
|
||||
return if(fcall.type==DataType.FLOAT) {
|
||||
val resultFpReg = codeGen.registers.nextFreeFloat()
|
||||
addInstr(result, IRInstruction(Opcode.LOADCPU, IRDataType.FLOAT, fpReg1 = resultFpReg, labelSymbol = regStr), null)
|
||||
ExpressionCodeResult(result, returnRegSpec!!.dt, -1, resultFpReg)
|
||||
} else {
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.LOADCPU, returnRegSpec!!.dt, reg1 = resultReg, labelSymbol = regStr), null)
|
||||
ExpressionCodeResult(result, returnRegSpec.dt, resultReg, -1)
|
||||
}
|
||||
}
|
||||
return if(fcall.void)
|
||||
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
else if(fcall.type==DataType.FLOAT)
|
||||
ExpressionCodeResult(result, returnRegSpec!!.dt, -1, returnRegSpec.registerNum)
|
||||
else
|
||||
ExpressionCodeResult(result, returnRegSpec!!.dt, returnRegSpec.registerNum, -1)
|
||||
}
|
||||
else -> throw AssemblyError("invalid node type")
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ TODO
|
||||
|
||||
For next minor release
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
- fix VM problem with param passing: void string.copy(".prg", &output_filename + string.length(output_filename))
|
||||
- fix romsub encoding into IR
|
||||
|
||||
...
|
||||
|
||||
|
@ -1,27 +1,15 @@
|
||||
%import textio
|
||||
%import string
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
txt.chrout('!')
|
||||
txt.print("test")
|
||||
txt.nl()
|
||||
; uword seconds_uword = 1
|
||||
; uword remainder = seconds_uword % $0003 ==0
|
||||
; txt.print_uw(remainder)
|
||||
;
|
||||
; blerp()
|
||||
ubyte @shared foo = derp(99)
|
||||
}
|
||||
|
||||
sub blerp() {
|
||||
%ir {{
|
||||
_xxx:
|
||||
loadr r2,r3
|
||||
_yyy:
|
||||
loadr r3,r4
|
||||
return
|
||||
asmsub derp(ubyte xx @Y) -> ubyte @ A {
|
||||
%asm {{
|
||||
rts
|
||||
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package prog8.intermediate
|
||||
|
||||
import prog8.code.core.CpuRegister
|
||||
import prog8.code.core.RegisterOrStatusflag
|
||||
import prog8.code.core.toHex
|
||||
|
||||
@ -39,15 +38,11 @@ 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
|
||||
|
||||
@ -60,6 +55,9 @@ call label(argument register list) [: resultreg.type]
|
||||
- calls a subroutine with the given arguments and return value (optional).
|
||||
save current instruction location+1, continue execution at instruction nr of the label.
|
||||
the argument register list is positional and includes the datatype, ex.: r4.b,r5.w,fp1.f
|
||||
If the call is to a rom-routine, 'label' will be a hexadecimal address instead such as $ffd2
|
||||
If the arguments should be passed in CPU registers, they'll have a @REGISTER postfix.
|
||||
For example: call $ffd2(r5.b@A)
|
||||
syscall number (argument register list) [: resultreg.type]
|
||||
- do a systemcall identified by number, result value(s) are pushed on value stack by the syscall code so
|
||||
will be POPped off into the given resultregister if any.
|
||||
@ -231,9 +229,7 @@ enum class Opcode {
|
||||
LOADX,
|
||||
LOADIX,
|
||||
LOADR,
|
||||
LOADCPU,
|
||||
STOREM,
|
||||
STORECPU,
|
||||
STOREI,
|
||||
STOREX,
|
||||
STOREIX,
|
||||
@ -418,11 +414,6 @@ val OpcodesThatBranch = setOf(
|
||||
Opcode.BLES
|
||||
)
|
||||
|
||||
val OpcodesForCpuRegisters = setOf(
|
||||
Opcode.LOADCPU,
|
||||
Opcode.STORECPU
|
||||
)
|
||||
|
||||
enum class IRDataType {
|
||||
BYTE,
|
||||
WORD,
|
||||
@ -513,9 +504,7 @@ val instructionFormats = mutableMapOf(
|
||||
Opcode.LOADX to InstructionFormat.from("BW,>r1,<r2,<a | F,>fr1,<r1,<a"),
|
||||
Opcode.LOADIX to InstructionFormat.from("BW,>r1,<r2,<a | F,>fr1,<r1,<a"),
|
||||
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,>a | F,<fr1,>a"),
|
||||
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,>a | F,<fr1,<r1,>a"),
|
||||
Opcode.STOREIX to InstructionFormat.from("BW,<r1,<r2,>a | F,<fr1,<r1,>a"),
|
||||
@ -823,33 +812,47 @@ data class IRInstruction(
|
||||
}
|
||||
|
||||
if(this.fcallArgs!=null) {
|
||||
immediate?.let { result.add(it.toHex()) }
|
||||
labelSymbol?.let { result.add(it) }
|
||||
immediate?.let { result.add(it.toHex()) } // syscall
|
||||
labelSymbol?.let { result.add(it) } // regular subroutine call
|
||||
address?.let { result.add(address.toHex()) } // romcall
|
||||
result.add("(")
|
||||
fcallArgs.arguments.forEach {
|
||||
val location = if(it.address==null) {
|
||||
if(it.name.isBlank()) "" else it.name+"="
|
||||
} else "${it.address}="
|
||||
|
||||
if(it.reg.cpuRegister!=null)
|
||||
TODO("handle cpuregister ${it.reg.cpuRegister}")
|
||||
val cpuReg = if(it.reg.cpuRegister==null) "" else {
|
||||
if(it.reg.cpuRegister.registerOrPair!=null)
|
||||
"@"+it.reg.cpuRegister.registerOrPair.toString()
|
||||
else
|
||||
"@"+it.reg.cpuRegister.statusflag.toString()
|
||||
}
|
||||
|
||||
when(it.reg.dt) {
|
||||
IRDataType.BYTE -> result.add("${location}r${it.reg.registerNum}.b,")
|
||||
IRDataType.WORD -> result.add("${location}r${it.reg.registerNum}.w,")
|
||||
IRDataType.FLOAT -> result.add("${location}fr${it.reg.registerNum}.f,")
|
||||
IRDataType.BYTE -> result.add("${location}r${it.reg.registerNum}.b$cpuReg,")
|
||||
IRDataType.WORD -> result.add("${location}r${it.reg.registerNum}.w$cpuReg,")
|
||||
IRDataType.FLOAT -> result.add("${location}fr${it.reg.registerNum}.f$cpuReg,")
|
||||
}
|
||||
}
|
||||
if(result.last().endsWith(',')) {
|
||||
result.add(result.removeLast().trimEnd(','))
|
||||
}
|
||||
result.add(")")
|
||||
fcallArgs.returns?.let {
|
||||
val returns = fcallArgs.returns
|
||||
if(returns!=null) {
|
||||
result.add(":")
|
||||
when(it.dt) {
|
||||
IRDataType.BYTE -> result.add("r${it.registerNum}.b")
|
||||
IRDataType.WORD -> result.add("r${it.registerNum}.w")
|
||||
IRDataType.FLOAT -> result.add("fr${it.registerNum}.f")
|
||||
when (returns.dt) {
|
||||
IRDataType.BYTE -> result.add("r${returns.registerNum}.b")
|
||||
IRDataType.WORD -> result.add("r${returns.registerNum}.w")
|
||||
IRDataType.FLOAT -> result.add("fr${returns.registerNum}.f")
|
||||
}
|
||||
if(returns.cpuRegister!=null) {
|
||||
val cpuReg =
|
||||
if(returns.cpuRegister.registerOrPair!=null)
|
||||
returns.cpuRegister.registerOrPair.toString()
|
||||
else
|
||||
returns.cpuRegister.statusflag.toString()
|
||||
result.add("@"+cpuReg)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -211,21 +211,6 @@ fun parseIRCodeLine(line: String): Either<IRInstruction, String> {
|
||||
throw IRParseException("labelsymbol confused with register?: $labelSymbol")
|
||||
}
|
||||
|
||||
if(opcode in OpcodesForCpuRegisters) {
|
||||
val reg = operands.last().lowercase()
|
||||
if(reg !in setOf(
|
||||
"a", "x", "y",
|
||||
"ax", "ay", "xy",
|
||||
"r0", "r1", "r2", "r3",
|
||||
"r4", "r5", "r6", "r7",
|
||||
"r8", "r9", "r10","r11",
|
||||
"r12", "r13", "r14", "r15",
|
||||
"pc", "pz", "pv","pn"))
|
||||
throw IRParseException("invalid cpu reg: $reg")
|
||||
|
||||
return left(IRInstruction(opcode, type, reg1, labelSymbol = reg))
|
||||
}
|
||||
|
||||
return left(IRInstruction(opcode, type, reg1, reg2, fpReg1, fpReg2, immediateInt, immediateFp, address, labelSymbol = labelSymbol))
|
||||
}
|
||||
|
||||
|
@ -283,8 +283,6 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
Opcode.BREAKPOINT -> InsBREAKPOINT()
|
||||
Opcode.CLC -> { statusCarry = false; nextPc() }
|
||||
Opcode.SEC -> { statusCarry = true; nextPc() }
|
||||
Opcode.LOADCPU -> InsLOADCPU(ins)
|
||||
Opcode.STORECPU -> InsSTORECPU(ins)
|
||||
|
||||
Opcode.FFROMUB -> InsFFROMUB(ins)
|
||||
Opcode.FFROMSB -> InsFFROMSB(ins)
|
||||
@ -373,81 +371,6 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
throw BreakpointException(pcChunk, pcIndex)
|
||||
}
|
||||
|
||||
private fun InsLOADCPU(i: IRInstruction) {
|
||||
val reg = i.labelSymbol!!
|
||||
val value: UInt
|
||||
if(reg.startsWith('r')) {
|
||||
val regnum = reg.substring(1).toInt()
|
||||
val regAddr = cx16virtualregsBaseAddress + regnum*2
|
||||
value = memory.getUW(regAddr).toUInt()
|
||||
} else {
|
||||
value = when(reg) {
|
||||
"a" -> registers.cpuA.toUInt()
|
||||
"x" -> registers.cpuX.toUInt()
|
||||
"y" -> registers.cpuY.toUInt()
|
||||
"ax" -> (registers.cpuA.toUInt() shl 8) or registers.cpuX.toUInt()
|
||||
"ay" -> (registers.cpuA.toUInt() shl 8) or registers.cpuY.toUInt()
|
||||
"xy" -> (registers.cpuX.toUInt() shl 8) or registers.cpuY.toUInt()
|
||||
"pc" -> if(statusCarry) 1u else 0u
|
||||
"pz" -> if(statusZero) 1u else 0u
|
||||
"pn" -> if(statusNegative) 1u else 0u
|
||||
"pv" -> throw IllegalArgumentException("overflow status register not supported in VM")
|
||||
else -> throw IllegalArgumentException("invalid cpu reg")
|
||||
}
|
||||
}
|
||||
when(i.type!!) {
|
||||
IRDataType.BYTE -> registers.setUB(i.reg1!!, value.toUByte())
|
||||
IRDataType.WORD -> registers.setUW(i.reg1!!, value.toUShort())
|
||||
else -> throw java.lang.IllegalArgumentException("invalid cpu reg type")
|
||||
}
|
||||
nextPc()
|
||||
}
|
||||
|
||||
private fun InsSTORECPU(i: IRInstruction) {
|
||||
val value: UInt = when(i.type!!) {
|
||||
IRDataType.BYTE -> registers.getUB(i.reg1!!).toUInt()
|
||||
IRDataType.WORD -> registers.getUW(i.reg1!!).toUInt()
|
||||
IRDataType.FLOAT -> throw IllegalArgumentException("there are no float cpu registers")
|
||||
}
|
||||
StoreCPU(value, i.type!!, i.labelSymbol!!)
|
||||
nextPc()
|
||||
}
|
||||
|
||||
private fun StoreCPU(value: UInt, dt: IRDataType, regStr: String) {
|
||||
if(regStr.startsWith('r')) {
|
||||
val regnum = regStr.substring(1).toInt()
|
||||
val regAddr = cx16virtualregsBaseAddress + regnum*2
|
||||
when(dt) {
|
||||
IRDataType.BYTE -> memory.setUB(regAddr, value.toUByte())
|
||||
IRDataType.WORD -> memory.setUW(regAddr, value.toUShort())
|
||||
else -> throw IllegalArgumentException("invalid reg dt")
|
||||
}
|
||||
} else {
|
||||
when (regStr) {
|
||||
"a" -> registers.cpuA = value.toUByte()
|
||||
"x" -> registers.cpuX = value.toUByte()
|
||||
"y" -> registers.cpuY = value.toUByte()
|
||||
"ax" -> {
|
||||
registers.cpuA = (value and 255u).toUByte()
|
||||
registers.cpuX = (value shr 8).toUByte()
|
||||
}
|
||||
"ay" -> {
|
||||
registers.cpuA = (value and 255u).toUByte()
|
||||
registers.cpuY = (value shr 8).toUByte()
|
||||
}
|
||||
"xy" -> {
|
||||
registers.cpuX = (value and 255u).toUByte()
|
||||
registers.cpuY = (value shr 8).toUByte()
|
||||
}
|
||||
"pc" -> statusCarry = value == 1u
|
||||
"pz" -> statusZero = value == 1u
|
||||
"pn" -> statusNegative = value == 1u
|
||||
"pv" -> throw IllegalArgumentException("overflow status register not supported in VM")
|
||||
else -> throw IllegalArgumentException("invalid cpu reg")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun InsLOAD(i: IRInstruction) {
|
||||
if(i.type==IRDataType.FLOAT)
|
||||
registers.setFloat(i.fpReg1!!, i.immediateFp!!)
|
||||
|
@ -67,7 +67,7 @@ class VmProgramLoader {
|
||||
|
||||
programChunks.forEach {
|
||||
it.instructions.forEach { ins ->
|
||||
if (ins.labelSymbol != null && ins.opcode !in OpcodesThatBranch && ins.opcode !in OpcodesForCpuRegisters)
|
||||
if (ins.labelSymbol != null && ins.opcode !in OpcodesThatBranch)
|
||||
require(ins.address != null) { "instruction with labelSymbol for a var should have value set to the memory address" }
|
||||
}
|
||||
}
|
||||
@ -152,16 +152,12 @@ class VmProgramLoader {
|
||||
// placeholder is not a variable, so it must be a label of a code chunk instead
|
||||
val target: IRCodeChunk? = chunks.firstOrNull { it.label==label }
|
||||
val opcode = chunk.instructions[line].opcode
|
||||
if(target==null) {
|
||||
// exception allowed: storecpu/loadcpu instructions that refer to CPU registers
|
||||
if(opcode !in OpcodesForCpuRegisters)
|
||||
throw IRParseException("placeholder not found in variables nor labels: $label")
|
||||
}
|
||||
else if(opcode in OpcodesThatBranch) {
|
||||
if(target==null)
|
||||
throw IRParseException("placeholder not found in variables nor labels: $label")
|
||||
else if(opcode in OpcodesThatBranch)
|
||||
chunk.instructions[line] = chunk.instructions[line].copy(branchTarget = target, address = null)
|
||||
} else {
|
||||
else
|
||||
throw IRParseException("vm cannot yet load a label address as a value: ${chunk.instructions[line]}") // TODO
|
||||
}
|
||||
}
|
||||
} else {
|
||||
chunk.instructions[line] = chunk.instructions[line].copy(address = replacement)
|
||||
|
Loading…
x
Reference in New Issue
Block a user