IR: new (sys)call instructions that encapsulate the full subroutine call

to fix the bugs resulting from nesting subroutine calls (as param to another call etc)
This commit is contained in:
Irmen de Jong 2023-05-12 23:26:36 +02:00
parent bff3c4f95c
commit 905921a684
20 changed files with 558 additions and 489 deletions

View File

@ -82,11 +82,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val left = exprGen.translateExpression(call.args[0]) val left = exprGen.translateExpression(call.args[0])
val right = exprGen.translateExpression(call.args[1]) val right = exprGen.translateExpression(call.args[1])
addToResult(result, left, left.resultReg, -1) addToResult(result, left, left.resultReg, -1)
addInstr(result, IRInstruction(Opcode.SETPARAM, IRDataType.WORD, reg1=left.resultReg, immediate = 0), null)
addToResult(result, right, right.resultReg, -1) addToResult(result, right, right.resultReg, -1)
addInstr(result, IRInstruction(Opcode.SETPARAM, IRDataType.WORD, reg1=right.resultReg, immediate = 1), null) result += codeGen.makeSyscall(IMSyscall.COMPARE_STRINGS, listOf(IRDataType.WORD to left.resultReg, IRDataType.WORD to right.resultReg), IRDataType.BYTE to left.resultReg)
addInstr(result, IRInstruction(Opcode.SYSCALL, immediate = IMSyscall.COMPARE_STRINGS.number), null)
addInstr(result, IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=left.resultReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, left.resultReg, -1) return ExpressionCodeResult(result, IRDataType.BYTE, left.resultReg, -1)
} }
@ -118,13 +115,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args[0]) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { val lengthReg = codeGen.registers.nextFree()
it += IRInstruction(Opcode.SETPARAM, IRDataType.WORD, reg1 = tr.resultReg, immediate = 0) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = array.length), null)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = tr.resultReg, immediate = array.length) result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
it += IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, reg1 = tr.resultReg, immediate = 1)
it += IRInstruction(Opcode.SYSCALL, immediate = syscall.number)
it += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=tr.resultReg)
}
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1) return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
} }
@ -143,13 +136,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args[0]) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { val lengthReg = codeGen.registers.nextFree()
it += IRInstruction(Opcode.SETPARAM, IRDataType.WORD, reg1 = tr.resultReg, immediate = 0) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = array.length), null)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = tr.resultReg, immediate = array.length) result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
it += IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, reg1 = tr.resultReg, immediate = 1)
it += IRInstruction(Opcode.SYSCALL, immediate = syscall.number)
it += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=tr.resultReg)
}
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1) return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
} }
@ -271,12 +260,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args[0]) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { val lengthReg = codeGen.registers.nextFree()
it += IRInstruction(Opcode.SETPARAM, IRDataType.WORD, reg1 = tr.resultReg, immediate = 0) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(array.dt==DataType.STR) array.length!!-1 else array.length), null)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = tr.resultReg, immediate = if(array.dt==DataType.STR) array.length!!-1 else array.length) result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null)
it += IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, reg1 = tr.resultReg, immediate = 1)
it += IRInstruction(Opcode.SYSCALL, immediate = syscall.number)
}
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
} }
@ -296,12 +282,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args[0]) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also { val lengthReg = codeGen.registers.nextFree()
it += IRInstruction(Opcode.SETPARAM, IRDataType.WORD, reg1 = tr.resultReg, immediate = 0) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(array.dt==DataType.STR) array.length!!-1 else array.length), null)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = tr.resultReg, immediate = if(array.dt==DataType.STR) array.length!!-1 else array.length) result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null)
it += IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, reg1 = tr.resultReg, immediate = 1)
it += IRInstruction(Opcode.SYSCALL, immediate = syscall.number)
}
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
} }

View File

