mirror of
https://github.com/irmen/prog8.git
synced 2024-12-23 09:32:43 +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 right = exprGen.translateExpression(call.args[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)
|
||||
addInstr(result, IRInstruction(Opcode.SETPARAM, IRDataType.WORD, reg1=right.resultReg, immediate = 1), null)
|
||||
addInstr(result, IRInstruction(Opcode.SYSCALL, immediate = IMSyscall.COMPARE_STRINGS.number), null)
|
||||
addInstr(result, IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=left.resultReg), null)
|
||||
result += codeGen.makeSyscall(IMSyscall.COMPARE_STRINGS, listOf(IRDataType.WORD to left.resultReg, IRDataType.WORD to right.resultReg), IRDataType.BYTE to left.resultReg)
|
||||
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 tr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.WORD, reg1 = tr.resultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = tr.resultReg, immediate = array.length)
|
||||
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)
|
||||
}
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = array.length), null)
|
||||
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
|
||||
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 tr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.WORD, reg1 = tr.resultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = tr.resultReg, immediate = array.length)
|
||||
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)
|
||||
}
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = array.length), null)
|
||||
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
|
||||
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 tr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.WORD, reg1 = tr.resultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = tr.resultReg, immediate = if(array.dt==DataType.STR) array.length!!-1 else array.length)
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, reg1 = tr.resultReg, immediate = 1)
|
||||
it += IRInstruction(Opcode.SYSCALL, immediate = syscall.number)
|
||||
}
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(array.dt==DataType.STR) array.length!!-1 else array.length), null)
|
||||
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null)
|
||||
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 tr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.WORD, reg1 = tr.resultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = tr.resultReg, immediate = if(array.dt==DataType.STR) array.length!!-1 else array.length)
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, reg1 = tr.resultReg, immediate = 1)
|
||||
it += IRInstruction(Opcode.SYSCALL, immediate = syscall.number)
|
||||
}
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(array.dt==DataType.STR) array.length!!-1 else array.length), null)
|
||||
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
}
|
||||
|
||||
|
@ -4,10 +4,7 @@ import prog8.code.StRomSub
|
||||
import prog8.code.StStaticVariable
|
||||
import prog8.code.StSub
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.PassByValueDatatypes
|
||||
import prog8.code.core.SignedDatatypes
|
||||
import prog8.code.core.*
|
||||
import prog8.intermediate.*
|
||||
|
||||
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 {
|
||||
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
|
||||
when(iterable.dt) {
|
||||
DataType.STR -> {
|
||||
tr = translateExpression(check.element)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, tr.resultReg, immediate = 0), null)
|
||||
tr = translateExpression(check.iterable)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.SETPARAM, IRDataType.WORD, tr.resultReg, immediate = 1), null)
|
||||
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)
|
||||
val elementTr = translateExpression(check.element)
|
||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||
val iterableTr = translateExpression(check.iterable)
|
||||
addToResult(result, iterableTr, iterableTr.resultReg, -1)
|
||||
result += codeGen.makeSyscall(IMSyscall.STRING_CONTAINS, listOf(IRDataType.BYTE to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg), IRDataType.BYTE to elementTr.resultReg)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
|
||||
}
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
tr = translateExpression(check.element)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.SETPARAM, IRDataType.BYTE, tr.resultReg, immediate = 0), null)
|
||||
tr = translateExpression(check.iterable)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.WORD, tr.resultReg, immediate = 1)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=tr.resultReg, immediate = iterable.length!!)
|
||||
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)
|
||||
val elementTr = translateExpression(check.element)
|
||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||
val iterableTr = translateExpression(check.iterable)
|
||||
addToResult(result, iterableTr, iterableTr.resultReg, -1)
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterable.length!!), null)
|
||||
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)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
|
||||
}
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
tr = translateExpression(check.element)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.SETPARAM, IRDataType.WORD, tr.resultReg, immediate = 0), null)
|
||||
tr = translateExpression(check.iterable)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SETPARAM, IRDataType.WORD, tr.resultReg, immediate = 1)
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=tr.resultReg, immediate = iterable.length!!)
|
||||
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)
|
||||
val elementTr = translateExpression(check.element)
|
||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||
val iterableTr = translateExpression(check.iterable)
|
||||
addToResult(result, iterableTr, iterableTr.resultReg, -1)
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterable.length!!), null)
|
||||
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)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
|
||||
}
|
||||
DataType.ARRAY_F -> throw AssemblyError("containment check in float-array not supported")
|
||||
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)) {
|
||||
is StSub -> {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
for ((index, argspec) in fcall.args.zip(callTarget.parameters).withIndex()) {
|
||||
val (arg, param) = argspec
|
||||
val paramDt = codeGen.irType(param.type)
|
||||
// assign the arguments
|
||||
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
|
||||
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
||||
val paramDt = codeGen.irType(parameter.type)
|
||||
val tr = translateExpression(arg)
|
||||
result += tr.chunks
|
||||
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
|
||||
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)) {
|
||||
// val paramDt = codeGen.irType(parameter.type)
|
||||
// val symbol = "${fcall.name}.${parameter.name}"
|
||||
// if(codeGen.isZero(arg)) {
|
||||
// addInstr(result, IRInstruction(Opcode.STOREZM, paramDt, labelSymbol = symbol), null)
|
||||
// } else {
|
||||
// if (paramDt == IRDataType.FLOAT) {
|
||||
// val tr = translateExpression(arg)
|
||||
// addToResult(result, tr, -1, tr.resultFpReg)
|
||||
// addInstr(result, IRInstruction(Opcode.STOREM, paramDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol), null)
|
||||
// } else {
|
||||
// val tr = translateExpression(arg)
|
||||
// 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)
|
||||
// return value
|
||||
val returnRegSpec = if(fcall.void) null else {
|
||||
val returnIrType = codeGen.irType(callTarget.returnType!!)
|
||||
if(returnIrType==IRDataType.FLOAT)
|
||||
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFreeFloat(), null)
|
||||
else
|
||||
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.nextFree(), null)
|
||||
}
|
||||
// create the call
|
||||
val call = IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec))
|
||||
addInstr(result, call, null)
|
||||
return if(fcall.void)
|
||||
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
} else {
|
||||
var resultReg = -1
|
||||
var resultFpReg = -1
|
||||
if(fcall.type==DataType.FLOAT) {
|
||||
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)
|
||||
}
|
||||
else if(call.fcallArgs!!.returns!!.dt==IRDataType.FLOAT)
|
||||
ExpressionCodeResult(result, codeGen.irType(fcall.type), -1, call.fcallArgs!!.returns!!.registerNum)
|
||||
else
|
||||
ExpressionCodeResult(result, codeGen.irType(fcall.type), call.fcallArgs!!.returns!!.registerNum, -1)
|
||||
}
|
||||
is StRomSub -> {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
// assign the arguments
|
||||
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
|
||||
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
||||
val paramDt = codeGen.irType(parameter.type)
|
||||
val paramRegStr = if(parameter.register.registerOrPair!=null) parameter.register.registerOrPair.toString() else parameter.register.statusflag.toString()
|
||||
if(codeGen.isZero(arg)) {
|
||||
addInstr(result, IRInstruction(Opcode.STOREZCPU, paramDt, labelSymbol = paramRegStr), null)
|
||||
val tr = translateExpression(arg)
|
||||
if(paramDt==IRDataType.FLOAT)
|
||||
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 {
|
||||
if (paramDt == IRDataType.FLOAT)
|
||||
throw AssemblyError("doesn't support float register argument in asm romsub")
|
||||
val tr = translateExpression(arg)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.STORECPU, paramDt, reg1 = tr.resultReg, labelSymbol = paramRegStr), null)
|
||||
// multiple return values: take the first *register* (not status flag) return value and ignore the rest.
|
||||
val returns = callTarget.returns.first { it.register.registerOrPair!=null }
|
||||
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)
|
||||
}
|
||||
}
|
||||
// just a regular call without using Vm register call convention: the value is returned in CPU registers!
|
||||
addInstr(result, IRInstruction(Opcode.CALL, address = callTarget.address.toInt()), null)
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
if(!fcall.void) {
|
||||
when(callTarget.returns.size) {
|
||||
0 -> throw AssemblyError("expect a return value")
|
||||
1 -> {
|
||||
if(fcall.type==DataType.FLOAT)
|
||||
throw AssemblyError("doesn't support float register result in asm romsub")
|
||||
val returns = callTarget.returns.single()
|
||||
val regStr = if(returns.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 -> {
|
||||
val returnRegister = callTarget.returns.singleOrNull{ it.register.registerOrPair!=null }
|
||||
if(returnRegister!=null) {
|
||||
// we skip the other values returned in the status flags.
|
||||
val regStr = returnRegister.register.registerOrPair.toString()
|
||||
addInstr(result, IRInstruction(Opcode.LOADCPU, codeGen.irType(fcall.type), reg1=resultReg, labelSymbol = regStr), null)
|
||||
} else {
|
||||
val firstReturnRegister = callTarget.returns.firstOrNull{ it.register.registerOrPair!=null }
|
||||
if(firstReturnRegister!=null) {
|
||||
// we just take the first register return value and ignore the rest.
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
// create the call
|
||||
val call = IRInstruction(Opcode.CALL, address = callTarget.address.toInt(), fcallArgs = FunctionCallArgs(argRegisters, returnRegSpec))
|
||||
addInstr(result, call, null)
|
||||
if(fcall.void) {
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
} else {
|
||||
val regStr = if(callTarget.returns.isEmpty())
|
||||
throw AssemblyError("expect a return value")
|
||||
else if(callTarget.returns.size==1) {
|
||||
val returns = callTarget.returns.single()
|
||||
if(returns.register.registerOrPair!=null) returns.register.registerOrPair.toString() else returns.register.statusflag.toString()
|
||||
} else {
|
||||
// multiple return values: take the first *register* (not status flag) return value and ignore the rest.
|
||||
callTarget.returns.first { it.register.registerOrPair!=null }.toString()
|
||||
}
|
||||
return if(fcall.type==DataType.FLOAT) {
|
||||
val resultFpReg = codeGen.registers.nextFreeFloat()
|
||||
addInstr(result, IRInstruction(Opcode.LOADCPU, IRDataType.FLOAT, fpReg1 = resultFpReg, labelSymbol = regStr), null)
|
||||
ExpressionCodeResult(result, returnRegSpec!!.dt, -1, resultFpReg)
|
||||
} else {
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
addInstr(result, IRInstruction(Opcode.LOADCPU, returnRegSpec!!.dt, reg1 = resultReg, labelSymbol = regStr), null)
|
||||
ExpressionCodeResult(result, returnRegSpec.dt, resultReg, -1)
|
||||
}
|
||||
}
|
||||
return ExpressionCodeResult(result, if(fcall.void) IRDataType.BYTE else codeGen.irType(fcall.type), resultReg, -1)
|
||||
}
|
||||
else -> throw AssemblyError("invalid node type")
|
||||
}
|
||||
|
@ -1589,4 +1589,14 @@ class IRCodeGen(
|
||||
irSymbolTable.add(staticVar)
|
||||
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
|
||||
if(idx>0 && ins.opcode==Opcode.RETURN) {
|
||||
val previous = chunk.instructions[idx-1]
|
||||
if(previous.opcode==Opcode.CALL || previous.opcode==Opcode.CALLR) {
|
||||
chunk.instructions[idx-1] = IRInstruction(Opcode.JUMP, address = previous.address, labelSymbol = previous.labelSymbol, branchTarget = previous.branchTarget)
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
// This can no longer be done here on the IR level, with the current CALL opcode that encodes the full subroutine call setup.
|
||||
// If machine code is ever generated from this IR, *that* should possibly optimize the JSR + RTS into a JMP.
|
||||
// if(idx>0 && ins.opcode==Opcode.RETURN) {
|
||||
// val previous = chunk.instructions[idx-1]
|
||||
// if(previous.opcode==Opcode.CALL) {
|
||||
// chunk.instructions[idx-1] = IRInstruction(Opcode.JUMP, address = previous.address, labelSymbol = previous.labelSymbol, branchTarget = previous.branchTarget)
|
||||
// chunk.instructions.removeAt(idx)
|
||||
// changed = true
|
||||
// }
|
||||
// }
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
@ -441,7 +441,7 @@ class TestVmCodeGen: FunSpec({
|
||||
irChunks.size shouldBe 1
|
||||
}
|
||||
|
||||
test("romsub allowed in codegen") {
|
||||
test("romsub allowed in ir-codegen") {
|
||||
//main {
|
||||
// romsub $5000 = routine()
|
||||
//
|
||||
@ -452,7 +452,7 @@ class TestVmCodeGen: FunSpec({
|
||||
val codegen = VmCodeGen()
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
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)
|
||||
val sub = PtSub("start", emptyList(), null, 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)
|
||||
%ir {{
|
||||
loadm.w r65535,conv.str2uword.string
|
||||
setparam.w r65535,0
|
||||
syscall 11
|
||||
pop.w r0
|
||||
syscall 11 (r65535.w) : r0.w
|
||||
returnr.w r0
|
||||
}}
|
||||
}
|
||||
@ -210,9 +208,7 @@ sub str2word(str string) -> word {
|
||||
; (any non-digit character will terminate the number string that is parsed)
|
||||
%ir {{
|
||||
loadm.w r65535,conv.str2word.string
|
||||
setparam.w r65535,0
|
||||
syscall 12
|
||||
pop.w r0
|
||||
syscall 12 (r65535.w) : r0.w
|
||||
returnr.w r0
|
||||
}}
|
||||
}
|
||||
|
@ -11,8 +11,7 @@ sub print_f(float value) {
|
||||
; ---- prints the floating point value (without a newline).
|
||||
%ir {{
|
||||
loadm.f fr65535,floats.print_f.value
|
||||
setparam.f fr65535,0
|
||||
syscall 25
|
||||
syscall 25 (fr65535.f)
|
||||
return
|
||||
}}
|
||||
}
|
||||
@ -127,8 +126,7 @@ sub ceil(float value) -> float {
|
||||
|
||||
sub rndf() -> float {
|
||||
%ir {{
|
||||
syscall 35
|
||||
pop.f fr0
|
||||
syscall 35 () : fr0.f
|
||||
returnr.f fr0
|
||||
}}
|
||||
}
|
||||
@ -136,8 +134,8 @@ sub rndf() -> float {
|
||||
sub rndseedf(float seed) {
|
||||
%ir {{
|
||||
loadm.f fr65535,floats.rndseedf.seed
|
||||
setparam.f fr65535,0
|
||||
syscall 32
|
||||
syscall 32 (fr65535.f)
|
||||
return
|
||||
}}
|
||||
}
|
||||
|
||||
|
@ -161,16 +161,14 @@ math {
|
||||
|
||||
sub rnd() -> ubyte {
|
||||
%ir {{
|
||||
syscall 33
|
||||
pop.b r0
|
||||
syscall 33 (): r0.b
|
||||
returnr.b r0
|
||||
}}
|
||||
}
|
||||
|
||||
sub rndw() -> uword {
|
||||
%ir {{
|
||||
syscall 34
|
||||
pop.w r0
|
||||
syscall 34 (): r0.w
|
||||
returnr.w r0
|
||||
}}
|
||||
}
|
||||
@ -178,11 +176,9 @@ math {
|
||||
sub rndseed(uword seed1, uword seed2) {
|
||||
; -- reset the pseudo RNG's seed values. Defaults are: $a55a, $7653.
|
||||
%ir {{
|
||||
loadm.w r65535,math.rndseed.seed1
|
||||
setparam.w r65535,0
|
||||
loadm.w r65534,math.rndseed.seed1
|
||||
loadm.w r65535,math.rndseed.seed2
|
||||
setparam.w r65535,1
|
||||
syscall 31
|
||||
syscall 31 (r65534.w, r65535.w)
|
||||
return
|
||||
}}
|
||||
}
|
||||
|
@ -84,12 +84,9 @@ string {
|
||||
; 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).
|
||||
%ir {{
|
||||
loadm.w r65535,string.compare.st1
|
||||
setparam.w r65535,0
|
||||
loadm.w r65534,string.compare.st1
|
||||
loadm.w r65535,string.compare.st2
|
||||
setparam.w r65535,1
|
||||
syscall 29
|
||||
pop.b r0
|
||||
syscall 29 (r65534.w, r65535.w) : r0.b
|
||||
returnr.b r0
|
||||
}}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ sys {
|
||||
sub reset_system() {
|
||||
; Soft-reset the system back to initial power-on Basic prompt.
|
||||
%ir {{
|
||||
syscall 0
|
||||
syscall 0 ()
|
||||
}}
|
||||
}
|
||||
|
||||
@ -16,15 +16,14 @@ sys {
|
||||
; --- wait approximately the given number of jiffies (1/60th seconds)
|
||||
%ir {{
|
||||
loadm.w r65535,sys.wait.jiffies
|
||||
setparam.w r65535,0
|
||||
syscall 13
|
||||
syscall 13 (r65535.w)
|
||||
}}
|
||||
}
|
||||
|
||||
sub waitvsync() {
|
||||
; --- busy wait till the next vsync has occurred (approximately), without depending on custom irq handling.
|
||||
%ir {{
|
||||
syscall 14
|
||||
syscall 14()
|
||||
}}
|
||||
}
|
||||
|
||||
@ -64,8 +63,7 @@ sys {
|
||||
; -- immediately exit the program with a return code in the A register
|
||||
%ir {{
|
||||
loadm.b r65535,sys.exit.returnvalue
|
||||
setparam.b r65535,0
|
||||
syscall 1
|
||||
syscall 1 (r65535.b)
|
||||
}}
|
||||
}
|
||||
|
||||
@ -85,39 +83,31 @@ sys {
|
||||
sub gfx_enable(ubyte mode) {
|
||||
%ir {{
|
||||
loadm.b r65535,sys.gfx_enable.mode
|
||||
setparam.b r65535,0
|
||||
syscall 8
|
||||
syscall 8 (r65535.b)
|
||||
}}
|
||||
}
|
||||
|
||||
sub gfx_clear(ubyte color) {
|
||||
%ir {{
|
||||
loadm.b r65535,sys.gfx_clear.color
|
||||
setparam.b r65535,0
|
||||
syscall 9
|
||||
syscall 9 (r65535.b)
|
||||
}}
|
||||
}
|
||||
|
||||
sub gfx_plot(uword xx, uword yy, ubyte color) {
|
||||
%ir {{
|
||||
loadm.w r65535,sys.gfx_plot.xx
|
||||
setparam.w r65535,0
|
||||
loadm.w r65535,sys.gfx_plot.yy
|
||||
setparam.w r65535,1
|
||||
loadm.w r65533,sys.gfx_plot.xx
|
||||
loadm.w r65534,sys.gfx_plot.yy
|
||||
loadm.b r65535,sys.gfx_plot.color
|
||||
setparam.b r65535,2
|
||||
syscall 10
|
||||
syscall 10 (r65533.w, r65534.w, r65535.b)
|
||||
}}
|
||||
}
|
||||
|
||||
sub gfx_getpixel(uword xx, uword yy) -> ubyte {
|
||||
%ir {{
|
||||
loadm.w r65535,sys.gfx_getpixel.xx
|
||||
setparam.w r65535,0
|
||||
loadm.w r65534,sys.gfx_getpixel.xx
|
||||
loadm.w r65535,sys.gfx_getpixel.yy
|
||||
setparam.w r65535,1
|
||||
syscall 30
|
||||
pop.b r0
|
||||
syscall 30 (r65534.w, r65535.w): r0.b
|
||||
returnr.b r0
|
||||
}}
|
||||
}
|
||||
|
@ -16,8 +16,7 @@ sub clear_screen() {
|
||||
str @shared sequence = "\x1b[2J\x1B[H"
|
||||
%ir {{
|
||||
load.w r65535,txt.clear_screen.sequence
|
||||
setparam.w r65535,0
|
||||
syscall 3
|
||||
syscall 3 (r65535.w)
|
||||
}}
|
||||
}
|
||||
|
||||
@ -40,16 +39,14 @@ sub uppercase() {
|
||||
sub chrout(ubyte char) {
|
||||
%ir {{
|
||||
loadm.b r65535,txt.chrout.char
|
||||
setparam.b r65535,0
|
||||
syscall 2
|
||||
syscall 2 (r65535.b)
|
||||
}}
|
||||
}
|
||||
|
||||
sub print (str text) {
|
||||
%ir {{
|
||||
loadm.w r65535,txt.print.text
|
||||
setparam.w r65535,0
|
||||
syscall 3
|
||||
syscall 3 (r65535.w)
|
||||
}}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
; It assumes the keyboard is selected as I/O channel!
|
||||
%ir {{
|
||||
loadm.w r65535,txt.input_chars.buffer
|
||||
setparam.w r65535,0
|
||||
loadm.w r65534,txt.input_chars.buffer
|
||||
load.b r65535,80
|
||||
setparam.b r65535,1
|
||||
syscall 6
|
||||
pop.b r0
|
||||
syscall 6 (r65534.w, r65535.b): r0.b
|
||||
returnr.b r0
|
||||
}}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ TODO
|
||||
For next minor release
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
- 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 {
|
||||
const ubyte BAR_Y_OFFSET = 6
|
||||
const ubyte BAR_Y_OFFSET = 5
|
||||
uword next_irq_line = 0
|
||||
ubyte anim1 = 0
|
||||
ubyte av1 = 0
|
||||
|
@ -5,11 +5,14 @@
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
uword seconds_uword = 1
|
||||
uword remainder = seconds_uword % $0003 ==0
|
||||
txt.print_uw(remainder)
|
||||
|
||||
blerp()
|
||||
txt.chrout('!')
|
||||
txt.print("test")
|
||||
txt.nl()
|
||||
; uword seconds_uword = 1
|
||||
; uword remainder = seconds_uword % $0003 ==0
|
||||
; txt.print_uw(remainder)
|
||||
;
|
||||
; blerp()
|
||||
}
|
||||
|
||||
sub blerp() {
|
||||
|
@ -1,5 +1,7 @@
|
||||
package prog8.intermediate
|
||||
|
||||
import prog8.code.core.CpuRegister
|
||||
import prog8.code.core.RegisterOrStatusflag
|
||||
import prog8.code.core.toHex
|
||||
|
||||
/*
|
||||
@ -54,14 +56,18 @@ CONTROL FLOW
|
||||
------------
|
||||
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)
|
||||
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 location - save current instruction location+1, continue execution at instruction nr given by location. No return value is expected.
|
||||
callr reg1, location - like call but expects the routine to return a value with a returnr instruction, it then puts that in reg1
|
||||
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)
|
||||
call label(argument register list) [: resultreg.type]
|
||||
- calls a subroutine with the given arguments and return value (optional).
|
||||
save current instruction location+1, continue execution at instruction nr of the label.
|
||||
the argument register list is positional and includes the datatype, ex.: r4.b,r5.w,fp1.f
|
||||
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.
|
||||
returnr reg1 - like return, but also returns the value in reg1 to the caller
|
||||
|
||||
|
||||
|
||||
BRANCHING and CONDITIONALS
|
||||
--------------------------
|
||||
All have type b or w except the branches that only check status bits.
|
||||
@ -232,15 +238,12 @@ enum class Opcode {
|
||||
STOREX,
|
||||
STOREIX,
|
||||
STOREZM,
|
||||
STOREZCPU,
|
||||
STOREZI,
|
||||
STOREZX,
|
||||
|
||||
JUMP,
|
||||
JUMPA,
|
||||
SETPARAM,
|
||||
CALL,
|
||||
CALLR,
|
||||
SYSCALL,
|
||||
RETURN,
|
||||
RETURNR,
|
||||
@ -388,7 +391,6 @@ val OpcodesThatBranch = setOf(
|
||||
Opcode.RETURN,
|
||||
Opcode.RETURNR,
|
||||
Opcode.CALL,
|
||||
Opcode.CALLR,
|
||||
Opcode.SYSCALL,
|
||||
Opcode.BSTCC,
|
||||
Opcode.BSTCS,
|
||||
@ -418,8 +420,7 @@ val OpcodesThatBranch = setOf(
|
||||
|
||||
val OpcodesForCpuRegisters = setOf(
|
||||
Opcode.LOADCPU,
|
||||
Opcode.STORECPU,
|
||||
Opcode.STOREZCPU
|
||||
Opcode.STORECPU
|
||||
)
|
||||
|
||||
enum class IRDataType {
|
||||
@ -442,7 +443,9 @@ data class InstructionFormat(val datatype: IRDataType?,
|
||||
val fpReg1: OperandDirection,
|
||||
val fpReg2: OperandDirection,
|
||||
val address: OperandDirection,
|
||||
val immediate: Boolean) {
|
||||
val immediate: Boolean,
|
||||
val funcCall: Boolean,
|
||||
val sysCall: Boolean) {
|
||||
companion object {
|
||||
fun from(spec: String): Map<IRDataType?, InstructionFormat> {
|
||||
val result = mutableMapOf<IRDataType?, InstructionFormat>()
|
||||
@ -455,33 +458,37 @@ data class InstructionFormat(val datatype: IRDataType?,
|
||||
var immediate = false
|
||||
val splits = part.splitToSequence(',').iterator()
|
||||
val typespec = splits.next()
|
||||
var funcCall = false
|
||||
var sysCall = false
|
||||
while(splits.hasNext()) {
|
||||
when(splits.next()) {
|
||||
"<r1" -> { reg1=OperandDirection.READ }
|
||||
">r1" -> { reg1=OperandDirection.WRITE }
|
||||
"<>r1" -> { reg1=OperandDirection.READWRITE }
|
||||
"<r1" -> reg1 = OperandDirection.READ
|
||||
">r1" -> reg1 = OperandDirection.WRITE
|
||||
"<>r1" -> reg1 = OperandDirection.READWRITE
|
||||
"<r2" -> reg2 = OperandDirection.READ
|
||||
"<fr1" -> { fpreg1=OperandDirection.READ }
|
||||
">fr1" -> { fpreg1=OperandDirection.WRITE }
|
||||
"<>fr1" -> { fpreg1=OperandDirection.READWRITE }
|
||||
"<fr1" -> fpreg1 = OperandDirection.READ
|
||||
">fr1" -> fpreg1 = OperandDirection.WRITE
|
||||
"<>fr1" -> fpreg1 = OperandDirection.READWRITE
|
||||
"<fr2" -> fpreg2 = OperandDirection.READ
|
||||
">i", "<>i" -> throw IllegalArgumentException("can't write into an immediate value")
|
||||
"<i" -> immediate = true
|
||||
"<a" -> address = OperandDirection.READ
|
||||
">a" -> address = OperandDirection.WRITE
|
||||
"<>a" -> address = OperandDirection.READWRITE
|
||||
"call" -> funcCall = true
|
||||
"syscall" -> sysCall = true
|
||||
else -> throw IllegalArgumentException(spec)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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
|
||||
}
|
||||
@ -513,15 +520,12 @@ val instructionFormats = mutableMapOf(
|
||||
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.STOREZM to InstructionFormat.from("BW,>a | F,>a"),
|
||||
Opcode.STOREZCPU to InstructionFormat.from("BW"),
|
||||
Opcode.STOREZI to InstructionFormat.from("BW,<r1 | F,<r1"),
|
||||
Opcode.STOREZX to InstructionFormat.from("BW,<r1,>a | F,<r1,>a"),
|
||||
Opcode.JUMP 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,<a"),
|
||||
Opcode.CALLR to InstructionFormat.from("BW,>r1,<a | F,>fr1,<a"),
|
||||
Opcode.SYSCALL to InstructionFormat.from("N,<i"),
|
||||
Opcode.CALL to InstructionFormat.from("N,call"),
|
||||
Opcode.SYSCALL to InstructionFormat.from("N,syscall"),
|
||||
Opcode.RETURN to InstructionFormat.from("N"),
|
||||
Opcode.RETURNR to InstructionFormat.from("BW,>r1 | F,>fr1"),
|
||||
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(
|
||||
val opcode: Opcode,
|
||||
val type: IRDataType?=null,
|
||||
@ -664,7 +676,8 @@ data class IRInstruction(
|
||||
val immediateFp: Float?=null,
|
||||
val address: Int?=null, // 0-$ffff
|
||||
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)
|
||||
// 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.fpReg2==OperandDirection.UNUSED) require(fpReg2==null) { "invalid fpReg2" }
|
||||
if(format.immediate) {
|
||||
if(type==IRDataType.FLOAT) {
|
||||
if(opcode!=Opcode.SETPARAM)
|
||||
require(immediateFp !=null) {"missing immediate fp value"}
|
||||
else
|
||||
require(immediateFp==null) {"setparam never has immediateFp only immediate"}
|
||||
}
|
||||
else require(immediate!=null || labelSymbol!=null) {"missing immediate value or labelsymbol"}
|
||||
if(type==IRDataType.FLOAT)
|
||||
require(immediateFp !=null) {"missing immediate fp value"}
|
||||
else
|
||||
require(immediate!=null || labelSymbol!=null) {"missing immediate value or labelsymbol"}
|
||||
}
|
||||
if(type!=IRDataType.FLOAT)
|
||||
require(fpReg1==null && fpReg2==null) {"int instruction can't use fp reg"}
|
||||
@ -811,36 +821,70 @@ data class IRInstruction(
|
||||
IRDataType.FLOAT -> result.add(".f ")
|
||||
else -> result.add(" ")
|
||||
}
|
||||
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(this.fcallArgs!=null) {
|
||||
immediate?.let { result.add(it.toHex()) }
|
||||
labelSymbol?.let { result.add(it) }
|
||||
result.add("(")
|
||||
fcallArgs.arguments.forEach {
|
||||
val location = if(it.address==null) {
|
||||
if(it.name.isBlank()) "" else it.name+"="
|
||||
} else "${it.address}="
|
||||
|
||||
if(it.reg.cpuRegister!=null)
|
||||
TODO("handle cpuregister ${it.reg.cpuRegister}")
|
||||
|
||||
when(it.reg.dt) {
|
||||
IRDataType.BYTE -> result.add("${location}r${it.reg.registerNum}.b,")
|
||||
IRDataType.WORD -> result.add("${location}r${it.reg.registerNum}.w,")
|
||||
IRDataType.FLOAT -> result.add("${location}fr${it.reg.registerNum}.f,")
|
||||
}
|
||||
}
|
||||
if(result.last().endsWith(',')) {
|
||||
result.add(result.removeLast().trimEnd(','))
|
||||
}
|
||||
result.add(")")
|
||||
fcallArgs.returns?.let {
|
||||
result.add(":")
|
||||
when(it.dt) {
|
||||
IRDataType.BYTE -> result.add("r${it.registerNum}.b")
|
||||
IRDataType.WORD -> result.add("r${it.registerNum}.w")
|
||||
IRDataType.FLOAT -> result.add("fr${it.registerNum}.f")
|
||||
}
|
||||
}
|
||||
} 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() == ",")
|
||||
result.removeLast()
|
||||
|
@ -122,39 +122,47 @@ fun parseIRCodeLine(line: String): Either<IRInstruction, String> {
|
||||
parseIRValue(operand)
|
||||
}
|
||||
}
|
||||
|
||||
operands.forEach { oper ->
|
||||
if(oper[0] == '&')
|
||||
throw IRParseException("address-of should be done with normal LOAD <symbol>")
|
||||
else if(oper[0] in "rR") {
|
||||
if(reg1==null) reg1 = oper.substring(1).toInt()
|
||||
else if(reg2==null) reg2 = oper.substring(1).toInt()
|
||||
else throw IRParseException("too many register operands")
|
||||
} else if (oper[0] in "fF" && oper[1] in "rR") {
|
||||
if(fpReg1==null) fpReg1 = oper.substring(2).toInt()
|
||||
else if(fpReg2==null) fpReg2 = oper.substring(2).toInt()
|
||||
else throw IRParseException("too many fp register operands")
|
||||
} 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()
|
||||
if(format.sysCall) {
|
||||
val call = parseCall(rest)
|
||||
val syscallNum = parseIRValue(call.target).toInt()
|
||||
return left(IRInstruction(Opcode.SYSCALL, immediate = syscallNum, fcallArgs = FunctionCallArgs(call.args, call.returns)))
|
||||
} else if (format.funcCall) {
|
||||
val call = parseCall(rest)
|
||||
return left(IRInstruction(Opcode.CALL, labelSymbol = call.target, fcallArgs = FunctionCallArgs(call.args, call.returns)))
|
||||
} else {
|
||||
operands.forEach { oper ->
|
||||
if (oper[0] == '&')
|
||||
throw IRParseException("address-of should be done with normal LOAD <symbol>")
|
||||
else if (oper[0] in "rR") {
|
||||
if (reg1 == null) reg1 = oper.substring(1).toInt()
|
||||
else if (reg2 == null) reg2 = oper.substring(1).toInt()
|
||||
else throw IRParseException("too many register operands")
|
||||
} else if (oper[0] in "fF" && oper[1] in "rR") {
|
||||
if (fpReg1 == null) fpReg1 = oper.substring(2).toInt()
|
||||
else if (fpReg2 == null) fpReg2 = oper.substring(2).toInt()
|
||||
else throw IRParseException("too many fp register operands")
|
||||
} 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 {
|
||||
address = value.toInt()
|
||||
}
|
||||
} 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 -> {}
|
||||
}
|
||||
}
|
||||
if(format.immediate && opcode==Opcode.SETPARAM && immediateInt==null) {
|
||||
immediateInt = immediateFp!!.toInt()
|
||||
immediateFp = null
|
||||
}
|
||||
|
||||
if(format.address!=OperandDirection.UNUSED && address==null && labelSymbol==null)
|
||||
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))
|
||||
}
|
||||
|
||||
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>
|
||||
<INLINEASM LABEL="sys.wait" IR="true" POS="[library:/prog8lib/virtual/syslib.p8: line 17 col 10-13]">
|
||||
loadm.w r0,sys.wait.jiffies
|
||||
setparam.w r0,0
|
||||
syscall 13
|
||||
</INLINEASM>
|
||||
<CODE>
|
||||
return
|
||||
|
@ -1,6 +1,8 @@
|
||||
package prog8.vm
|
||||
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.intermediate.FunctionCallArgs
|
||||
import prog8.intermediate.IRDataType
|
||||
import kotlin.math.min
|
||||
|
||||
/*
|
||||
@ -89,21 +91,50 @@ enum class Syscall {
|
||||
}
|
||||
|
||||
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) {
|
||||
Syscall.RESET -> {
|
||||
vm.reset(false)
|
||||
}
|
||||
Syscall.EXIT ->{
|
||||
vm.exit(vm.valueStack.pop().toInt())
|
||||
val exitValue = getArgValues(callspec.arguments, vm).single() as UByte
|
||||
vm.exit(exitValue.toInt())
|
||||
}
|
||||
Syscall.PRINT_C -> {
|
||||
val char = vm.valueStack.pop().toInt()
|
||||
print(Char(char))
|
||||
val char = getArgValues(callspec.arguments, vm).single() as UByte
|
||||
print(Char(char.toInt()))
|
||||
}
|
||||
Syscall.PRINT_S -> {
|
||||
var addr = vm.valueStack.popw().toInt()
|
||||
var addr = (getArgValues(callspec.arguments, vm).single() as UShort).toInt()
|
||||
while(true) {
|
||||
val char = vm.memory.getUB(addr).toInt()
|
||||
if(char==0)
|
||||
@ -113,35 +144,52 @@ object SysCalls {
|
||||
}
|
||||
}
|
||||
Syscall.PRINT_U8 -> {
|
||||
print(vm.valueStack.pop())
|
||||
val value = getArgValues(callspec.arguments, vm).single()
|
||||
print(value)
|
||||
}
|
||||
Syscall.PRINT_U16 -> {
|
||||
print(vm.valueStack.popw())
|
||||
val value = getArgValues(callspec.arguments, vm).single()
|
||||
print(value)
|
||||
}
|
||||
Syscall.INPUT -> {
|
||||
val (address, maxlen) = getArgValues(callspec.arguments, vm)
|
||||
var input = readln()
|
||||
val maxlen = vm.valueStack.pop().toInt()
|
||||
if(maxlen>0)
|
||||
input = input.substring(0, min(input.length, maxlen))
|
||||
vm.memory.setString(vm.valueStack.popw().toInt(), input, true)
|
||||
vm.valueStack.push(input.length.toUByte())
|
||||
val maxlenvalue = (maxlen as UByte).toInt()
|
||||
if(maxlenvalue>0)
|
||||
input = input.substring(0, min(input.length, maxlenvalue))
|
||||
vm.memory.setString((address as UShort).toInt(), input, true)
|
||||
returnValue(callspec.returns!!, input.length, vm)
|
||||
}
|
||||
Syscall.SLEEP -> {
|
||||
val duration = vm.valueStack.popw().toLong()
|
||||
Thread.sleep(duration)
|
||||
val duration = getArgValues(callspec.arguments, vm).single() as UShort
|
||||
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 -> {
|
||||
val millis = vm.valueStack.popw().toLong() * 1000/60
|
||||
Thread.sleep(millis)
|
||||
val time = getArgValues(callspec.arguments, vm).single() as UShort
|
||||
Thread.sleep(time.toLong() * 1000/60)
|
||||
}
|
||||
Syscall.WAITVSYNC -> vm.waitvsync()
|
||||
Syscall.SORT_UBYTE -> {
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
|
||||
val address = (addressV as UShort).toInt()
|
||||
val length = (lengthV as UByte).toInt()
|
||||
val array = IntProgression.fromClosedRange(address, address+length-1, 1).map {
|
||||
vm.memory.getUB(it)
|
||||
}.sorted()
|
||||
@ -150,8 +198,9 @@ object SysCalls {
|
||||
}
|
||||
}
|
||||
Syscall.SORT_BYTE -> {
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
|
||||
val address = (addressV as UShort).toInt()
|
||||
val length = (lengthV as UByte).toInt()
|
||||
val array = IntProgression.fromClosedRange(address, address+length-1, 1).map {
|
||||
vm.memory.getSB(it)
|
||||
}.sorted()
|
||||
@ -160,8 +209,9 @@ object SysCalls {
|
||||
}
|
||||
}
|
||||
Syscall.SORT_UWORD -> {
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
|
||||
val address = (addressV as UShort).toInt()
|
||||
val length = (lengthV as UByte).toInt()
|
||||
val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map {
|
||||
vm.memory.getUW(it)
|
||||
}.sorted()
|
||||
@ -170,8 +220,9 @@ object SysCalls {
|
||||
}
|
||||
}
|
||||
Syscall.SORT_WORD -> {
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
|
||||
val address = (addressV as UShort).toInt()
|
||||
val length = (lengthV as UByte).toInt()
|
||||
val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map {
|
||||
vm.memory.getSW(it)
|
||||
}.sorted()
|
||||
@ -180,8 +231,9 @@ object SysCalls {
|
||||
}
|
||||
}
|
||||
Syscall.REVERSE_BYTES -> {
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
|
||||
val address = (addressV as UShort).toInt()
|
||||
val length = (lengthV as UByte).toInt()
|
||||
val array = IntProgression.fromClosedRange(address, address+length-1, 1).map {
|
||||
vm.memory.getUB(it)
|
||||
}.reversed()
|
||||
@ -190,8 +242,9 @@ object SysCalls {
|
||||
}
|
||||
}
|
||||
Syscall.REVERSE_WORDS -> {
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
|
||||
val address = (addressV as UShort).toInt()
|
||||
val length = (lengthV as UByte).toInt()
|
||||
val array = IntProgression.fromClosedRange(address, address+length*2-2, 2).map {
|
||||
vm.memory.getUW(it)
|
||||
}.reversed()
|
||||
@ -200,8 +253,9 @@ object SysCalls {
|
||||
}
|
||||
}
|
||||
Syscall.REVERSE_FLOATS -> {
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
|
||||
val address = (addressV as UShort).toInt()
|
||||
val length = (lengthV as UByte).toInt()
|
||||
val array = IntProgression.fromClosedRange(address, address+length*4-2, 4).map {
|
||||
vm.memory.getFloat(it)
|
||||
}.reversed()
|
||||
@ -210,154 +264,154 @@ object SysCalls {
|
||||
}
|
||||
}
|
||||
Syscall.ANY_BYTE -> {
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
|
||||
val address = (addressV as UShort).toInt()
|
||||
val length = (lengthV as UByte).toInt()
|
||||
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
|
||||
if(addresses.any { vm.memory.getUB(it).toInt()!=0 })
|
||||
vm.valueStack.push(1u)
|
||||
returnValue(callspec.returns!!, 1, vm)
|
||||
else
|
||||
vm.valueStack.push(0u)
|
||||
returnValue(callspec.returns!!, 0, vm)
|
||||
}
|
||||
Syscall.ANY_WORD -> {
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
|
||||
val address = (addressV as UShort).toInt()
|
||||
val length = (lengthV as UByte).toInt()
|
||||
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
|
||||
if(addresses.any { vm.memory.getUW(it).toInt()!=0 })
|
||||
vm.valueStack.push(1u)
|
||||
returnValue(callspec.returns!!, 1, vm)
|
||||
else
|
||||
vm.valueStack.push(0u)
|
||||
returnValue(callspec.returns!!, 0, vm)
|
||||
}
|
||||
Syscall.ANY_FLOAT -> {
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
|
||||
val address = (addressV as UShort).toInt()
|
||||
val length = (lengthV as UByte).toInt()
|
||||
val addresses = IntProgression.fromClosedRange(address, address+length*4-2, 4)
|
||||
if(addresses.any { vm.memory.getFloat(it).toInt()!=0 })
|
||||
vm.valueStack.push(1u)
|
||||
returnValue(callspec.returns!!, 1, vm)
|
||||
else
|
||||
vm.valueStack.push(0u)
|
||||
returnValue(callspec.returns!!, 0, vm)
|
||||
}
|
||||
Syscall.ALL_BYTE -> {
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
|
||||
val address = (addressV as UShort).toInt()
|
||||
val length = (lengthV as UByte).toInt()
|
||||
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
|
||||
if(addresses.all { vm.memory.getUB(it).toInt()!=0 })
|
||||
vm.valueStack.push(1u)
|
||||
returnValue(callspec.returns!!, 1, vm)
|
||||
else
|
||||
vm.valueStack.push(0u)
|
||||
returnValue(callspec.returns!!, 0, vm)
|
||||
}
|
||||
Syscall.ALL_WORD -> {
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
|
||||
val address = (addressV as UShort).toInt()
|
||||
val length = (lengthV as UByte).toInt()
|
||||
val addresses = IntProgression.fromClosedRange(address, address+length*2-2, 2)
|
||||
if(addresses.all { vm.memory.getUW(it).toInt()!=0 })
|
||||
vm.valueStack.push(1u)
|
||||
returnValue(callspec.returns!!, 1, vm)
|
||||
else
|
||||
vm.valueStack.push(0u)
|
||||
returnValue(callspec.returns!!, 0, vm)
|
||||
}
|
||||
Syscall.ALL_FLOAT -> {
|
||||
val length = vm.valueStack.pop().toInt()
|
||||
val address = vm.valueStack.popw().toInt()
|
||||
val (addressV, lengthV) = getArgValues(callspec.arguments, vm)
|
||||
val address = (addressV as UShort).toInt()
|
||||
val length = (lengthV as UByte).toInt()
|
||||
val addresses = IntProgression.fromClosedRange(address, address+length*4-2, 4)
|
||||
if(addresses.all { vm.memory.getFloat(it).toInt()!=0 })
|
||||
vm.valueStack.push(1u)
|
||||
returnValue(callspec.returns!!, 1, vm)
|
||||
else
|
||||
vm.valueStack.push(0u)
|
||||
returnValue(callspec.returns!!, 0, vm)
|
||||
}
|
||||
Syscall.PRINT_F -> {
|
||||
print(vm.valueStack.popf())
|
||||
val value = getArgValues(callspec.arguments, vm).single() as Float
|
||||
print(value)
|
||||
}
|
||||
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 value = try {
|
||||
string.toUShort()
|
||||
} catch(_: NumberFormatException) {
|
||||
0u
|
||||
}
|
||||
vm.valueStack.pushw(value)
|
||||
returnValue(callspec.returns!!, value, vm)
|
||||
}
|
||||
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 match = Regex("^[+-]?\\d+").find(memstring)
|
||||
if(match==null) {
|
||||
vm.valueStack.pushw(0u)
|
||||
return
|
||||
}
|
||||
val match = Regex("^[+-]?\\d+").find(memstring) ?: return returnValue(callspec.returns!!, 0, vm)
|
||||
val value = try {
|
||||
match.value.toShort()
|
||||
} catch(_: NumberFormatException) {
|
||||
0
|
||||
}
|
||||
vm.valueStack.pushw(value.toUShort())
|
||||
return returnValue(callspec.returns!!, value, vm)
|
||||
}
|
||||
Syscall.COMPARE_STRINGS -> {
|
||||
val secondAddr = vm.valueStack.popw()
|
||||
val firstAddr = vm.valueStack.popw()
|
||||
val (firstV, secondV) = getArgValues(callspec.arguments, vm)
|
||||
val firstAddr = firstV as UShort
|
||||
val secondAddr = secondV as UShort
|
||||
val first = vm.memory.getString(firstAddr.toInt())
|
||||
val second = vm.memory.getString(secondAddr.toInt())
|
||||
val comparison = first.compareTo(second)
|
||||
if(comparison==0)
|
||||
vm.valueStack.push(0u)
|
||||
returnValue(callspec.returns!!, 0, vm)
|
||||
else if(comparison<0)
|
||||
vm.valueStack.push((-1).toUByte())
|
||||
returnValue(callspec.returns!!, -1, vm)
|
||||
else
|
||||
vm.valueStack.push(1u)
|
||||
returnValue(callspec.returns!!, 1, vm)
|
||||
}
|
||||
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
|
||||
vm.randomSeedFloat(-seed)
|
||||
else
|
||||
vm.randomSeedFloat(seed)
|
||||
}
|
||||
Syscall.RNDSEED -> {
|
||||
val seed2 = vm.valueStack.popw()
|
||||
val seed1 = vm.valueStack.popw()
|
||||
vm.randomSeed(seed1, seed2)
|
||||
val (seed1, seed2) = getArgValues(callspec.arguments, vm)
|
||||
vm.randomSeed(seed1 as UShort, seed2 as UShort)
|
||||
}
|
||||
Syscall.RND -> {
|
||||
vm.valueStack.push(vm.randomGenerator.nextInt().toUByte())
|
||||
returnValue(callspec.returns!!, vm.randomGenerator.nextInt().toUByte(), vm)
|
||||
}
|
||||
Syscall.RNDW -> {
|
||||
vm.valueStack.pushw(vm.randomGenerator.nextInt().toUShort())
|
||||
returnValue(callspec.returns!!, vm.randomGenerator.nextInt().toUShort(), vm)
|
||||
}
|
||||
Syscall.RNDF -> {
|
||||
vm.valueStack.pushf(vm.randomGeneratorFloats.nextFloat())
|
||||
returnValue(callspec.returns!!, vm.randomGeneratorFloats.nextFloat(), vm)
|
||||
}
|
||||
Syscall.STRING_CONTAINS -> {
|
||||
val stringAddr = vm.valueStack.popw()
|
||||
val char = vm.valueStack.pop().toInt().toChar()
|
||||
val (charV, addr) = getArgValues(callspec.arguments, vm)
|
||||
val stringAddr = addr as UShort
|
||||
val char = (charV as UByte).toInt().toChar()
|
||||
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 -> {
|
||||
var length = vm.valueStack.pop()
|
||||
var array = vm.valueStack.popw().toInt()
|
||||
val value = vm.valueStack.pop()
|
||||
val (value, arrayV, lengthV) = getArgValues(callspec.arguments, vm)
|
||||
var length = lengthV as UByte
|
||||
var array = (arrayV as UShort).toInt()
|
||||
while(length>0u) {
|
||||
if(vm.memory.getUB(array)==value) {
|
||||
vm.valueStack.push(1u)
|
||||
return
|
||||
}
|
||||
if(vm.memory.getUB(array)==value)
|
||||
return returnValue(callspec.returns!!, 1u, vm)
|
||||
array++
|
||||
length--
|
||||
}
|
||||
vm.valueStack.push(0u)
|
||||
returnValue(callspec.returns!!, 0u, vm)
|
||||
}
|
||||
Syscall.WORDARRAY_CONTAINS -> {
|
||||
var length = vm.valueStack.pop()
|
||||
var array = vm.valueStack.popw().toInt()
|
||||
val value = vm.valueStack.popw()
|
||||
val (value, arrayV, lengthV) = getArgValues(callspec.arguments, vm)
|
||||
var length = lengthV as UByte
|
||||
var array = (arrayV as UShort).toInt()
|
||||
while(length>0u) {
|
||||
if(vm.memory.getUW(array)==value) {
|
||||
vm.valueStack.push(1u)
|
||||
return
|
||||
}
|
||||
if(vm.memory.getUW(array)==value)
|
||||
return returnValue(callspec.returns!!, 1u, vm)
|
||||
array += 2
|
||||
length--
|
||||
}
|
||||
vm.valueStack.push(0u)
|
||||
returnValue(callspec.returns!!, 0u, vm)
|
||||
}
|
||||
else -> throw AssemblyError("missing syscall ${call.name}")
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ class BreakpointException(val pcChunk: IRCodeChunk, val pcIndex: Int): Exception
|
||||
|
||||
@Suppress("FunctionName")
|
||||
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 program: List<IRCodeChunk>
|
||||
val registers = Registers()
|
||||
@ -176,8 +176,7 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
Opcode.STOREZI -> InsSTOREZI(ins)
|
||||
Opcode.JUMP -> InsJUMP(ins)
|
||||
Opcode.JUMPA -> throw IllegalArgumentException("vm program can't jump to system memory address (JUMPA)")
|
||||
Opcode.SETPARAM -> InsSETPARAM(ins)
|
||||
Opcode.CALL, Opcode.CALLR -> InsCALL(ins)
|
||||
Opcode.CALL -> InsCALL(ins)
|
||||
Opcode.SYSCALL -> InsSYSCALL(ins)
|
||||
Opcode.RETURN -> InsRETURN()
|
||||
Opcode.RETURNR -> InsRETURNR(ins)
|
||||
@ -286,7 +285,6 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
Opcode.SEC -> { statusCarry = true; nextPc() }
|
||||
Opcode.LOADCPU -> InsLOADCPU(ins)
|
||||
Opcode.STORECPU -> InsSTORECPU(ins)
|
||||
Opcode.STOREZCPU -> InsSTOREZCPU(ins)
|
||||
|
||||
Opcode.FFROMUB -> InsFFROMUB(ins)
|
||||
Opcode.FFROMSB -> InsFFROMSB(ins)
|
||||
@ -363,7 +361,7 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
value.dt=null
|
||||
}
|
||||
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()
|
||||
}
|
||||
|
||||
@ -415,11 +413,6 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
nextPc()
|
||||
}
|
||||
|
||||
private fun InsSTOREZCPU(i: IRInstruction) {
|
||||
StoreCPU(0u, i.type!!, i.labelSymbol!!)
|
||||
nextPc()
|
||||
}
|
||||
|
||||
private fun StoreCPU(value: UInt, dt: IRDataType, regStr: String) {
|
||||
if(regStr.startsWith('r')) {
|
||||
val regnum = regStr.substring(1).toInt()
|
||||
@ -603,30 +596,17 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
private class SyscallParamValue(var dt: IRDataType?, var value: Comparable<*>?)
|
||||
private val syscallParams = Array(100) { SyscallParamValue(null, null) }
|
||||
|
||||
private fun InsSETPARAM(i: IRInstruction) {
|
||||
// store the argument value into the retrieved subroutine's parameter variable (already cached in the instruction's address)
|
||||
// the reason this is a special instruction is to be flexible in implementing the call convention
|
||||
val address = i.address
|
||||
if(address==null) {
|
||||
// this param is for a SYSCALL (that has no param variable address, instead it goes via the stack)
|
||||
syscallParams[i.immediate!!].dt = i.type!!
|
||||
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!!))
|
||||
private fun InsCALL(i: IRInstruction) {
|
||||
i.fcallArgs!!.arguments.forEach { arg ->
|
||||
require(arg.address!=null) {"argument variable should have been given its memory address as well"}
|
||||
when(arg.reg.dt) {
|
||||
IRDataType.BYTE -> memory.setUB(arg.address!!, registers.getUB(arg.reg.registerNum))
|
||||
IRDataType.WORD -> memory.setUW(arg.address!!, registers.getUW(arg.reg.registerNum))
|
||||
IRDataType.FLOAT -> memory.setFloat(arg.address!!, registers.getFloat(arg.reg.registerNum))
|
||||
}
|
||||
}
|
||||
nextPc()
|
||||
}
|
||||
|
||||
private fun InsCALL(i: IRInstruction) {
|
||||
callStack.push(CallSiteContext(pcChunk, pcIndex+1, i.reg1, i.fpReg1))
|
||||
// store the call site and jump
|
||||
callStack.push(CallSiteContext(pcChunk, pcIndex+1, i.fcallArgs!!))
|
||||
branchTo(i)
|
||||
}
|
||||
|
||||
@ -646,10 +626,11 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
exit(0)
|
||||
else {
|
||||
val context = callStack.pop()
|
||||
val returns = context.fcallSpec.returns
|
||||
when (i.type!!) {
|
||||
IRDataType.BYTE -> {
|
||||
if(context.returnValueReg!=null)
|
||||
registers.setUB(context.returnValueReg, registers.getUB(i.reg1!!))
|
||||
if(returns!=null)
|
||||
registers.setUB(returns.registerNum, registers.getUB(i.reg1!!))
|
||||
else {
|
||||
val callInstr = context.returnChunk.instructions[context.returnIndex-1]
|
||||
if(callInstr.opcode!=Opcode.CALL)
|
||||
@ -657,8 +638,8 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
}
|
||||
}
|
||||
IRDataType.WORD -> {
|
||||
if(context.returnValueReg!=null)
|
||||
registers.setUW(context.returnValueReg, registers.getUW(i.reg1!!))
|
||||
if(returns!=null)
|
||||
registers.setUW(returns.registerNum, registers.getUW(i.reg1!!))
|
||||
else {
|
||||
val callInstr = context.returnChunk.instructions[context.returnIndex-1]
|
||||
if(callInstr.opcode!=Opcode.CALL)
|
||||
@ -666,8 +647,8 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
}
|
||||
}
|
||||
IRDataType.FLOAT -> {
|
||||
if(context.returnValueFpReg!=null)
|
||||
registers.setFloat(context.returnValueFpReg, registers.getFloat(i.fpReg1!!))
|
||||
if(returns!=null)
|
||||
registers.setFloat(returns.registerNum, registers.getFloat(i.fpReg1!!))
|
||||
else {
|
||||
val callInstr = context.returnChunk.instructions[context.returnIndex-1]
|
||||
if(callInstr.opcode!=Opcode.CALL)
|
||||
@ -2302,8 +2283,8 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
|
||||
private var window: GraphicsWindow? = null
|
||||
|
||||
fun gfx_enable() {
|
||||
window = when(valueStack.pop().toInt()) {
|
||||
fun gfx_enable(mode: UByte) {
|
||||
window = when(mode.toInt()) {
|
||||
0 -> GraphicsWindow(320, 240, 3)
|
||||
1 -> GraphicsWindow(640, 480, 2)
|
||||
else -> throw IllegalArgumentException("invalid screen mode")
|
||||
@ -2311,25 +2292,20 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
window!!.start()
|
||||
}
|
||||
|
||||
fun gfx_clear() {
|
||||
window?.clear(valueStack.pop().toInt())
|
||||
fun gfx_clear(color: UByte) {
|
||||
window?.clear(color.toInt())
|
||||
}
|
||||
|
||||
fun gfx_plot() {
|
||||
val color = valueStack.pop()
|
||||
val y = valueStack.popw()
|
||||
val x = valueStack.popw()
|
||||
fun gfx_plot(x: UShort, y: UShort, color: UByte) {
|
||||
window?.plot(x.toInt(), y.toInt(), color.toInt())
|
||||
}
|
||||
|
||||
fun gfx_getpixel() {
|
||||
val y = valueStack.popw()
|
||||
val x = valueStack.popw()
|
||||
if(window==null)
|
||||
valueStack.push(0u)
|
||||
fun gfx_getpixel(x: UShort, y: UShort): UByte {
|
||||
return if(window==null)
|
||||
0u
|
||||
else {
|
||||
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 {
|
||||
it.instructions.withIndex().forEach { (index, ins) ->
|
||||
if(ins.opcode==Opcode.SETPARAM && ins.address==null) {
|
||||
val call = findCall(it, index)
|
||||
if(call.opcode==Opcode.SYSCALL) {
|
||||
// there is no variable to set, SYSCALLs get their args from the stack.
|
||||
} else if(call.labelSymbol!=null) {
|
||||
// set the address in the instruction to the subroutine's parameter variable's address
|
||||
// this avoids having to look it up every time the SETPARAM instruction is encountered during execution
|
||||
val target = subroutines.getValue(call.labelSymbol!!)
|
||||
val paramVar = target.parameters[ins.immediate!!]
|
||||
val address = variableAddresses.getValue(paramVar.name)
|
||||
it.instructions[index] = ins.copy(address = address)
|
||||
} else
|
||||
throw IRParseException("weird call $call")
|
||||
subroutines.forEach {
|
||||
it.value.chunks.forEach { chunk ->
|
||||
chunk.instructions.withIndex().forEach { (index, ins) ->
|
||||
if(ins.opcode==Opcode.CALL) {
|
||||
val fcallspec = ins.fcallArgs!!
|
||||
val argsWithAddresses = fcallspec.arguments.map { arg ->
|
||||
if(arg.address!=null)
|
||||
arg
|
||||
else {
|
||||
val address = variableAddresses.getValue(ins.labelSymbol + "." + arg.name)
|
||||
FunctionCallArgs.ArgumentSpec(arg.name, address, arg.reg)
|
||||
}
|
||||
}
|
||||
fcallspec.arguments = argsWithAddresses
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
var idx = startIndex
|
||||
while(it.instructions[idx].opcode !in functionCallOpcodes)
|
||||
|
Loading…
Reference in New Issue
Block a user