mirror of
https://github.com/irmen/prog8.git
synced 2025-01-10 20:30:23 +00:00
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:
parent
bff3c4f95c
commit
905921a684
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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() {
|
||||||
|
@ -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()
|
||||||
|
@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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}")
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user