@ -4,10 +4,7 @@ import prog8.code.StRomSub
import prog8.code.StStaticVariable import prog8.code.StStaticVariable
import prog8.code.StSub import prog8.code.StSub
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.AssemblyError import prog8.code.core.*
import prog8.code.core.DataType
import prog8.code.core.PassByValueDatatypes
import prog8.code.core.SignedDatatypes
import prog8.intermediate.* import prog8.intermediate.*
internal class ExpressionCodeResult(val chunks: IRCodeChunks, val dt: IRDataType, val resultReg: Int, val resultFpReg: Int) { internal class ExpressionCodeResult(val chunks: IRCodeChunks, val dt: IRDataType, val resultReg: Int, val resultFpReg: Int) {
@ -105,53 +102,35 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
private fun translate(check: PtContainmentCheck): ExpressionCodeResult { private fun translate(check: PtContainmentCheck): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
var tr = translateExpression(check.element)
addToResult(result, tr, tr.resultReg, -1)
val iterable = codeGen.symbolTable.flat.getValue(check.iterable.name) as StStaticVariable val iterable = codeGen.symbolTable.flat.getValue(check.iterable.name) as StStaticVariable
when(iterable.dt) { when(iterable.dt) {
DataType.STR -> { DataType.STR -> {
tr = translateExpression(check.element) val elementTr = translateExpression(check.element)
addToResult(result, tr, tr.resultReg, -1) addToResult(result, elementTr, elementTr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, tr.resultReg, immediate = 0), null) val iterableTr = translateExpression(check.iterable)
tr = translateExpression(check.iterable) addToResult(result, iterableTr, iterableTr.resultReg, -1)
addToResult(result, tr, tr.resultReg, -1) result += codeGen.makeSyscall(IMSyscall.STRING_CONTAINS, listOf(IRDataType.BYTE to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg), IRDataType.BYTE to elementTr.resultReg)
addInstr(result, IRInstruction(Opcode.SETPARAM, IRDataType.WORD, tr.resultReg, immediate = 1), null) return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SYSCALL, immediate = IMSyscall.STRING_CONTAINS.number)
it += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=tr.resultReg)
}
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
} }
DataType.ARRAY_UB, DataType.ARRAY_B -> { DataType.ARRAY_UB, DataType.ARRAY_B -> {
tr = translateExpression(check.element) val elementTr = translateExpression(check.element)
addToResult(result, tr, tr.resultReg, -1) addToResult(result, elementTr, elementTr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, tr.resultReg, immediate = 0), null) val iterableTr = translateExpression(check.iterable)
tr = translateExpression(check.iterable) addToResult(result, iterableTr, iterableTr.resultReg, -1)
addToResult(result, tr, tr.resultReg, -1) val lengthReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also { addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterable.length!!), null)
it += IRInstruction(Opcode.SETPARAM, IRDataType.WORD, tr.resultReg, immediate = 1) result += codeGen.makeSyscall(IMSyscall.BYTEARRAY_CONTAINS, listOf(IRDataType.BYTE to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to elementTr.resultReg)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=tr.resultReg, immediate = iterable.length!!) return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
it += IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, tr.resultReg, immediate = 2)
it += IRInstruction(Opcode.SYSCALL, immediate = IMSyscall.BYTEARRAY_CONTAINS.number)
it += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=tr.resultReg)
}
// SysCall call convention: return value in register r0
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
} }
DataType.ARRAY_UW, DataType.ARRAY_W -> { DataType.ARRAY_UW, DataType.ARRAY_W -> {
tr = translateExpression(check.element) val elementTr = translateExpression(check.element)
addToResult(result, tr, tr.resultReg, -1) addToResult(result, elementTr, elementTr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.SETPARAM, IRDataType.WORD, tr.resultReg, immediate = 0), null) val iterableTr = translateExpression(check.iterable)
tr = translateExpression(check.iterable) addToResult(result, iterableTr, iterableTr.resultReg, -1)
addToResult(result, tr, tr.resultReg, -1) val lengthReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also { addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterable.length!!), null)
it += IRInstruction(Opcode.SETPARAM, IRDataType.WORD, tr.resultReg, immediate = 1) result += codeGen.makeSyscall(IMSyscall.WORDARRAY_CONTAINS, listOf(IRDataType.WORD to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to elementTr.resultReg)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=tr.resultReg, immediate = iterable.length!!) return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
it += IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, tr.resultReg, immediate = 2)
it += IRInstruction(Opcode.SYSCALL, immediate = IMSyscall.WORDARRAY_CONTAINS.number)
it += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=tr.resultReg)
}
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
} }
DataType.ARRAY_F -> throw AssemblyError("containment check in float-array not supported") DataType.ARRAY_F -> throw AssemblyError("containment check in float-array not supported")
else -> throw AssemblyError("weird iterable dt ${iterable.dt} for ${check.iterable.name}") else -> throw AssemblyError("weird iterable dt ${iterable.dt} for ${check.iterable.name}")
@ -358,97 +337,94 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
when (val callTarget = codeGen.symbolTable.flat.getValue(fcall.name)) { when (val callTarget = codeGen.symbolTable.flat.getValue(fcall.name)) {
is StSub -> { is StSub -> {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
for ((index, argspec) in fcall.args.zip(callTarget.parameters).withIndex()) { // assign the arguments
val (arg, param) = argspec val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
val paramDt = codeGen.irType(param.type) for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
val paramDt = codeGen.irType(parameter.type)
val tr = translateExpression(arg) val tr = translateExpression(arg)
result += tr.chunks
if(paramDt==IRDataType.FLOAT) if(paramDt==IRDataType.FLOAT)
addInstr(result, IRInstruction(Opcode.SETPARAM, paramDt, fpReg1 = tr.resultFpReg, immediate = index), null) argRegisters.add(FunctionCallArgs.ArgumentSpec(parameter.name, null, FunctionCallArgs.RegSpec(IRDataType.FLOAT, tr.resultFpReg, null)))
else else
addInstr(result, IRInstruction(Opcode.SETPARAM, paramDt, reg1 = tr.resultReg, immediate = index), null) argRegisters.add(FunctionCallArgs.ArgumentSpec(parameter.name, null, FunctionCallArgs.RegSpec(paramDt, tr.resultReg, null)))
result += tr.chunks
} }
// for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) { // return value
// val paramDt = codeGen.irType(parameter.type) val returnRegSpec = if(fcall.void) null else {
// val symbol = "${fcall.name}.${parameter.name}" val returnIrType = codeGen.irType(callTarget.returnType!!)
// if(codeGen.isZero(arg)) { if(returnIrType==IRDataType.FLOAT)
// addInstr(result, IRInstruction(Opcode.STOREZM, paramDt, labelSymbol = symbol), null) FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFreeFloat(), null)
// } else { else
// if (paramDt == IRDataType.FLOAT) { FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFree(), null)
// val tr = translateExpression(arg) }
// addToResult(result, tr, -1, tr.resultFpReg) // create the call
// addInstr(result, IRInstruction(Opcode.STOREM, paramDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol), null) val call = IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec))
// } else { addInstr(result, call, null)
// val tr = translateExpression(arg) return if(fcall.void)
// addToResult(result, tr, tr.resultReg, -1)
// addInstr(result, IRInstruction(Opcode.STOREM, paramDt, reg1 = tr.resultReg, labelSymbol = symbol), null)
// }
// }
// }
return if(fcall.void) {
addInstr(result, IRInstruction(Opcode.CALL, labelSymbol = fcall.name), null)
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
} else { else if(call.fcallArgs!!.returns!!.dt==IRDataType.FLOAT)
var resultReg = -1 ExpressionCodeResult(result, codeGen.irType(fcall.type), -1, call.fcallArgs!!.returns!!.registerNum)
var resultFpReg = -1 else
if(fcall.type==DataType.FLOAT) { ExpressionCodeResult(result, codeGen.irType(fcall.type), call.fcallArgs!!.returns!!.registerNum, -1)
resultFpReg = codeGen.registers.nextFreeFloat()
addInstr(result, IRInstruction(Opcode.CALLR, IRDataType.FLOAT, fpReg1=resultFpReg, labelSymbol=fcall.name), null)
} else {
resultReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.CALLR, codeGen.irType(fcall.type), reg1=resultReg, labelSymbol=fcall.name), null)
}
ExpressionCodeResult(result, codeGen.irType(fcall.type), resultReg, resultFpReg)
}
} }
is StRomSub -> { is StRomSub -> {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
// assign the arguments
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) { for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
val paramDt = codeGen.irType(parameter.type) val paramDt = codeGen.irType(parameter.type)
val paramRegStr = if(parameter.register.registerOrPair!=null) parameter.register.registerOrPair.toString() else parameter.register.statusflag.toString() val tr = translateExpression(arg)
if(codeGen.isZero(arg)) { if(paramDt==IRDataType.FLOAT)
addInstr(result, IRInstruction(Opcode.STOREZCPU, paramDt, labelSymbol = paramRegStr), null) argRegisters.add(FunctionCallArgs.ArgumentSpec("", null, FunctionCallArgs.RegSpec(IRDataType.FLOAT, tr.resultFpReg, parameter.register)))
else
argRegisters.add(FunctionCallArgs.ArgumentSpec("", null, FunctionCallArgs.RegSpec(paramDt, tr.resultReg, parameter.register)))
result += tr.chunks
}
// return value
val returnRegSpec = if(fcall.void) null else {
if(callTarget.returns.isEmpty())
null
else if(callTarget.returns.size==1) {
val returns = callTarget.returns[0]
val returnIrType = codeGen.irType(returns.type)
if(returnIrType==IRDataType.FLOAT)
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFreeFloat(), returns.register)
else
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFree(), returns.register)
} else { } else {
if (paramDt == IRDataType.FLOAT) // multiple return values: take the first *register* (not status flag) return value and ignore the rest.
throw AssemblyError("doesn't support float register argument in asm romsub") val returns = callTarget.returns.first { it.register.registerOrPair!=null }
val tr = translateExpression(arg) val returnIrType = codeGen.irType(returns.type)
addToResult(result, tr, tr.resultReg, -1) if(returnIrType==IRDataType.FLOAT)
addInstr(result, IRInstruction(Opcode.STORECPU, paramDt, reg1 = tr.resultReg, labelSymbol = paramRegStr), null) FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFreeFloat(), returns.register)
else
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFree(), returns.register)
} }
} }
// just a regular call without using Vm register call convention: the value is returned in CPU registers! // create the call
addInstr(result, IRInstruction(Opcode.CALL, address = callTarget.address.toInt()), null) val call = IRInstruction(Opcode.CALL, address = callTarget.address.toInt(), fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec))
val resultReg = codeGen.registers.nextFree() addInstr(result, call, null)
if(!fcall.void) { if(fcall.void) {
when(callTarget.returns.size) { return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
0 -> throw AssemblyError("expect a return value") } else {
1 -> { val regStr = if(callTarget.returns.isEmpty())
if(fcall.type==DataType.FLOAT) throw AssemblyError("expect a return value")
throw AssemblyError("doesn't support float register result in asm romsub") else if(callTarget.returns.size==1) {
val returns = callTarget.returns.single() val returns = callTarget.returns.single()
val regStr = if(returns.register.registerOrPair!=null) returns.register.registerOrPair.toString() else returns.register.statusflag.toString() if(returns.register.registerOrPair!=null) returns.register.registerOrPair.toString() else returns.register.statusflag.toString()
addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultReg, labelSymbol = regStr), null) } else {
} // multiple return values: take the first *register* (not status flag) return value and ignore the rest.
else -> { callTarget.returns.first { it.register.registerOrPair!=null }.toString()
val returnRegister = callTarget.returns.singleOrNull{ it.register.registerOrPair!=null } }
if(returnRegister!=null) { return if(fcall.type==DataType.FLOAT) {
// we skip the other values returned in the status flags. val resultFpReg = codeGen.registers.nextFreeFloat()
val regStr = returnRegister.register.registerOrPair.toString() addInstr(result, IRInstruction(Opcode.LOADCPU, IRDataType.FLOAT, fpReg1 = resultFpReg, labelSymbol = regStr), null)
addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultReg, labelSymbol = regStr), null) ExpressionCodeResult(result, returnRegSpec!!.dt, -1, resultFpReg)
} else { } else {
val firstReturnRegister = callTarget.returns.firstOrNull{ it.register.registerOrPair!=null } val resultReg = codeGen.registers.nextFree()
if(firstReturnRegister!=null) { addInstr(result, IRInstruction(Opcode.LOADCPU, returnRegSpec!!.dt, reg1 = resultReg, labelSymbol = regStr), null)
// we just take the first register return value and ignore the rest. ExpressionCodeResult(result, returnRegSpec.dt, resultReg, -1)
val regStr = firstReturnRegister.register.registerOrPair.toString()
addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultReg, labelSymbol = regStr), null)
} else {
throw AssemblyError("invalid number of return values from call")
}
}
}
} }
} }
return ExpressionCodeResult(result, if(fcall.void) IRDataType.BYTE else codeGen.irType(fcall.type), resultReg, -1)
} }
else -> throw AssemblyError("invalid node type") else -> throw AssemblyError("invalid node type")
} }

View File

@ -1589,4 +1589,14 @@ class IRCodeGen(
irSymbolTable.add(staticVar) irSymbolTable.add(staticVar)
return tempvar return tempvar
} }
fun makeSyscall(syscall: IMSyscall, params: List<Pair<IRDataType, Int>>, returns: Pair<IRDataType, Int>?, label: String?=null): IRCodeChunk {
return IRCodeChunk(label, null).also {
val args = params.map { (dt, reg)->
FunctionCallArgs.ArgumentSpec("", null, FunctionCallArgs.RegSpec(dt, reg, null))
}
val returnSpec = if(returns==null) null else FunctionCallArgs.RegSpec(returns.first, returns.second, null)
it += IRInstruction(Opcode.SYSCALL, immediate = syscall.number, fcallArgs = FunctionCallArgs(args, returnSpec))
}
}
} }

View File

@ -250,14 +250,16 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
} }
// replace call + return --> jump // replace call + return --> jump
if(idx>0 && ins.opcode==Opcode.RETURN) { // This can no longer be done here on the IR level, with the current CALL opcode that encodes the full subroutine call setup.
val previous = chunk.instructions[idx-1] // If machine code is ever generated from this IR, *that* should possibly optimize the JSR + RTS into a JMP.
if(previous.opcode==Opcode.CALL || previous.opcode==Opcode.CALLR) { // if(idx>0 && ins.opcode==Opcode.RETURN) {
chunk.instructions[idx-1] = IRInstruction(Opcode.JUMP, address = previous.address, labelSymbol = previous.labelSymbol, branchTarget = previous.branchTarget) // val previous = chunk.instructions[idx-1]
chunk.instructions.removeAt(idx) // if(previous.opcode==Opcode.CALL) {
changed = true // chunk.instructions[idx-1] = IRInstruction(Opcode.JUMP, address = previous.address, labelSymbol = previous.labelSymbol, branchTarget = previous.branchTarget)
} // chunk.instructions.removeAt(idx)
} // changed = true
// }
// }
} }
return changed return changed
} }

View File

