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

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

View File

@ -82,11 +82,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val left = exprGen.translateExpression(call.args[0])
val 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)
}

View File

@ -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")
}

View File

@ -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))
}
}
}

View File

@ -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
}

View File

@ -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)

View File

@ -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
}}
}

View File

@ -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
}}
}

View File

@ -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
}}
}

View File

@ -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
}}
}

View File

@ -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
}}
}

View File

@ -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
}}
}

View File

@ -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
...

View File

@ -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

View File

@ -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() {

View File

@ -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()

View File

@ -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)
)
}

View File

@ -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

View File

@ -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}")
}

View File

@ -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()
}
}

View File

@ -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)