@ -441,7 +441,7 @@ class TestVmCodeGen: FunSpec({
irChunks.size shouldBe 1 irChunks.size shouldBe 1
} }
test("romsub allowed in codegen") { test("romsub allowed in ir-codegen") {
//main { //main {
// romsub $5000 = routine() // romsub $5000 = routine()
// //
@ -452,7 +452,7 @@ class TestVmCodeGen: FunSpec({
val codegen = VmCodeGen() val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY) val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val romsub = PtAsmSub("routine", 0x5000u, emptySet(), emptyList(), emptyList(), false, Position.DUMMY) val romsub = PtAsmSub("routine", 0x5000u, setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY)
block.add(romsub) block.add(romsub)
val sub = PtSub("start", emptyList(), null, Position.DUMMY) val sub = PtSub("start", emptyList(), null, Position.DUMMY)
val call = PtFunctionCall("main.routine", true, DataType.UNDEFINED, Position.DUMMY) val call = PtFunctionCall("main.routine", true, DataType.UNDEFINED, Position.DUMMY)

View File

@ -197,9 +197,7 @@ sub str2uword(str string) -> uword {
; (any non-digit character will terminate the number string that is parsed) ; (any non-digit character will terminate the number string that is parsed)
%ir {{ %ir {{
loadm.w r65535,conv.str2uword.string loadm.w r65535,conv.str2uword.string
setparam.w r65535,0 syscall 11 (r65535.w) : r0.w
syscall 11
pop.w r0
returnr.w r0 returnr.w r0
}} }}
} }
@ -210,9 +208,7 @@ sub str2word(str string) -> word {
; (any non-digit character will terminate the number string that is parsed) ; (any non-digit character will terminate the number string that is parsed)
%ir {{ %ir {{
loadm.w r65535,conv.str2word.string loadm.w r65535,conv.str2word.string
setparam.w r65535,0 syscall 12 (r65535.w) : r0.w
syscall 12
pop.w r0
returnr.w r0 returnr.w r0
}} }}
} }

View File

@ -11,8 +11,7 @@ sub print_f(float value) {
; ---- prints the floating point value (without a newline). ; ---- prints the floating point value (without a newline).
%ir {{ %ir {{
loadm.f fr65535,floats.print_f.value loadm.f fr65535,floats.print_f.value
setparam.f fr65535,0 syscall 25 (fr65535.f)
syscall 25
return return
}} }}
} }
@ -127,8 +126,7 @@ sub ceil(float value) -> float {
sub rndf() -> float { sub rndf() -> float {
%ir {{ %ir {{
syscall 35 syscall 35 () : fr0.f
pop.f fr0
returnr.f fr0 returnr.f fr0
}} }}
} }
@ -136,8 +134,8 @@ sub rndf() -> float {
sub rndseedf(float seed) { sub rndseedf(float seed) {
%ir {{ %ir {{
loadm.f fr65535,floats.rndseedf.seed loadm.f fr65535,floats.rndseedf.seed
setparam.f fr65535,0 syscall 32 (fr65535.f)
syscall 32 return
}} }}
} }

View File

@ -161,16 +161,14 @@ math {
sub rnd() -> ubyte { sub rnd() -> ubyte {
%ir {{ %ir {{
syscall 33 syscall 33 (): r0.b
pop.b r0
returnr.b r0 returnr.b r0
}} }}
} }
sub rndw() -> uword { sub rndw() -> uword {
%ir {{ %ir {{
syscall 34 syscall 34 (): r0.w
pop.w r0
returnr.w r0 returnr.w r0
}} }}
} }
@ -178,11 +176,9 @@ math {
sub rndseed(uword seed1, uword seed2) { sub rndseed(uword seed1, uword seed2) {
; -- reset the pseudo RNG's seed values. Defaults are: $a55a, $7653. ; -- reset the pseudo RNG's seed values. Defaults are: $a55a, $7653.
%ir {{ %ir {{
loadm.w r65535,math.rndseed.seed1 loadm.w r65534,math.rndseed.seed1
setparam.w r65535,0
loadm.w r65535,math.rndseed.seed2 loadm.w r65535,math.rndseed.seed2
setparam.w r65535,1 syscall 31 (r65534.w, r65535.w)
syscall 31
return return
}} }}
} }

View File

@ -84,12 +84,9 @@ string {
; Note that you can also directly compare strings and string values with eachother using ; Note that you can also directly compare strings and string values with eachother using
; comparison operators ==, < etcetera (it will use strcmp for you under water automatically). ; comparison operators ==, < etcetera (it will use strcmp for you under water automatically).
%ir {{ %ir {{
loadm.w r65535,string.compare.st1 loadm.w r65534,string.compare.st1
setparam.w r65535,0
loadm.w r65535,string.compare.st2 loadm.w r65535,string.compare.st2
setparam.w r65535,1 syscall 29 (r65534.w, r65535.w) : r0.b
syscall 29
pop.b r0
returnr.b r0 returnr.b r0
}} }}
} }

View File

@ -8,7 +8,7 @@ sys {
sub reset_system() { sub reset_system() {
; Soft-reset the system back to initial power-on Basic prompt. ; Soft-reset the system back to initial power-on Basic prompt.
%ir {{ %ir {{
syscall 0 syscall 0 ()
}} }}
} }
@ -16,15 +16,14 @@ sys {
; --- wait approximately the given number of jiffies (1/60th seconds) ; --- wait approximately the given number of jiffies (1/60th seconds)
%ir {{ %ir {{
loadm.w r65535,sys.wait.jiffies loadm.w r65535,sys.wait.jiffies
setparam.w r65535,0 syscall 13 (r65535.w)
syscall 13
}} }}
} }
sub waitvsync() { sub waitvsync() {
; --- busy wait till the next vsync has occurred (approximately), without depending on custom irq handling. ; --- busy wait till the next vsync has occurred (approximately), without depending on custom irq handling.
%ir {{ %ir {{
syscall 14 syscall 14()
}} }}
} }
@ -64,8 +63,7 @@ sys {
; -- immediately exit the program with a return code in the A register ; -- immediately exit the program with a return code in the A register
%ir {{ %ir {{
loadm.b r65535,sys.exit.returnvalue loadm.b r65535,sys.exit.returnvalue
setparam.b r65535,0 syscall 1 (r65535.b)
syscall 1
}} }}
} }
@ -85,39 +83,31 @@ sys {
sub gfx_enable(ubyte mode) { sub gfx_enable(ubyte mode) {
%ir {{ %ir {{
loadm.b r65535,sys.gfx_enable.mode loadm.b r65535,sys.gfx_enable.mode
setparam.b r65535,0 syscall 8 (r65535.b)
syscall 8
}} }}
} }
sub gfx_clear(ubyte color) { sub gfx_clear(ubyte color) {
%ir {{ %ir {{
loadm.b r65535,sys.gfx_clear.color loadm.b r65535,sys.gfx_clear.color
setparam.b r65535,0 syscall 9 (r65535.b)
syscall 9
}} }}
} }
sub gfx_plot(uword xx, uword yy, ubyte color) { sub gfx_plot(uword xx, uword yy, ubyte color) {
%ir {{ %ir {{
loadm.w r65535,sys.gfx_plot.xx loadm.w r65533,sys.gfx_plot.xx
setparam.w r65535,0 loadm.w r65534,sys.gfx_plot.yy
loadm.w r65535,sys.gfx_plot.yy
setparam.w r65535,1
loadm.b r65535,sys.gfx_plot.color loadm.b r65535,sys.gfx_plot.color
setparam.b r65535,2 syscall 10 (r65533.w, r65534.w, r65535.b)
syscall 10
}} }}
} }
sub gfx_getpixel(uword xx, uword yy) -> ubyte { sub gfx_getpixel(uword xx, uword yy) -> ubyte {
%ir {{ %ir {{
loadm.w r65535,sys.gfx_getpixel.xx loadm.w r65534,sys.gfx_getpixel.xx
setparam.w r65535,0
loadm.w r65535,sys.gfx_getpixel.yy loadm.w r65535,sys.gfx_getpixel.yy
setparam.w r65535,1 syscall 30 (r65534.w, r65535.w): r0.b
syscall 30
pop.b r0
returnr.b r0 returnr.b r0
}} }}
} }

View File

@ -16,8 +16,7 @@ sub clear_screen() {
str @shared sequence = "\x1b[2J\x1B[H" str @shared sequence = "\x1b[2J\x1B[H"
%ir {{ %ir {{
load.w r65535,txt.clear_screen.sequence load.w r65535,txt.clear_screen.sequence
setparam.w r65535,0 syscall 3 (r65535.w)
syscall 3
}} }}
} }
@ -40,16 +39,14 @@ sub uppercase() {
sub chrout(ubyte char) { sub chrout(ubyte char) {
%ir {{ %ir {{
loadm.b r65535,txt.chrout.char loadm.b r65535,txt.chrout.char
setparam.b r65535,0 syscall 2 (r65535.b)
syscall 2
}} }}
} }
sub print (str text) { sub print (str text) {
%ir {{ %ir {{
loadm.w r65535,txt.print.text loadm.w r65535,txt.print.text
setparam.w r65535,0 syscall 3 (r65535.w)
syscall 3
}} }}
} }
@ -125,12 +122,9 @@ sub input_chars (uword buffer) -> ubyte {
; ---- Input a string (max. 80 chars) from the keyboard. Returns length of input. (string is terminated with a 0 byte as well) ; ---- Input a string (max. 80 chars) from the keyboard. Returns length of input. (string is terminated with a 0 byte as well)
; It assumes the keyboard is selected as I/O channel! ; It assumes the keyboard is selected as I/O channel!
%ir {{ %ir {{
loadm.w r65535,txt.input_chars.buffer loadm.w r65534,txt.input_chars.buffer
setparam.w r65535,0
load.b r65535,80 load.b r65535,80
setparam.b r65535,1 syscall 6 (r65534.w, r65535.b): r0.b
syscall 6
pop.b r0
returnr.b r0 returnr.b r0
}} }}
} }

View File

@ -4,7 +4,6 @@ TODO
For next minor release For next minor release
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
- fix VM problem with param passing: void string.copy(".prg", &output_filename + string.length(output_filename)) - fix VM problem with param passing: void string.copy(".prg", &output_filename + string.length(output_filename))
some work on this is on a git shelf
... ...

View File

@ -34,7 +34,7 @@ main {
irq { irq {
const ubyte BAR_Y_OFFSET = 6 const ubyte BAR_Y_OFFSET = 5
uword next_irq_line = 0 uword next_irq_line = 0
ubyte anim1 = 0 ubyte anim1 = 0
ubyte av1 = 0 ubyte av1 = 0

View File

@ -5,11 +5,14 @@
main { main {
sub start() { sub start() {
uword seconds_uword = 1 txt.chrout('!')
uword remainder = seconds_uword % $0003 ==0 txt.print("test")
txt.print_uw(remainder) txt.nl()
; uword seconds_uword = 1
blerp() ; uword remainder = seconds_uword % $0003 ==0
; txt.print_uw(remainder)
;
; blerp()
} }
sub blerp() { sub blerp() {

View File

@ -1,5 +1,7 @@
package prog8.intermediate package prog8.intermediate
import prog8.code.core.CpuRegister
import prog8.code.core.RegisterOrStatusflag
import prog8.code.core.toHex import prog8.code.core.toHex
/* /*
@ -54,14 +56,18 @@ CONTROL FLOW
------------ ------------
jump location - continue running at instruction number given by location jump location - continue running at instruction number given by location
jumpa address - continue running at memory address (note: only used to encode a physical cpu jump to fixed address instruction) jumpa address - continue running at memory address (note: only used to encode a physical cpu jump to fixed address instruction)
setparam reg1, argpos - sets reg1 as the value for the parameter in the given position for an upcoming function call (call, callr, syscall, or even jump opcode). call label(argument register list) [: resultreg.type]
call location - save current instruction location+1, continue execution at instruction nr given by location. No return value is expected. - calls a subroutine with the given arguments and return value (optional).
callr reg1, location - like call but expects the routine to return a value with a returnr instruction, it then puts that in reg1 save current instruction location+1, continue execution at instruction nr of the label.
syscall value - do a systemcall identified by call number, result value(s) are pushed on value stack so need to be POPped off (depends on syscall) the argument register list is positional and includes the datatype, ex.: r4.b,r5.w,fp1.f
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.
return - restore last saved instruction location and continue at that instruction. No return value. return - restore last saved instruction location and continue at that instruction. No return value.
returnr reg1 - like return, but also returns the value in reg1 to the caller returnr reg1 - like return, but also returns the value in reg1 to the caller
BRANCHING and CONDITIONALS BRANCHING and CONDITIONALS
-------------------------- --------------------------
All have type b or w except the branches that only check status bits. All have type b or w except the branches that only check status bits.
@ -232,15 +238,12 @@ enum class Opcode {
STOREX, STOREX,
STOREIX, STOREIX,
STOREZM, STOREZM,
STOREZCPU,
STOREZI, STOREZI,
STOREZX, STOREZX,
JUMP, JUMP,
JUMPA, JUMPA,
SETPARAM,
CALL, CALL,
CALLR,
SYSCALL, SYSCALL,
RETURN, RETURN,
RETURNR, RETURNR,
@ -388,7 +391,6 @@ val OpcodesThatBranch = setOf(
Opcode.RETURN, Opcode.RETURN,
Opcode.RETURNR, Opcode.RETURNR,
Opcode.CALL, Opcode.CALL,
Opcode.CALLR,
Opcode.SYSCALL, Opcode.SYSCALL,
Opcode.BSTCC, Opcode.BSTCC,
Opcode.BSTCS, Opcode.BSTCS,
@ -418,8 +420,7 @@ val OpcodesThatBranch = setOf(
val OpcodesForCpuRegisters = setOf( val OpcodesForCpuRegisters = setOf(
Opcode.LOADCPU, Opcode.LOADCPU,
Opcode.STORECPU, Opcode.STORECPU
Opcode.STOREZCPU
) )
enum class IRDataType { enum class IRDataType {
@ -442,7 +443,9 @@ data class InstructionFormat(val datatype: IRDataType?,
val fpReg1: OperandDirection, val fpReg1: OperandDirection,
val fpReg2: OperandDirection, val fpReg2: OperandDirection,
val address: OperandDirection, val address: OperandDirection,
val immediate: Boolean) { val immediate: Boolean,
val funcCall: Boolean,
val sysCall: Boolean) {
companion object { companion object {
fun from(spec: String): Map<IRDataType?, InstructionFormat> { fun from(spec: String): Map<IRDataType?, InstructionFormat> {
val result = mutableMapOf<IRDataType?, InstructionFormat>() val result = mutableMapOf<IRDataType?, InstructionFormat>()
@ -455,33 +458,37 @@ data class InstructionFormat(val datatype: IRDataType?,
var immediate = false var immediate = false
val splits = part.splitToSequence(',').iterator() val splits = part.splitToSequence(',').iterator()
val typespec = splits.next() val typespec = splits.next()
var funcCall = false
var sysCall = false
while(splits.hasNext()) { while(splits.hasNext()) {
when(splits.next()) { when(splits.next()) {
"<r1" -> { reg1=OperandDirection.READ } "<r1" -> reg1 = OperandDirection.READ
">r1" -> { reg1=OperandDirection.WRITE } ">r1" -> reg1 = OperandDirection.WRITE
"<>r1" -> { reg1=OperandDirection.READWRITE } "<>r1" -> reg1 = OperandDirection.READWRITE
"<r2" -> reg2 = OperandDirection.READ "<r2" -> reg2 = OperandDirection.READ
"<fr1" -> { fpreg1=OperandDirection.READ } "<fr1" -> fpreg1 = OperandDirection.READ
">fr1" -> { fpreg1=OperandDirection.WRITE } ">fr1" -> fpreg1 = OperandDirection.WRITE
"<>fr1" -> { fpreg1=OperandDirection.READWRITE } "<>fr1" -> fpreg1 = OperandDirection.READWRITE
"<fr2" -> fpreg2 = OperandDirection.READ "<fr2" -> fpreg2 = OperandDirection.READ
">i", "<>i" -> throw IllegalArgumentException("can't write into an immediate value") ">i", "<>i" -> throw IllegalArgumentException("can't write into an immediate value")
"<i" -> immediate = true "<i" -> immediate = true
"<a" -> address = OperandDirection.READ "<a" -> address = OperandDirection.READ
">a" -> address = OperandDirection.WRITE ">a" -> address = OperandDirection.WRITE
"<>a" -> address = OperandDirection.READWRITE "<>a" -> address = OperandDirection.READWRITE
"call" -> funcCall = true
"syscall" -> sysCall = true
else -> throw IllegalArgumentException(spec) else -> throw IllegalArgumentException(spec)
} }
} }
if(typespec=="N") if(typespec=="N")
result[null] = InstructionFormat(null, reg1, reg2, fpreg1, fpreg2, address, immediate) result[null] = InstructionFormat(null, reg1, reg2, fpreg1, fpreg2, address, immediate, funcCall, sysCall)
if('B' in typespec) if('B' in typespec)
result[IRDataType.BYTE] = InstructionFormat(IRDataType.BYTE, reg1, reg2, fpreg1, fpreg2, address, immediate) result[IRDataType.BYTE] = InstructionFormat(IRDataType.BYTE, reg1, reg2, fpreg1, fpreg2, address, immediate, funcCall, sysCall)
if('W' in typespec) if('W' in typespec)
result[IRDataType.WORD] = InstructionFormat(IRDataType.WORD, reg1, reg2, fpreg1, fpreg2, address, immediate) result[IRDataType.WORD] = InstructionFormat(IRDataType.WORD, reg1, reg2, fpreg1, fpreg2, address, immediate, funcCall, sysCall)
if('F' in typespec) if('F' in typespec)
result[IRDataType.FLOAT] = InstructionFormat(IRDataType.FLOAT, reg1, reg2, fpreg1, fpreg2, address, immediate) result[IRDataType.FLOAT] = InstructionFormat(IRDataType.FLOAT, reg1, reg2, fpreg1, fpreg2, address, immediate, funcCall, sysCall)
} }
return result return result
} }
@ -513,15 +520,12 @@ val instructionFormats = mutableMapOf(
Opcode.STOREX to InstructionFormat.from("BW,<r1,<r2,>a | F,<fr1,<r1,>a"), 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"), Opcode.STOREIX to InstructionFormat.from("BW,<r1,<r2,>a | F,<fr1,<r1,>a"),
Opcode.STOREZM to InstructionFormat.from("BW,>a | F,>a"), Opcode.STOREZM to InstructionFormat.from("BW,>a | F,>a"),
Opcode.STOREZCPU to InstructionFormat.from("BW"),
Opcode.STOREZI to InstructionFormat.from("BW,<r1 | F,<r1"), Opcode.STOREZI to InstructionFormat.from("BW,<r1 | F,<r1"),
Opcode.STOREZX to InstructionFormat.from("BW,<r1,>a | F,<r1,>a"), Opcode.STOREZX to InstructionFormat.from("BW,<r1,>a | F,<r1,>a"),
Opcode.JUMP to InstructionFormat.from("N,<a"), Opcode.JUMP to InstructionFormat.from("N,<a"),
Opcode.JUMPA to InstructionFormat.from("N,<a"), Opcode.JUMPA to InstructionFormat.from("N,<a"),
Opcode.SETPARAM to InstructionFormat.from("BW,<r1,<i | F,<fr1,<i"), Opcode.CALL to InstructionFormat.from("N,call"),
Opcode.CALL to InstructionFormat.from("N,<a"), Opcode.SYSCALL to InstructionFormat.from("N,syscall"),
Opcode.CALLR to InstructionFormat.from("BW,>r1,<a | F,>fr1,<a"),
Opcode.SYSCALL to InstructionFormat.from("N,<i"),
Opcode.RETURN to InstructionFormat.from("N"), Opcode.RETURN to InstructionFormat.from("N"),
Opcode.RETURNR to InstructionFormat.from("BW,>r1 | F,>fr1"), Opcode.RETURNR to InstructionFormat.from("BW,>r1 | F,>fr1"),
Opcode.BSTCC to InstructionFormat.from("N,<a"), Opcode.BSTCC to InstructionFormat.from("N,<a"),
@ -653,6 +657,14 @@ val instructionFormats = mutableMapOf(
) )
class FunctionCallArgs(
var arguments: List<ArgumentSpec>,
val returns: RegSpec?
) {
class RegSpec(val dt: IRDataType, val registerNum: Int, val cpuRegister: RegisterOrStatusflag?)
class ArgumentSpec(val name: String, val address: Int?, val reg: RegSpec)
}
data class IRInstruction( data class IRInstruction(
val opcode: Opcode, val opcode: Opcode,
val type: IRDataType?=null, val type: IRDataType?=null,
@ -664,7 +676,8 @@ data class IRInstruction(
val immediateFp: Float?=null, val immediateFp: Float?=null,
val address: Int?=null, // 0-$ffff val address: Int?=null, // 0-$ffff
val labelSymbol: String?=null, // symbolic label name as alternative to address (so only for Branch/jump/call Instructions!) val labelSymbol: String?=null, // symbolic label name as alternative to address (so only for Branch/jump/call Instructions!)
var branchTarget: IRCodeChunkBase? = null // Will be linked after loading in IRProgram.linkChunks()! This is the chunk that the branch labelSymbol points to. var branchTarget: IRCodeChunkBase? = null, // Will be linked after loading in IRProgram.linkChunks()! This is the chunk that the branch labelSymbol points to.
val fcallArgs: FunctionCallArgs? = null // will be set for the CALL and SYSCALL instructions.
) { ) {
// reg1 and fpreg1 can be IN/OUT/INOUT (all others are readonly INPUT) // 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. // This knowledge is useful in IL assembly optimizers to see how registers are used.
@ -694,13 +707,10 @@ data class IRInstruction(
if(format.fpReg1==OperandDirection.UNUSED) require(fpReg1==null) { "invalid fpReg1" } if(format.fpReg1==OperandDirection.UNUSED) require(fpReg1==null) { "invalid fpReg1" }
if(format.fpReg2==OperandDirection.UNUSED) require(fpReg2==null) { "invalid fpReg2" } if(format.fpReg2==OperandDirection.UNUSED) require(fpReg2==null) { "invalid fpReg2" }
if(format.immediate) { if(format.immediate) {
if(type==IRDataType.FLOAT) { if(type==IRDataType.FLOAT)
if(opcode!=Opcode.SETPARAM) require(immediateFp !=null) {"missing immediate fp value"}
require(immediateFp !=null) {"missing immediate fp value"} else
else require(immediate!=null || labelSymbol!=null) {"missing immediate value or labelsymbol"}
require(immediateFp==null) {"setparam never has immediateFp only immediate"}
}
else require(immediate!=null || labelSymbol!=null) {"missing immediate value or labelsymbol"}
} }
if(type!=IRDataType.FLOAT) if(type!=IRDataType.FLOAT)
require(fpReg1==null && fpReg2==null) {"int instruction can't use fp reg"} require(fpReg1==null && fpReg2==null) {"int instruction can't use fp reg"}
@ -811,36 +821,70 @@ data class IRInstruction(
IRDataType.FLOAT -> result.add(".f ") IRDataType.FLOAT -> result.add(".f ")
else -> result.add(" ") else -> result.add(" ")
} }
reg1?.let {
result.add("r$it") if(this.fcallArgs!=null) {
result.add(",") immediate?.let { result.add(it.toHex()) }
} labelSymbol?.let { result.add(it) }
reg2?.let { result.add("(")
result.add("r$it") fcallArgs.arguments.forEach {
result.add(",") val location = if(it.address==null) {
} if(it.name.isBlank()) "" else it.name+"="
fpReg1?.let { } else "${it.address}="
result.add("fr$it")
result.add(",") if(it.reg.cpuRegister!=null)
} TODO("handle cpuregister ${it.reg.cpuRegister}")
fpReg2?.let {
result.add("fr$it") when(it.reg.dt) {
result.add(",") IRDataType.BYTE -> result.add("${location}r${it.reg.registerNum}.b,")
} IRDataType.WORD -> result.add("${location}r${it.reg.registerNum}.w,")
immediate?.let { IRDataType.FLOAT -> result.add("${location}fr${it.reg.registerNum}.f,")
result.add(it.toHex()) }
result.add(",") }
} if(result.last().endsWith(',')) {
immediateFp?.let { result.add(result.removeLast().trimEnd(','))
result.add(it.toString()) }
result.add(",") result.add(")")
} fcallArgs.returns?.let {
address?.let { result.add(":")
result.add(it.toHex()) when(it.dt) {
result.add(",") IRDataType.BYTE -> result.add("r${it.registerNum}.b")
} IRDataType.WORD -> result.add("r${it.registerNum}.w")
labelSymbol?.let { IRDataType.FLOAT -> result.add("fr${it.registerNum}.f")
result.add(it) }
}
} else {
reg1?.let {
result.add("r$it")
result.add(",")
}
reg2?.let {
result.add("r$it")
result.add(",")
}
fpReg1?.let {
result.add("fr$it")
result.add(",")
}
fpReg2?.let {
result.add("fr$it")
result.add(",")
}
immediate?.let {
result.add(it.toHex())
result.add(",")
}
immediateFp?.let {
result.add(it.toString())
result.add(",")
}
address?.let {
result.add(it.toHex())
result.add(",")
}
labelSymbol?.let {
result.add(it)
}
} }
if(result.last() == ",") if(result.last() == ",")
result.removeLast() result.removeLast()

View File

@ -122,39 +122,47 @@ fun parseIRCodeLine(line: String): Either<IRInstruction, String> {
parseIRValue(operand) parseIRValue(operand)
} }
} }
if(format.sysCall) {
operands.forEach { oper -> val call = parseCall(rest)
if(oper[0] == '&') val syscallNum = parseIRValue(call.target).toInt()
throw IRParseException("address-of should be done with normal LOAD <symbol>") return left(IRInstruction(Opcode.SYSCALL, immediate = syscallNum, fcallArgs = FunctionCallArgs(call.args, call.returns)))
else if(oper[0] in "rR") { } else if (format.funcCall) {
if(reg1==null) reg1 = oper.substring(1).toInt() val call = parseCall(rest)
else if(reg2==null) reg2 = oper.substring(1).toInt() return left(IRInstruction(Opcode.CALL, labelSymbol = call.target, fcallArgs = FunctionCallArgs(call.args, call.returns)))
else throw IRParseException("too many register operands") } else {
} else if (oper[0] in "fF" && oper[1] in "rR") { operands.forEach { oper ->
if(fpReg1==null) fpReg1 = oper.substring(2).toInt() if (oper[0] == '&')
else if(fpReg2==null) fpReg2 = oper.substring(2).toInt() throw IRParseException("address-of should be done with normal LOAD <symbol>")
else throw IRParseException("too many fp register operands") else if (oper[0] in "rR") {
} else if (oper[0].isDigit() || oper[0] == '$' || oper[0]=='%' || oper[0]=='-' || oper.startsWith("0x")) { if (reg1 == null) reg1 = oper.substring(1).toInt()
val value = parseIRValue(oper) else if (reg2 == null) reg2 = oper.substring(1).toInt()
if(format.immediate) { else throw IRParseException("too many register operands")
if(immediateInt==null && immediateFp==null) { } else if (oper[0] in "fF" && oper[1] in "rR") {
if (type == IRDataType.FLOAT) if (fpReg1 == null) fpReg1 = oper.substring(2).toInt()
immediateFp = value else if (fpReg2 == null) fpReg2 = oper.substring(2).toInt()
else else throw IRParseException("too many fp register operands")
immediateInt = value.toInt() } else if (oper[0].isDigit() || oper[0] == '$' || oper[0] == '%' || oper[0] == '-' || oper.startsWith("0x")) {
val value = parseIRValue(oper)
if (format.immediate) {
if (immediateInt == null && immediateFp == null) {
if (type == IRDataType.FLOAT)
immediateFp = value
else
immediateInt = value.toInt()
} else {
address = value.toInt()
}
} else { } else {
address = value.toInt() address = value.toInt()
} }
} else { } else {
address = value.toInt() if (!oper[0].isLetter())
throw IRParseException("expected symbol name: $oper")
labelSymbol = oper
val value = parseValueOrPlaceholder(oper)
if (value != null)
address = value.toInt()
} }
} else {
if(!oper[0].isLetter())
throw IRParseException("expected symbol name: $oper")
labelSymbol = oper
val value = parseValueOrPlaceholder(oper)
if(value!=null)
address = value.toInt()
} }
} }
@ -194,10 +202,6 @@ fun parseIRCodeLine(line: String): Either<IRInstruction, String> {
null -> {} null -> {}
} }
} }
if(format.immediate && opcode==Opcode.SETPARAM && immediateInt==null) {
immediateInt = immediateFp!!.toInt()
immediateFp = null
}
if(format.address!=OperandDirection.UNUSED && address==null && labelSymbol==null) if(format.address!=OperandDirection.UNUSED && address==null && labelSymbol==null)
throw IRParseException("requires address or symbol for $line") throw IRParseException("requires address or symbol for $line")
@ -224,3 +228,52 @@ fun parseIRCodeLine(line: String): Either<IRInstruction, String> {
return left(IRInstruction(opcode, type, reg1, reg2, fpReg1, fpReg2, immediateInt, immediateFp, address, labelSymbol = labelSymbol)) return left(IRInstruction(opcode, type, reg1, reg2, fpReg1, fpReg2, immediateInt, immediateFp, address, labelSymbol = labelSymbol))
} }
private class ParsedCall(
val target: String,
val args: List<FunctionCallArgs.ArgumentSpec>,
val returns: FunctionCallArgs.RegSpec?
)
private fun parseCall(rest: String): ParsedCall {
fun parseRegspec(reg: String): FunctionCallArgs.RegSpec {
val pattern = Regex("f?r([0-9]+)\\.(.)")
val match = pattern.matchEntire(reg) ?: throw IRParseException("invalid regspec $reg")
val num = match.groups[1]!!.value.toInt()
val type = when(match.groups[2]!!.value) {
"b" -> IRDataType.BYTE
"w" -> IRDataType.WORD
"f" -> IRDataType.FLOAT
else -> throw IRParseException("invalid type spec in $reg")
}
return FunctionCallArgs.RegSpec(type, num, null) // TODO parse cpu register
}
fun parseArgs(args: String): List<FunctionCallArgs.ArgumentSpec> {
if(args.isBlank())
return emptyList()
return args.split(',').map {
if(it.contains('=')) {
val (argVar, argReg) = it.split('=')
FunctionCallArgs.ArgumentSpec(argVar, null, parseRegspec(argReg)) // address will be set later
} else {
FunctionCallArgs.ArgumentSpec("", null, parseRegspec(it)) // address will be set later
}
}
}
val pattern = Regex("(?<target>.+?)\\((?<arglist>.*?)\\)(:(?<returns>.+?))?")
val match = pattern.matchEntire(rest.replace(" ","")) ?: throw IRParseException("invalid call spec $rest")
val target = match.groups["target"]!!.value
val args = match.groups["arglist"]!!.value
val arguments = parseArgs(args)
val returns = match.groups["returns"]?.value
return ParsedCall(
target,
arguments,
if(returns==null) null else parseRegspec(returns)
)
}

View File

@ -92,8 +92,6 @@ uword sys.wait.jiffies
</PARAMS> </PARAMS>
<INLINEASM LABEL="sys.wait" IR="true" POS="[library:/prog8lib/virtual/syslib.p8: line 17 col 10-13]"> <INLINEASM LABEL="sys.wait" IR="true" POS="[library:/prog8lib/virtual/syslib.p8: line 17 col 10-13]">
loadm.w r0,sys.wait.jiffies loadm.w r0,sys.wait.jiffies
setparam.w r0,0
syscall 13
</INLINEASM> </INLINEASM>
<CODE> <CODE>
return return

View File

@ -1,6 +1,8 @@
package prog8.vm package prog8.vm
import prog8.code.core.AssemblyError import prog8.code.core.AssemblyError
import prog8.intermediate.FunctionCallArgs
import prog8.intermediate.IRDataType
import kotlin.math.min import kotlin.math.min
/* /*
@ -89,21 +91,50 @@ enum class Syscall {
} }
object SysCalls { object SysCalls {
fun call(call: Syscall, vm: VirtualMachine) { private fun getArgValues(argspec: List<FunctionCallArgs.ArgumentSpec>, vm: VirtualMachine): List<Comparable<Nothing>> {
return argspec.map {
when(it.reg.dt) {
IRDataType.BYTE -> vm.registers.getUB(it.reg.registerNum)
IRDataType.WORD -> vm.registers.getUW(it.reg.registerNum)
IRDataType.FLOAT -> vm.registers.getFloat(it.reg.registerNum)
}
}
}
private fun returnValue(returns: FunctionCallArgs.RegSpec, value: Comparable<Nothing>, vm: VirtualMachine) {
val vv: Float = when(value) {
is UByte -> value.toFloat()
is UShort -> value.toFloat()
is UInt -> value.toFloat()
is Byte -> value.toFloat()
is Short -> value.toFloat()
is Int -> value.toFloat()
is Float -> value
else -> (value as Number).toFloat()
}
when(returns.dt) {
IRDataType.BYTE -> vm.registers.setUB(returns.registerNum, vv.toInt().toUByte())
IRDataType.WORD -> vm.registers.setUW(returns.registerNum, vv.toInt().toUShort())
IRDataType.FLOAT -> vm.registers.setFloat(returns.registerNum, vv)
}
}
fun call(call: Syscall, callspec: FunctionCallArgs, vm: VirtualMachine) {
when(call) { when(call) {
Syscall.RESET -> { Syscall.RESET -> {
vm.reset(false) vm.reset(false)
} }
Syscall.EXIT ->{ Syscall.EXIT ->{
vm.exit(vm.valueStack.pop().toInt()) val exitValue = getArgValues(callspec.arguments, vm).single() as UByte
vm.exit(exitValue.toInt())
} }
Syscall.PRINT_C -> { Syscall.PRINT_C -> {
val char = vm.valueStack.pop().toInt() val char = getArgValues(callspec.arguments, vm).single() as UByte
print(Char(char)) print(Char(char.toInt()))
} }
Syscall.PRINT_S -> { Syscall.PRINT_S -> {
var addr = vm.valueStack.popw().toInt() var addr = (getArgValues(callspec.arguments, vm).single() as UShort).toInt()
while(true) { while(true) {
val char = vm.memory.getUB(addr).toInt() val char = vm.memory.getUB(addr).toInt()
if(char==0) if(char==0)
@ -113,35 +144,52 @@ object SysCalls {
} }
} }
Syscall.PRINT_U8 -> { Syscall.PRINT_U8 -> {
print(vm.valueStack.pop()) val value = getArgValues(callspec.arguments, vm).single()
print(value)
} }
Syscall.PRINT_U16 -> { Syscall.PRINT_U16 -> {
print(vm.valueStack.popw()) val value = getArgValues(callspec.arguments, vm).single()
print(value)
} }
Syscall.INPUT -> { Syscall.INPUT -> {
val (address, maxlen) = getArgValues(callspec.arguments, vm)
var input = readln() var input = readln()
val maxlen = vm.valueStack.pop().toInt() val maxlenvalue = (maxlen as UByte).toInt()
if(maxlen>0) if(maxlenvalue>0)
input = input.substring(0, min(input.length, maxlen)) input = input.substring(0, min(input.length, maxlenvalue))
vm.memory.setString(vm.valueStack.popw().toInt(), input, true) vm.memory.setString((address as UShort).toInt(), input, true)
vm.valueStack.push(input.length.toUByte()) returnValue(callspec.returns!!, input.length, vm)
} }
Syscall.SLEEP -> { Syscall.SLEEP -> {
val duration = vm.valueStack.popw().toLong() val duration = getArgValues(callspec.arguments, vm).single() as UShort
Thread.sleep(duration) Thread.sleep(duration.toLong())
}
Syscall.GFX_ENABLE -> {
val mode = getArgValues(callspec.arguments, vm).single() as UByte
vm.gfx_enable(mode)
}
Syscall.GFX_CLEAR -> {
val color = getArgValues(callspec.arguments, vm).single() as UByte
vm.gfx_clear(color)
}
Syscall.GFX_PLOT -> {
val (x,y,color) = getArgValues(callspec.arguments, vm)
vm.gfx_plot(x as UShort, y as UShort, color as UByte)
}
Syscall.GFX_GETPIXEL -> {
val (x,y) = getArgValues(callspec.arguments, vm)
val color = vm.gfx_getpixel(x as UShort, y as UShort)
returnValue(callspec.returns!!, color, vm)
} }
Syscall.GFX_ENABLE -> vm.gfx_enable()
Syscall.GFX_CLEAR -> vm.gfx_clear()
Syscall.GFX_PLOT -> vm.gfx_plot()
Syscall.GFX_GETPIXEL ->vm.gfx_getpixel()
Syscall.WAIT -> { Syscall.WAIT -> {
val millis = vm.valueStack.popw().toLong() * 1000/60 val time = getArgValues(callspec.arguments, vm).single() as UShort
Thread.sleep(millis) Thread.sleep(time.toLong() * 1000/60)
} }
Syscall.WAITVSYNC -> vm.waitvsync() Syscall.WAITVSYNC -> vm.waitvsync()
Syscall.SORT_UBYTE -> { Syscall.SORT_UBYTE -> {
val length = vm.valueStack.pop().toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = vm.valueStack.popw().toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length-1, 1).map { val array = IntProgression.fromClosedRange(address, address+length-1, 1).map {
vm.memory.getUB(it) vm.memory.getUB(it)
}.sorted() }.sorted()
@ -150,8 +198,9 @@ object SysCalls {
} }
} }
Syscall.SORT_BYTE -> { Syscall.SORT_BYTE -> {
val length = vm.valueStack.pop().toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = vm.valueStack.popw().toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length-1, 1).map { val array = IntProgression.fromClosedRange(address, address+length-1, 1).map {
vm.memory.getSB(it) vm.memory.getSB(it)
}.sorted() }.sorted()
@ -160,8 +209,9 @@ object SysCalls {
} }
} }
Syscall.SORT_UWORD -> { Syscall.SORT_UWORD -> {
val length = vm.valueStack.pop().toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = vm.valueStack.popw().toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map { val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map {
vm.memory.getUW(it) vm.memory.getUW(it)
}.sorted() }.sorted()
@ -170,8 +220,9 @@ object SysCalls {
} }
} }
Syscall.SORT_WORD -> { Syscall.SORT_WORD -> {
val length = vm.valueStack.pop().toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = vm.valueStack.popw().toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map { val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map {
vm.memory.getSW(it) vm.memory.getSW(it)
}.sorted() }.sorted()
@ -180,8 +231,9 @@ object SysCalls {
} }
} }
Syscall.REVERSE_BYTES -> { Syscall.REVERSE_BYTES -> {
val length = vm.valueStack.pop().toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = vm.valueStack.popw().toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length-1, 1).map { val array = IntProgression.fromClosedRange(address, address+length-1, 1).map {
vm.memory.getUB(it) vm.memory.getUB(it)
}.reversed() }.reversed()
@ -190,8 +242,9 @@ object SysCalls {
} }
} }
Syscall.REVERSE_WORDS -> { Syscall.REVERSE_WORDS -> {
val length = vm.valueStack.pop().toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = vm.valueStack.popw().toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map { val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map {
vm.memory.getUW(it) vm.memory.getUW(it)
}.reversed() }.reversed()
@ -200,8 +253,9 @@ object SysCalls {
} }
} }
Syscall.REVERSE_FLOATS -> { Syscall.REVERSE_FLOATS -> {
val length = vm.valueStack.pop().toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = vm.valueStack.popw().toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val array = IntProgression.fromClosedRange(address, address+length*4-2, 4).map { val array = IntProgression.fromClosedRange(address, address+length*4-2, 4).map {
vm.memory.getFloat(it) vm.memory.getFloat(it)
}.reversed() }.reversed()
@ -210,154 +264,154 @@ object SysCalls {
} }
} }
Syscall.ANY_BYTE -> { Syscall.ANY_BYTE -> {
val length = vm.valueStack.pop().toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = vm.valueStack.popw().toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2) val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
if(addresses.any { vm.memory.getUB(it).toInt()!=0 }) if(addresses.any { vm.memory.getUB(it).toInt()!=0 })
vm.valueStack.push(1u) returnValue(callspec.returns!!, 1, vm)
else else
vm.valueStack.push(0u) returnValue(callspec.returns!!, 0, vm)
} }
Syscall.ANY_WORD -> { Syscall.ANY_WORD -> {
val length = vm.valueStack.pop().toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = vm.valueStack.popw().toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2) val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
if(addresses.any { vm.memory.getUW(it).toInt()!=0 }) if(addresses.any { vm.memory.getUW(it).toInt()!=0 })
vm.valueStack.push(1u) returnValue(callspec.returns!!, 1, vm)
else else
vm.valueStack.push(0u) returnValue(callspec.returns!!, 0, vm)
} }
Syscall.ANY_FLOAT -> { Syscall.ANY_FLOAT -> {
val length = vm.valueStack.pop().toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = vm.valueStack.popw().toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*4-2, 4) val addresses = IntProgression.fromClosedRange(address, address+length*4-2, 4)
if(addresses.any { vm.memory.getFloat(it).toInt()!=0 }) if(addresses.any { vm.memory.getFloat(it).toInt()!=0 })
vm.valueStack.push(1u) returnValue(callspec.returns!!, 1, vm)
else else
vm.valueStack.push(0u) returnValue(callspec.returns!!, 0, vm)
} }
Syscall.ALL_BYTE -> { Syscall.ALL_BYTE -> {
val length = vm.valueStack.pop().toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = vm.valueStack.popw().toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2) val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
if(addresses.all { vm.memory.getUB(it).toInt()!=0 }) if(addresses.all { vm.memory.getUB(it).toInt()!=0 })
vm.valueStack.push(1u) returnValue(callspec.returns!!, 1, vm)
else else
vm.valueStack.push(0u) returnValue(callspec.returns!!, 0, vm)
} }
Syscall.ALL_WORD -> { Syscall.ALL_WORD -> {
val length = vm.valueStack.pop().toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = vm.valueStack.popw().toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2) val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
if(addresses.all { vm.memory.getUW(it).toInt()!=0 }) if(addresses.all { vm.memory.getUW(it).toInt()!=0 })
vm.valueStack.push(1u) returnValue(callspec.returns!!, 1, vm)
else else
vm.valueStack.push(0u) returnValue(callspec.returns!!, 0, vm)
} }
Syscall.ALL_FLOAT -> { Syscall.ALL_FLOAT -> {
val length = vm.valueStack.pop().toInt() val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
val address = vm.valueStack.popw().toInt() val address = (addressV as UShort).toInt()
val length = (lengthV as UByte).toInt()
val addresses = IntProgression.fromClosedRange(address, address+length*4-2, 4) val addresses = IntProgression.fromClosedRange(address, address+length*4-2, 4)
if(addresses.all { vm.memory.getFloat(it).toInt()!=0 }) if(addresses.all { vm.memory.getFloat(it).toInt()!=0 })
vm.valueStack.push(1u) returnValue(callspec.returns!!, 1, vm)
else else
vm.valueStack.push(0u) returnValue(callspec.returns!!, 0, vm)
} }
Syscall.PRINT_F -> { Syscall.PRINT_F -> {
print(vm.valueStack.popf()) val value = getArgValues(callspec.arguments, vm).single() as Float
print(value)
} }
Syscall.STR_TO_UWORD -> { Syscall.STR_TO_UWORD -> {
val stringAddr = vm.valueStack.popw() val stringAddr = getArgValues(callspec.arguments, vm).single() as UShort
val string = vm.memory.getString(stringAddr.toInt()).takeWhile { it.isDigit() } val string = vm.memory.getString(stringAddr.toInt()).takeWhile { it.isDigit() }
val value = try { val value = try {
string.toUShort() string.toUShort()
} catch(_: NumberFormatException) { } catch(_: NumberFormatException) {
0u 0u
} }
vm.valueStack.pushw(value) returnValue(callspec.returns!!, value, vm)
} }
Syscall.STR_TO_WORD -> { Syscall.STR_TO_WORD -> {
val stringAddr = vm.valueStack.popw() val stringAddr = getArgValues(callspec.arguments, vm).single() as UShort
val memstring = vm.memory.getString(stringAddr.toInt()) val memstring = vm.memory.getString(stringAddr.toInt())
val match = Regex("^[+-]?\\d+").find(memstring) val match = Regex("^[+-]?\\d+").find(memstring) ?: return returnValue(callspec.returns!!, 0, vm)
if(match==null) {
vm.valueStack.pushw(0u)
return
}
val value = try { val value = try {
match.value.toShort() match.value.toShort()
} catch(_: NumberFormatException) { } catch(_: NumberFormatException) {
0 0
} }
vm.valueStack.pushw(value.toUShort()) return returnValue(callspec.returns!!, value, vm)
} }
Syscall.COMPARE_STRINGS -> { Syscall.COMPARE_STRINGS -> {
val secondAddr = vm.valueStack.popw() val (firstV, secondV) = getArgValues(callspec.arguments, vm)
val firstAddr = vm.valueStack.popw() val firstAddr = firstV as UShort
val secondAddr = secondV as UShort
val first = vm.memory.getString(firstAddr.toInt()) val first = vm.memory.getString(firstAddr.toInt())
val second = vm.memory.getString(secondAddr.toInt()) val second = vm.memory.getString(secondAddr.toInt())
val comparison = first.compareTo(second) val comparison = first.compareTo(second)
if(comparison==0) if(comparison==0)
vm.valueStack.push(0u) returnValue(callspec.returns!!, 0, vm)
else if(comparison<0) else if(comparison<0)
vm.valueStack.push((-1).toUByte()) returnValue(callspec.returns!!, -1, vm)
else else
vm.valueStack.push(1u) returnValue(callspec.returns!!, 1, vm)
} }
Syscall.RNDFSEED -> { Syscall.RNDFSEED -> {
val seed = vm.valueStack.popf() val seed = getArgValues(callspec.arguments, vm).single() as Float
if(seed>0) // always use negative seed, this mimics the behavior on CBM machines if(seed>0) // always use negative seed, this mimics the behavior on CBM machines
vm.randomSeedFloat(-seed) vm.randomSeedFloat(-seed)
else else
vm.randomSeedFloat(seed) vm.randomSeedFloat(seed)
} }
Syscall.RNDSEED -> { Syscall.RNDSEED -> {
val seed2 = vm.valueStack.popw() val (seed1, seed2) = getArgValues(callspec.arguments, vm)
val seed1 = vm.valueStack.popw() vm.randomSeed(seed1 as UShort, seed2 as UShort)
vm.randomSeed(seed1, seed2)
} }
Syscall.RND -> { Syscall.RND -> {
vm.valueStack.push(vm.randomGenerator.nextInt().toUByte()) returnValue(callspec.returns!!, vm.randomGenerator.nextInt().toUByte(), vm)
} }
Syscall.RNDW -> { Syscall.RNDW -> {
vm.valueStack.pushw(vm.randomGenerator.nextInt().toUShort()) returnValue(callspec.returns!!, vm.randomGenerator.nextInt().toUShort(), vm)
} }
Syscall.RNDF -> { Syscall.RNDF -> {
vm.valueStack.pushf(vm.randomGeneratorFloats.nextFloat()) returnValue(callspec.returns!!, vm.randomGeneratorFloats.nextFloat(), vm)
} }
Syscall.STRING_CONTAINS -> { Syscall.STRING_CONTAINS -> {
val stringAddr = vm.valueStack.popw() val (charV, addr) = getArgValues(callspec.arguments, vm)
val char = vm.valueStack.pop().toInt().toChar() val stringAddr = addr as UShort
val char = (charV as UByte).toInt().toChar()
val string = vm.memory.getString(stringAddr.toInt()) val string = vm.memory.getString(stringAddr.toInt())
vm.valueStack.push(if(char in string) 1u else 0u) returnValue(callspec.returns!!, if(char in string) 1u else 0u, vm)
} }
Syscall.BYTEARRAY_CONTAINS -> { Syscall.BYTEARRAY_CONTAINS -> {
var length = vm.valueStack.pop() val (value, arrayV, lengthV) = getArgValues(callspec.arguments, vm)
var array = vm.valueStack.popw().toInt() var length = lengthV as UByte
val value = vm.valueStack.pop() var array = (arrayV as UShort).toInt()
while(length>0u) { while(length>0u) {
if(vm.memory.getUB(array)==value) { if(vm.memory.getUB(array)==value)
vm.valueStack.push(1u) return returnValue(callspec.returns!!, 1u, vm)
return
}
array++ array++
length-- length--
} }
vm.valueStack.push(0u) returnValue(callspec.returns!!, 0u, vm)
} }
Syscall.WORDARRAY_CONTAINS -> { Syscall.WORDARRAY_CONTAINS -> {
var length = vm.valueStack.pop() val (value, arrayV, lengthV) = getArgValues(callspec.arguments, vm)
var array = vm.valueStack.popw().toInt() var length = lengthV as UByte
val value = vm.valueStack.popw() var array = (arrayV as UShort).toInt()
while(length>0u) { while(length>0u) {
if(vm.memory.getUW(array)==value) { if(vm.memory.getUW(array)==value)
vm.valueStack.push(1u) return returnValue(callspec.returns!!, 1u, vm)
return
}
array += 2 array += 2
length-- length--
} }
vm.valueStack.push(0u) returnValue(callspec.returns!!, 0u, vm)
} }
else -> throw AssemblyError("missing syscall ${call.name}") else -> throw AssemblyError("missing syscall ${call.name}")
} }

View File

@ -32,7 +32,7 @@ class BreakpointException(val pcChunk: IRCodeChunk, val pcIndex: Int): Exception
@Suppress("FunctionName") @Suppress("FunctionName")
class VirtualMachine(irProgram: IRProgram) { class VirtualMachine(irProgram: IRProgram) {
class CallSiteContext(val returnChunk: IRCodeChunk, val returnIndex: Int, val returnValueReg: Int?, val returnValueFpReg: Int?) class CallSiteContext(val returnChunk: IRCodeChunk, val returnIndex: Int, val fcallSpec: FunctionCallArgs)
val memory = Memory() val memory = Memory()
val program: List<IRCodeChunk> val program: List<IRCodeChunk>
val registers = Registers() val registers = Registers()
@ -176,8 +176,7 @@ class VirtualMachine(irProgram: IRProgram) {
Opcode.STOREZI -> InsSTOREZI(ins) Opcode.STOREZI -> InsSTOREZI(ins)
Opcode.JUMP -> InsJUMP(ins) Opcode.JUMP -> InsJUMP(ins)
Opcode.JUMPA -> throw IllegalArgumentException("vm program can't jump to system memory address (JUMPA)") Opcode.JUMPA -> throw IllegalArgumentException("vm program can't jump to system memory address (JUMPA)")
Opcode.SETPARAM -> InsSETPARAM(ins) Opcode.CALL -> InsCALL(ins)
Opcode.CALL, Opcode.CALLR -> InsCALL(ins)
Opcode.SYSCALL -> InsSYSCALL(ins) Opcode.SYSCALL -> InsSYSCALL(ins)
Opcode.RETURN -> InsRETURN() Opcode.RETURN -> InsRETURN()
Opcode.RETURNR -> InsRETURNR(ins) Opcode.RETURNR -> InsRETURNR(ins)
@ -286,7 +285,6 @@ class VirtualMachine(irProgram: IRProgram) {
Opcode.SEC -> { statusCarry = true; nextPc() } Opcode.SEC -> { statusCarry = true; nextPc() }
Opcode.LOADCPU -> InsLOADCPU(ins) Opcode.LOADCPU -> InsLOADCPU(ins)
Opcode.STORECPU -> InsSTORECPU(ins) Opcode.STORECPU -> InsSTORECPU(ins)
Opcode.STOREZCPU -> InsSTOREZCPU(ins)
Opcode.FFROMUB -> InsFFROMUB(ins) Opcode.FFROMUB -> InsFFROMUB(ins)
Opcode.FFROMSB -> InsFFROMSB(ins) Opcode.FFROMSB -> InsFFROMSB(ins)
@ -363,7 +361,7 @@ class VirtualMachine(irProgram: IRProgram) {
value.dt=null value.dt=null
} }
val call = Syscall.fromInt(i.immediate!!) val call = Syscall.fromInt(i.immediate!!)
SysCalls.call(call, this) // note: any result value(s) are pushed back on the value stack SysCalls.call(call, i.fcallArgs!!, this) // note: any result value(s) are pushed back on the value stack
nextPc() nextPc()
} }
@ -415,11 +413,6 @@ class VirtualMachine(irProgram: IRProgram) {
nextPc() nextPc()
} }
private fun InsSTOREZCPU(i: IRInstruction) {
StoreCPU(0u, i.type!!, i.labelSymbol!!)
nextPc()
}
private fun StoreCPU(value: UInt, dt: IRDataType, regStr: String) { private fun StoreCPU(value: UInt, dt: IRDataType, regStr: String) {
if(regStr.startsWith('r')) { if(regStr.startsWith('r')) {
val regnum = regStr.substring(1).toInt() val regnum = regStr.substring(1).toInt()
@ -603,30 +596,17 @@ class VirtualMachine(irProgram: IRProgram) {
private class SyscallParamValue(var dt: IRDataType?, var value: Comparable<*>?) private class SyscallParamValue(var dt: IRDataType?, var value: Comparable<*>?)
private val syscallParams = Array(100) { SyscallParamValue(null, null) } private val syscallParams = Array(100) { SyscallParamValue(null, null) }
private fun InsSETPARAM(i: IRInstruction) { private fun InsCALL(i: IRInstruction) {
// store the argument value into the retrieved subroutine's parameter variable (already cached in the instruction's address) i.fcallArgs!!.arguments.forEach { arg ->
// the reason this is a special instruction is to be flexible in implementing the call convention require(arg.address!=null) {"argument variable should have been given its memory address as well"}
val address = i.address when(arg.reg.dt) {
if(address==null) { IRDataType.BYTE -> memory.setUB(arg.address!!, registers.getUB(arg.reg.registerNum))
// this param is for a SYSCALL (that has no param variable address, instead it goes via the stack) IRDataType.WORD -> memory.setUW(arg.address!!, registers.getUW(arg.reg.registerNum))
syscallParams[i.immediate!!].dt = i.type!! IRDataType.FLOAT -> memory.setFloat(arg.address!!, registers.getFloat(arg.reg.registerNum))
syscallParams[i.immediate!!].value = when(i.type!!) {
IRDataType.BYTE -> registers.getUB(i.reg1!!)
IRDataType.WORD -> registers.getUW(i.reg1!!)
IRDataType.FLOAT -> registers.getFloat(i.fpReg1!!)
}
} else {
when (i.type!!) {
IRDataType.BYTE -> memory.setUB(address, registers.getUB(i.reg1!!))
IRDataType.WORD -> memory.setUW(address, registers.getUW(i.reg1!!))
IRDataType.FLOAT -> memory.setFloat(address, registers.getFloat(i.fpReg1!!))
} }
} }
nextPc() // store the call site and jump
} callStack.push(CallSiteContext(pcChunk, pcIndex+1, i.fcallArgs!!))
private fun InsCALL(i: IRInstruction) {
callStack.push(CallSiteContext(pcChunk, pcIndex+1, i.reg1, i.fpReg1))
branchTo(i) branchTo(i)
} }
@ -646,10 +626,11 @@ class VirtualMachine(irProgram: IRProgram) {
exit(0) exit(0)
else { else {
val context = callStack.pop() val context = callStack.pop()
val returns = context.fcallSpec.returns
when (i.type!!) { when (i.type!!) {
IRDataType.BYTE -> { IRDataType.BYTE -> {
if(context.returnValueReg!=null) if(returns!=null)
registers.setUB(context.returnValueReg, registers.getUB(i.reg1!!)) registers.setUB(returns.registerNum, registers.getUB(i.reg1!!))
else { else {
val callInstr = context.returnChunk.instructions[context.returnIndex-1] val callInstr = context.returnChunk.instructions[context.returnIndex-1]
if(callInstr.opcode!=Opcode.CALL) if(callInstr.opcode!=Opcode.CALL)
@ -657,8 +638,8 @@ class VirtualMachine(irProgram: IRProgram) {
} }
} }
IRDataType.WORD -> { IRDataType.WORD -> {
if(context.returnValueReg!=null) if(returns!=null)
registers.setUW(context.returnValueReg, registers.getUW(i.reg1!!)) registers.setUW(returns.registerNum, registers.getUW(i.reg1!!))
else { else {
val callInstr = context.returnChunk.instructions[context.returnIndex-1] val callInstr = context.returnChunk.instructions[context.returnIndex-1]
if(callInstr.opcode!=Opcode.CALL) if(callInstr.opcode!=Opcode.CALL)
@ -666,8 +647,8 @@ class VirtualMachine(irProgram: IRProgram) {
} }
} }
IRDataType.FLOAT -> { IRDataType.FLOAT -> {
if(context.returnValueFpReg!=null) if(returns!=null)
registers.setFloat(context.returnValueFpReg, registers.getFloat(i.fpReg1!!)) registers.setFloat(returns.registerNum, registers.getFloat(i.fpReg1!!))
else { else {
val callInstr = context.returnChunk.instructions[context.returnIndex-1] val callInstr = context.returnChunk.instructions[context.returnIndex-1]
if(callInstr.opcode!=Opcode.CALL) if(callInstr.opcode!=Opcode.CALL)
@ -2302,8 +2283,8 @@ class VirtualMachine(irProgram: IRProgram) {
private var window: GraphicsWindow? = null private var window: GraphicsWindow? = null
fun gfx_enable() { fun gfx_enable(mode: UByte) {
window = when(valueStack.pop().toInt()) { window = when(mode.toInt()) {
0 -> GraphicsWindow(320, 240, 3) 0 -> GraphicsWindow(320, 240, 3)
1 -> GraphicsWindow(640, 480, 2) 1 -> GraphicsWindow(640, 480, 2)
else -> throw IllegalArgumentException("invalid screen mode") else -> throw IllegalArgumentException("invalid screen mode")
@ -2311,25 +2292,20 @@ class VirtualMachine(irProgram: IRProgram) {
window!!.start() window!!.start()
} }
fun gfx_clear() { fun gfx_clear(color: UByte) {
window?.clear(valueStack.pop().toInt()) window?.clear(color.toInt())
} }
fun gfx_plot() { fun gfx_plot(x: UShort, y: UShort, color: UByte) {
val color = valueStack.pop()
val y = valueStack.popw()
val x = valueStack.popw()
window?.plot(x.toInt(), y.toInt(), color.toInt()) window?.plot(x.toInt(), y.toInt(), color.toInt())
} }
fun gfx_getpixel() { fun gfx_getpixel(x: UShort, y: UShort): UByte {
val y = valueStack.popw() return if(window==null)
val x = valueStack.popw() 0u
if(window==null)
valueStack.push(0u)
else { else {
val color = Color(window!!.getpixel(x.toInt(), y.toInt())) val color = Color(window!!.getpixel(x.toInt(), y.toInt()))
valueStack.push(color.green.toUByte()) // gets called from a syscall, return value via stack. color.green.toUByte()
} }
} }

View File

@ -168,27 +168,27 @@ class VmProgramLoader {
} }
} }
chunks.forEach { subroutines.forEach {
it.instructions.withIndex().forEach { (index, ins) -> it.value.chunks.forEach { chunk ->
if(ins.opcode==Opcode.SETPARAM && ins.address==null) { chunk.instructions.withIndex().forEach { (index, ins) ->
val call = findCall(it, index) if(ins.opcode==Opcode.CALL) {
if(call.opcode==Opcode.SYSCALL) { val fcallspec = ins.fcallArgs!!
// there is no variable to set, SYSCALLs get their args from the stack. val argsWithAddresses = fcallspec.arguments.map { arg ->
} else if(call.labelSymbol!=null) { if(arg.address!=null)
// set the address in the instruction to the subroutine's parameter variable's address arg
// this avoids having to look it up every time the SETPARAM instruction is encountered during execution else {
val target = subroutines.getValue(call.labelSymbol!!) val address = variableAddresses.getValue(ins.labelSymbol + "." + arg.name)
val paramVar = target.parameters[ins.immediate!!] FunctionCallArgs.ArgumentSpec(arg.name, address, arg.reg)
val address = variableAddresses.getValue(paramVar.name) }
it.instructions[index] = ins.copy(address = address) }
} else fcallspec.arguments = argsWithAddresses
throw IRParseException("weird call $call") }
} }
} }
} }
} }
private val functionCallOpcodes = setOf(Opcode.CALL, Opcode.CALLR, Opcode.SYSCALL, Opcode.JUMP, Opcode.JUMPA) private val functionCallOpcodes = setOf(Opcode.CALL, Opcode.SYSCALL, Opcode.JUMP, Opcode.JUMPA)
private fun findCall(it: IRCodeChunk, startIndex: Int): IRInstruction { private fun findCall(it: IRCodeChunk, startIndex: Int): IRInstruction {
var idx = startIndex var idx = startIndex
while(it.instructions[idx].opcode !in functionCallOpcodes) while(it.instructions[idx].opcode !in functionCallOpcodes)