package prog8.vm import prog8.code.core.toHex import prog8.code.target.virtual.IVirtualMachineRunner import prog8.code.target.virtual.VirtualMachineDefinition import prog8.intermediate.* import java.awt.Color import java.awt.Toolkit import java.util.* import kotlin.math.* import kotlin.random.Random /* Virtual machine specs: Program to execute is not stored in the system memory, it's just a separate list of instructions. 65536 virtual registers, 16 bits wide, can also be used as 8 bits. r0-r65535 65536 virtual floating point registers (32 bits single precision floats) fr0-fr65535 65536 bytes of memory, thus memory pointers (addresses) are limited to 16 bits. Value stack, max 128 entries of 1 byte each. Status flags: Carry, Zero, Negative. NOTE: status flags are only affected by the CMP instruction or explicit CLC/SEC!!! logical AND, OR, XOR also set the N and Z bits. arithmetic operations DO NOT AFFECT THE STATUS FLAGS UNLESS EXPLICITLY NOTED! */ class ProgramExitException(val status: Int): Exception() class BreakpointException(val pcChunk: IRCodeChunk, val pcIndex: Int): Exception() @Suppress("FunctionName") class VirtualMachine(irProgram: IRProgram) { class CallSiteContext(val returnChunk: IRCodeChunk, val returnIndex: Int, val fcallSpec: FunctionCallArgs) val memory = Memory() val machinedef = VirtualMachineDefinition() val program: List val artificialLabelAddresses: Map val registers = Registers() val callStack = Stack() val valueStack = Stack() // max 128 entries var breakpointHandler: ((pcChunk: IRCodeChunk, pcIndex: Int) -> Unit)? = null // can set custom breakpoint handler var pcChunk = IRCodeChunk(null, null) var pcIndex = 0 var stepCount = 0 var statusCarry = false var statusZero = false var statusNegative = false internal var randomGenerator = Random(0xa55a7653) internal var randomGeneratorFloats = Random(0xc0d3dbad) internal var mul16_last_upper = 0u init { val (prg, labelAddr) = VmProgramLoader().load(irProgram, memory) program = prg artificialLabelAddresses = mutableMapOf() labelAddr.forEach { labelname, artificialAddress -> artificialLabelAddresses[artificialAddress] = program.single { it.label==labelname } } require(irProgram.st.getAsmSymbols().isEmpty()) { "virtual machine can't yet process asmsymbols defined on command line" } reset(false) } fun run() { try { var before = System.nanoTime() var numIns = 0 while(true) { step() numIns++ // if(stepCount and 32767 == 0) { // Thread.sleep(1) // avoid 100% cpu core usage // } if(stepCount and 0xffffff == 0) { val now = System.nanoTime() val duration = now-before before = now val insPerSecond = numIns*1000.0/duration println("${insPerSecond.roundToInt()} MIPS") numIns = 0 } } } catch (hx: ProgramExitException) { println("\nProgram exit! Statuscode=${hx.status} #steps=${stepCount}") gfx_close() } } fun reset(clearMemory: Boolean) { // "reset" the VM without erasing the currently loaded program // this allows you to re-run the program multiple times without having to reload it registers.reset() if(clearMemory) memory.reset() pcIndex = 0 pcChunk = program.firstOrNull() ?: IRCodeChunk(null, null) stepCount = 0 callStack.clear() valueStack.clear() statusCarry = false statusNegative = false statusZero = false } fun exit(statuscode: Int) { throw ProgramExitException(statuscode) } fun step(count: Int=1) { var left=count while(left>0) { if(pcIndex >= pcChunk.instructions.size) { stepNextChunk() } stepCount++ dispatch(pcChunk.instructions[pcIndex]) left-- } } private fun stepNextChunk() { do { when (val nextChunk = pcChunk.next) { is IRCodeChunk -> { pcChunk = nextChunk pcIndex = 0 } null -> { exit(0) // end of program reached } else -> { throw IllegalArgumentException("VM cannot run code from non-code chunk $nextChunk") } } } while (pcChunk.isEmpty()) } private fun nextPc() { pcIndex ++ if(pcIndex>=pcChunk.instructions.size) stepNextChunk() } private fun branchTo(i: IRInstruction) { when (val target = i.branchTarget) { is IRCodeChunk -> { pcChunk = target pcIndex = 0 } null -> { if(i.address!=null) throw IllegalArgumentException("vm program can't jump to system memory address (${i.opcode} ${i.address!!.toHex()})") else if(i.labelSymbol!=null) throw IllegalArgumentException("vm program can't jump to system memory address (${i.opcode} ${i.labelSymbol})") else if(i.reg1!=null) throw IllegalArgumentException("vm program can't jump to system memory address (${i} = ${registers.getUW(i.reg1!!)})") else throw IllegalArgumentException("no branchtarget in $i") } is IRInlineAsmChunk -> TODO("branch to inline asm chunk") is IRInlineBinaryChunk -> throw IllegalArgumentException("can't branch to inline binary chunk") else -> { throw IllegalArgumentException("VM can't execute code in a non-codechunk: $target") } } } private fun dispatch(ins: IRInstruction) { when(ins.opcode) { Opcode.NOP -> nextPc() Opcode.LOAD -> InsLOAD(ins) Opcode.LOADM -> InsLOADM(ins) Opcode.LOADX -> InsLOADX(ins) Opcode.LOADI -> InsLOADI(ins) Opcode.LOADIX -> InsLOADIX(ins) Opcode.LOADR -> InsLOADR(ins) Opcode.LOADHA, Opcode.LOADHX, Opcode.LOADHY, Opcode.LOADHAX, Opcode.LOADHAY, Opcode.LOADHXY -> throw IllegalArgumentException("VM cannot access actual CPU hardware register") Opcode.STOREM -> InsSTOREM(ins) Opcode.STOREX -> InsSTOREX(ins) Opcode.STOREIX -> InsSTOREIX(ins) Opcode.STOREI -> InsSTOREI(ins) Opcode.STOREZM -> InsSTOREZM(ins) Opcode.STOREZX -> InsSTOREZX(ins) Opcode.STOREZI -> InsSTOREZI(ins) Opcode.STOREHA, Opcode.STOREHX, Opcode.STOREHY, Opcode.STOREHAX, Opcode.STOREHAY, Opcode.STOREHXY -> throw IllegalArgumentException("VM cannot access actual CPU hardware register") Opcode.JUMP -> InsJUMP(ins) Opcode.JUMPI -> InsJUMPI(ins) Opcode.PREPARECALL -> nextPc() Opcode.CALLI -> throw IllegalArgumentException("VM cannot run code from memory bytes") Opcode.CALL -> InsCALL(ins) Opcode.SYSCALL -> InsSYSCALL(ins) Opcode.RETURN -> InsRETURN() Opcode.RETURNR -> InsRETURNR(ins) Opcode.BSTCC -> InsBSTCC(ins) Opcode.BSTCS -> InsBSTCS(ins) Opcode.BSTEQ -> InsBSTEQ(ins) Opcode.BSTNE -> InsBSTNE(ins) Opcode.BSTNEG -> InsBSTNEG(ins) Opcode.BSTPOS -> InsBSTPOS(ins) Opcode.BSTVC, Opcode.BSTVS -> TODO("overflow status flag not yet supported in VM (BSTVC,BSTVS)") Opcode.BGTR -> InsBGTR(ins) Opcode.BGTSR -> InsBGTSR(ins) Opcode.BGER -> InsBGER(ins) Opcode.BGESR -> InsBGESR(ins) Opcode.BGT -> InsBGT(ins) Opcode.BLT -> InsBLT(ins) Opcode.BGTS -> InsBGTS(ins) Opcode.BLTS -> InsBLTS(ins) Opcode.BGE -> InsBGE(ins) Opcode.BLE -> InsBLE(ins) Opcode.BGES -> InsBGES(ins) Opcode.BLES -> InsBLES(ins) Opcode.SCC -> InsSCC(ins) Opcode.SCS -> InsSCS(ins) Opcode.SZ -> InsSZ(ins) Opcode.SNZ -> InsSNZ(ins) Opcode.SEQ -> InsSEQ(ins) Opcode.SNE -> InsSNE(ins) Opcode.SLT -> InsSLT(ins) Opcode.SLTS -> InsSLTS(ins) Opcode.SGT -> InsSGT(ins) Opcode.SGTS -> InsSGTS(ins) Opcode.SLE -> InsSLE(ins) Opcode.SLES -> InsSLES(ins) Opcode.SGE -> InsSGE(ins) Opcode.SGES -> InsSGES(ins) Opcode.INC -> InsINC(ins) Opcode.INCM -> InsINCM(ins) Opcode.DEC -> InsDEC(ins) Opcode.DECM -> InsDECM(ins) Opcode.NEG -> InsNEG(ins) Opcode.NEGM -> InsNEGM(ins) Opcode.ADDR -> InsADDR(ins) Opcode.ADD -> InsADD(ins) Opcode.ADDM -> InsADDM(ins) Opcode.SUBR -> InsSUBR(ins) Opcode.SUB -> InsSUB(ins) Opcode.SUBM -> InsSUBM(ins) Opcode.MULR -> InsMULR(ins) Opcode.MUL -> InsMUL(ins) Opcode.MULM -> InsMULM(ins) Opcode.DIVR -> InsDIVR(ins) Opcode.DIV -> InsDIV(ins) Opcode.DIVM -> InsDIVM(ins) Opcode.DIVSR -> InsDIVSR(ins) Opcode.DIVS -> InsDIVS(ins) Opcode.DIVSM -> InsDIVSM(ins) Opcode.MODR -> InsMODR(ins) Opcode.MOD -> InsMOD(ins) Opcode.DIVMODR -> InsDIVMODR(ins) Opcode.DIVMOD -> InsDIVMOD(ins) Opcode.SGN -> InsSGN(ins) Opcode.CMP -> InsCMP(ins) Opcode.CMPI -> InsCMPI(ins) Opcode.SQRT -> InsSQRT(ins) Opcode.SQUARE -> InsSQUARE(ins) Opcode.EXT -> InsEXT(ins) Opcode.EXTS -> InsEXTS(ins) Opcode.ANDR -> InsANDR(ins) Opcode.AND -> InsAND(ins) Opcode.ANDM -> InsANDM(ins) Opcode.ORR -> InsORR(ins) Opcode.OR -> InsOR(ins) Opcode.ORM -> InsORM(ins) Opcode.XORR -> InsXORR(ins) Opcode.XOR -> InsXOR(ins) Opcode.XORM ->InsXORM(ins) Opcode.INV -> InsINV(ins) Opcode.INVM -> InsINVM(ins) Opcode.ASRN -> InsASRN(ins) Opcode.LSRN -> InsLSRN(ins) Opcode.LSLN -> InsLSLN(ins) Opcode.ASR -> InsASR(ins) Opcode.LSR -> InsLSR(ins) Opcode.LSL -> InsLSL(ins) Opcode.ASRNM -> InsASRNM(ins) Opcode.LSRNM -> InsLSRNM(ins) Opcode.LSLNM -> InsLSLNM(ins) Opcode.ASRM -> InsASRM(ins) Opcode.LSRM -> InsLSRM(ins) Opcode.LSLM -> InsLSLM(ins) Opcode.ROR -> InsROR(ins, false) Opcode.RORM -> InsRORM(ins, false) Opcode.ROXR -> InsROR(ins, true) Opcode.ROXRM -> InsRORM(ins, true) Opcode.ROL -> InsROL(ins, false) Opcode.ROLM -> InsROLM(ins, false) Opcode.ROXL -> InsROL(ins, true) Opcode.ROXLM -> InsROLM(ins, true) Opcode.MSIG -> InsMSIG(ins) Opcode.CONCAT -> InsCONCAT(ins) Opcode.PUSH -> InsPUSH(ins) Opcode.POP -> InsPOP(ins) Opcode.PUSHST -> InsPUSHST() Opcode.POPST -> InsPOPST() Opcode.BREAKPOINT -> InsBREAKPOINT() Opcode.CLC -> { statusCarry = false; nextPc() } Opcode.SEC -> { statusCarry = true; nextPc() } Opcode.FFROMUB -> InsFFROMUB(ins) Opcode.FFROMSB -> InsFFROMSB(ins) Opcode.FFROMUW -> InsFFROMUW(ins) Opcode.FFROMSW -> InsFFROMSW(ins) Opcode.FTOUB -> InsFTOUB(ins) Opcode.FTOSB -> InsFTOSB(ins) Opcode.FTOUW -> InsFTOUW(ins) Opcode.FTOSW -> InsFTOSW(ins) Opcode.FPOW -> InsFPOW(ins) Opcode.FABS -> InsFABS(ins) Opcode.FSIN -> InsFSIN(ins) Opcode.FCOS -> InsFCOS(ins) Opcode.FTAN -> InsFTAN(ins) Opcode.FATAN -> InsFATAN(ins) Opcode.FLN -> InsFLN(ins) Opcode.FLOG -> InsFLOG(ins) Opcode.FROUND -> InsFROUND(ins) Opcode.FFLOOR -> InsFFLOOR(ins) Opcode.FCEIL -> InsFCEIL(ins) Opcode.FCOMP -> InsFCOMP(ins) else -> throw IllegalArgumentException("invalid opcode ${ins.opcode}") } } private inline fun setResultReg(reg: Int, value: Int, type: IRDataType) { when(type) { IRDataType.BYTE -> { registers.setUB(reg, value.toUByte()) statusZero = value==0 statusNegative = value>=0x80 } IRDataType.WORD -> { registers.setUW(reg, value.toUShort()) statusZero = value==0 statusNegative = value>=0x8000 } IRDataType.FLOAT -> throw IllegalArgumentException("attempt to set integer result register but float type") } } private fun InsPUSH(i: IRInstruction) { if(valueStack.size>=128) throw StackOverflowError("valuestack limit 128 exceeded") when(i.type!!) { IRDataType.BYTE -> { val value = registers.getUB(i.reg1!!) valueStack.push(value) } IRDataType.WORD -> { val value = registers.getUW(i.reg1!!) valueStack.pushw(value) } IRDataType.FLOAT -> { val value = registers.getFloat(i.fpReg1!!) valueStack.pushf(value) } } nextPc() } private fun InsPOP(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> setResultReg(i.reg1!!, valueStack.pop().toInt(), i.type!!) IRDataType.WORD -> setResultReg(i.reg1!!, valueStack.popw().toInt(), i.type!!) IRDataType.FLOAT -> registers.setFloat(i.fpReg1!!, valueStack.popf()) } nextPc() } private fun InsPUSHST() { var status: UByte = 0u if(statusNegative) status = status or 0b10000000u if(statusZero) status = status or 0b00000010u if(statusCarry) status = status or 0b00000001u // TODO overflow not yet supported valueStack.push(status) nextPc() } private fun InsPOPST() { val status = valueStack.pop().toInt() statusNegative = status and 0b10000000 != 0 statusZero = status and 0b00000010 != 0 statusCarry = status and 0b00000001 != 0 // TODO overflow not yet supported nextPc() } private fun InsSYSCALL(i: IRInstruction) { // put the syscall's arguments that were prepared onto the stack for(value in syscallParams) { if(value.dt==null) break when(value.dt!!) { IRDataType.BYTE -> valueStack.push(value.value as UByte) IRDataType.WORD -> valueStack.pushw(value.value as UShort) IRDataType.FLOAT -> valueStack.pushf(value.value as Double) } value.dt=null } val call = Syscall.fromInt(i.immediate!!) SysCalls.call(call, i.fcallArgs!!, this) // note: any result value(s) are pushed back on the value stack nextPc() } private fun InsBREAKPOINT() { nextPc() if(breakpointHandler!=null) breakpointHandler?.invoke(pcChunk, pcIndex) else throw BreakpointException(pcChunk, pcIndex) } private fun InsLOAD(i: IRInstruction) { if(i.type==IRDataType.FLOAT) registers.setFloat(i.fpReg1!!, i.immediateFp!!) else { if(i.immediate!=null) { setResultReg(i.reg1!!, i.immediate!!, i.type!!) statusbitsNZ(i.immediate!!, i.type!!) } else { if(i.labelSymbol==null) throw IllegalArgumentException("expected LOAD of address of labelsymbol") setResultReg(i.reg1!!, i.address!!, i.type!!) statusbitsNZ(i.address!!, i.type!!) } } nextPc() } private fun InsLOADM(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> { val value = memory.getUB(i.address!!) registers.setUB(i.reg1!!, value) statusbitsNZ(value.toInt(), i.type!!) } IRDataType.WORD -> { val value = memory.getUW(i.address!!) registers.setUW(i.reg1!!, value) statusbitsNZ(value.toInt(), i.type!!) } IRDataType.FLOAT -> registers.setFloat(i.fpReg1!!, memory.getFloat(i.address!!)) } nextPc() } private fun InsLOADI(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> { val value = memory.getUB(registers.getUW(i.reg2!!).toInt()) registers.setUB(i.reg1!!, value) statusbitsNZ(value.toInt(), i.type!!) } IRDataType.WORD -> { val value = memory.getUW(registers.getUW(i.reg2!!).toInt()) registers.setUW(i.reg1!!, value) statusbitsNZ(value.toInt(), i.type!!) } IRDataType.FLOAT -> registers.setFloat(i.fpReg1!!, memory.getFloat(registers.getUW(i.reg1!!).toInt())) } nextPc() } private fun InsLOADX(i: IRInstruction) { when (i.type!!) { IRDataType.BYTE -> { val value = memory.getUB(i.address!! + registers.getUB(i.reg2!!).toInt()) registers.setUB(i.reg1!!, value) statusbitsNZ(value.toInt(), i.type!!) } IRDataType.WORD -> { val value = memory.getUW(i.address!! + registers.getUB(i.reg2!!).toInt()) registers.setUW(i.reg1!!, value) statusbitsNZ(value.toInt(), i.type!!) } IRDataType.FLOAT -> registers.setFloat(i.fpReg1!!, memory.getFloat(i.address!! + registers.getUB(i.reg1!!).toInt())) } nextPc() } private fun InsLOADIX(i: IRInstruction) { when (i.type!!) { IRDataType.BYTE -> { val pointer = memory.getUW(i.address!!) + registers.getUB(i.reg2!!) val value = memory.getUB(pointer.toInt()) registers.setUB(i.reg1!!, value) statusbitsNZ(value.toInt(), i.type!!) } IRDataType.WORD -> { val pointer = memory.getUW(i.address!!) + registers.getUB(i.reg2!!) val value = memory.getUW(pointer.toInt()) registers.setUW(i.reg1!!, value) statusbitsNZ(value.toInt(), i.type!!) } IRDataType.FLOAT -> { val pointer = memory.getUW(i.address!!) + registers.getUB(i.reg1!!) registers.setFloat(i.fpReg1!!, memory.getFloat(pointer.toInt())) } } nextPc() } private fun InsLOADR(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> { val value = registers.getUB(i.reg2!!) registers.setUB(i.reg1!!, value) statusbitsNZ(value.toInt(), i.type!!) } IRDataType.WORD -> { val value = registers.getUW(i.reg2!!) registers.setUW(i.reg1!!, value) statusbitsNZ(value.toInt(), i.type!!) } IRDataType.FLOAT -> registers.setFloat(i.fpReg1!!, registers.getFloat(i.fpReg2!!)) } nextPc() } private fun InsSTOREM(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> memory.setUB(i.address!!, registers.getUB(i.reg1!!)) IRDataType.WORD -> memory.setUW(i.address!!, registers.getUW(i.reg1!!)) IRDataType.FLOAT -> memory.setFloat(i.address!!, registers.getFloat(i.fpReg1!!)) } nextPc() } private fun InsSTOREI(i: IRInstruction) { when (i.type!!) { IRDataType.BYTE -> memory.setUB(registers.getUW(i.reg2!!).toInt(), registers.getUB(i.reg1!!)) IRDataType.WORD -> memory.setUW(registers.getUW(i.reg2!!).toInt(), registers.getUW(i.reg1!!)) IRDataType.FLOAT -> memory.setFloat(registers.getUW(i.reg1!!).toInt(), registers.getFloat(i.fpReg1!!)) } nextPc() } private fun InsSTOREX(i: IRInstruction) { when (i.type!!) { IRDataType.BYTE -> memory.setUB(i.address!! + registers.getUB(i.reg2!!).toInt(), registers.getUB(i.reg1!!)) IRDataType.WORD -> memory.setUW(i.address!! + registers.getUB(i.reg2!!).toInt(), registers.getUW(i.reg1!!)) IRDataType.FLOAT -> memory.setFloat(i.address!! + registers.getUB(i.reg1!!).toInt(), registers.getFloat(i.fpReg1!!)) } nextPc() } private fun InsSTOREIX(i: IRInstruction) { when (i.type!!) { IRDataType.BYTE -> { val pointer = memory.getUW(i.address!!) + registers.getUB(i.reg2!!) memory.setUB(pointer.toInt(), registers.getUB(i.reg1!!)) } IRDataType.WORD -> { val pointer = memory.getUW(i.address!!) + registers.getUB(i.reg2!!) memory.setUW(pointer.toInt(), registers.getUW(i.reg1!!)) } IRDataType.FLOAT -> { val pointer = memory.getUW(i.address!!) + registers.getUB(i.reg1!!) memory.setFloat(pointer.toInt(), registers.getFloat(i.fpReg1!!)) } } nextPc() } private fun InsSTOREZM(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> memory.setUB(i.address!!, 0u) IRDataType.WORD -> memory.setUW(i.address!!, 0u) IRDataType.FLOAT -> memory.setFloat(i.address!!, 0.0) } nextPc() } private fun InsSTOREZI(i: IRInstruction) { when (i.type!!) { IRDataType.BYTE -> memory.setUB(registers.getUW(i.reg1!!).toInt(), 0u) IRDataType.WORD -> memory.setUW(registers.getUW(i.reg1!!).toInt(), 0u) IRDataType.FLOAT -> memory.setFloat(registers.getUW(i.reg1!!).toInt(), 0.0) } nextPc() } private fun InsSTOREZX(i: IRInstruction) { when (i.type!!) { IRDataType.BYTE -> memory.setUB(i.address!! + registers.getUB(i.reg1!!).toInt(), 0u) IRDataType.WORD -> memory.setUW(i.address!! + registers.getUB(i.reg1!!).toInt(), 0u) IRDataType.FLOAT -> memory.setFloat(i.address!! + registers.getUB(i.reg1!!).toInt(), 0.0) } nextPc() } private fun InsJUMP(i: IRInstruction) { branchTo(i) } private fun InsJUMPI(i: IRInstruction) { val artificialAddress = memory.getUW(i.address!!).toInt() pcChunk = artificialLabelAddresses.getValue(artificialAddress) pcIndex = 0 } private class SyscallParamValue(var dt: IRDataType?, var value: Comparable<*>?) private val syscallParams = Array(100) { SyscallParamValue(null, null) } 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)) } } // store the call site and jump callStack.push(CallSiteContext(pcChunk, pcIndex+1, i.fcallArgs!!)) branchTo(i) } private fun InsRETURN() { if(callStack.isEmpty()) exit(0) else { val context = callStack.pop() pcChunk = context.returnChunk pcIndex = context.returnIndex // ignore any return values. } } private fun InsRETURNR(i: IRInstruction) { if(callStack.isEmpty()) exit(0) else { val context = callStack.pop() val returns = context.fcallSpec.returns when (i.type!!) { IRDataType.BYTE -> { if(returns.isNotEmpty()) registers.setUB(returns.single().registerNum, registers.getUB(i.reg1!!)) else { val callInstr = context.returnChunk.instructions[context.returnIndex-1] if(callInstr.opcode!=Opcode.CALL) throw IllegalArgumentException("missing return value reg") } } IRDataType.WORD -> { if(returns.isNotEmpty()) registers.setUW(returns.single().registerNum, registers.getUW(i.reg1!!)) else { val callInstr = context.returnChunk.instructions[context.returnIndex-1] if(callInstr.opcode!=Opcode.CALL) throw IllegalArgumentException("missing return value reg") } } IRDataType.FLOAT -> { if(returns.isNotEmpty()) registers.setFloat(returns.single().registerNum, registers.getFloat(i.fpReg1!!)) else { val callInstr = context.returnChunk.instructions[context.returnIndex-1] if(callInstr.opcode!=Opcode.CALL) throw IllegalArgumentException("missing return value reg") } } } pcChunk = context.returnChunk pcIndex = context.returnIndex } } private fun InsBSTCC(i: IRInstruction) { if(!statusCarry) branchTo(i) else nextPc() } private fun InsBSTCS(i: IRInstruction) { if(statusCarry) branchTo(i) else nextPc() } private fun InsBSTEQ(i: IRInstruction) { if(statusZero) branchTo(i) else nextPc() } private fun InsBSTNE(i: IRInstruction) { if(!statusZero) branchTo(i) else nextPc() } private fun InsBSTNEG(i: IRInstruction) { if(statusNegative) branchTo(i) else nextPc() } private fun InsBSTPOS(i: IRInstruction) { if(!statusNegative) branchTo(i) else nextPc() } private fun InsBGTR(i: IRInstruction) { val (left: UInt, right: UInt) = getBranchOperandsU(i) if(left>right) branchTo(i) else nextPc() } private fun InsBGT(i: IRInstruction) { val (left: UInt, right: UInt) = getBranchOperandsImmU(i) if(left>right) branchTo(i) else nextPc() } private fun InsBLT(i: IRInstruction) { val (left: UInt, right: UInt) = getBranchOperandsImmU(i) if(leftright) branchTo(i) else nextPc() } private fun InsBGTS(i: IRInstruction) { val (left: Int, right: Int) = getBranchOperandsImm(i) if(left>right) branchTo(i) else nextPc() } private fun InsBLTS(i: IRInstruction) { val (left: Int, right: Int) = getBranchOperandsImm(i) if(left=right) branchTo(i) else nextPc() } private fun InsBGE(i: IRInstruction) { val (left: UInt, right: UInt) = getBranchOperandsImmU(i) if(left>=right) branchTo(i) else nextPc() } private fun InsBLE(i: IRInstruction) { val (left: UInt, right: UInt) = getBranchOperandsImmU(i) if(left<=right) branchTo(i) else nextPc() } private fun InsBGESR(i: IRInstruction) { val (left: Int, right: Int) = getBranchOperands(i) if(left>=right) branchTo(i) else nextPc() } private fun InsBGES(i: IRInstruction) { val (left: Int, right: Int) = getBranchOperandsImm(i) if(left>=right) branchTo(i) else nextPc() } private fun InsBLES(i: IRInstruction) { val (left: Int, right: Int) = getBranchOperandsImm(i) if(left<=right) branchTo(i) else nextPc() } private fun InsSCC(i: IRInstruction) { setResultReg(i.reg1!!, if(statusCarry) 0 else 1, i.type!!) nextPc() } private fun InsSCS(i: IRInstruction) { setResultReg(i.reg1!!, if(statusCarry) 1 else 0, i.type!!) nextPc() } private fun InsSZ(i: IRInstruction) { val right = when(i.type) { IRDataType.BYTE -> registers.getSB(i.reg2!!).toInt() IRDataType.WORD -> registers.getSW(i.reg2!!).toInt() IRDataType.FLOAT -> throw IllegalArgumentException("can't use float here") null -> throw IllegalArgumentException("need type for branch instruction") } val value = if(right==0) 1 else 0 setResultReg(i.reg1!!, value, i.type!!) nextPc() } private fun InsSNZ(i: IRInstruction) { val right = when(i.type) { IRDataType.BYTE -> registers.getSB(i.reg2!!).toInt() IRDataType.WORD -> registers.getSW(i.reg2!!).toInt() IRDataType.FLOAT -> throw IllegalArgumentException("can't use float here") null -> throw IllegalArgumentException("need type for branch instruction") } val value = if(right!=0) 1 else 0 setResultReg(i.reg1!!, value, i.type!!) nextPc() } private fun InsSEQ(i: IRInstruction) { val (left: Int, right: Int) = getSetOnConditionOperands(i) val value = if(left==right) 1 else 0 setResultReg(i.reg1!!, value, i.type!!) nextPc() } private fun InsSNE(i: IRInstruction) { val (left: Int, right: Int) = getSetOnConditionOperands(i) val value = if(left!=right) 1 else 0 setResultReg(i.reg1!!, value, i.type!!) nextPc() } private fun InsSLT(i: IRInstruction) { val (left, right) = getSetOnConditionOperandsU(i) val value = if(leftright) 1 else 0 setResultReg(i.reg1!!, value, i.type!!) nextPc() } private fun InsSGTS(i: IRInstruction) { val (left, right) = getSetOnConditionOperands(i) val value = if(left>right) 1 else 0 setResultReg(i.reg1!!, value, i.type!!) nextPc() } private fun InsSLE(i: IRInstruction) { val (left, right) = getSetOnConditionOperandsU(i) val value = if(left<=right) 1 else 0 setResultReg(i.reg1!!, value, i.type!!) nextPc() } private fun InsSLES(i: IRInstruction) { val (left, right) = getSetOnConditionOperands(i) val value = if(left<=right) 1 else 0 setResultReg(i.reg1!!, value, i.type!!) nextPc() } private fun InsSGE(i: IRInstruction) { val (left, right) = getSetOnConditionOperandsU(i) val value = if(left>=right) 1 else 0 setResultReg(i.reg1!!, value, i.type!!) nextPc() } private fun InsSGES(i: IRInstruction) { val (left, right) = getSetOnConditionOperands(i) val value = if(left>=right) 1 else 0 setResultReg(i.reg1!!, value, i.type!!) nextPc() } private fun InsINC(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> { val value = (registers.getUB(i.reg1!!)+1u).toUByte() registers.setUB(i.reg1!!, value) statusbitsNZ(value.toInt(), i.type!!) } IRDataType.WORD -> { val value = (registers.getUW(i.reg1!!)+1u).toUShort() registers.setUW(i.reg1!!, value) statusbitsNZ(value.toInt(), i.type!!) } IRDataType.FLOAT -> registers.setFloat(i.fpReg1!!, registers.getFloat(i.fpReg1!!)+1f) } nextPc() } private fun InsINCM(i: IRInstruction) { val address = i.address!! when(i.type!!) { IRDataType.BYTE -> { val value = (memory.getUB(address)+1u).toUByte() memory.setUB(address, value) statusbitsNZ(value.toInt(), i.type!!) } IRDataType.WORD -> { val value = (memory.getUW(address)+1u).toUShort() memory.setUW(address, value) statusbitsNZ(value.toInt(), i.type!!) } IRDataType.FLOAT -> memory.setFloat(address, memory.getFloat(address)+1f) } nextPc() } private fun InsDEC(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> { val value = (registers.getUB(i.reg1!!)-1u).toUByte() registers.setUB(i.reg1!!, value) statusbitsNZ(value.toInt(), i.type!!) } IRDataType.WORD -> { val value = (registers.getUW(i.reg1!!)-1u).toUShort() registers.setUW(i.reg1!!, value) statusbitsNZ(value.toInt(), i.type!!) } IRDataType.FLOAT -> registers.setFloat(i.fpReg1!!, registers.getFloat(i.fpReg1!!)-1f) } nextPc() } private fun InsDECM(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> { val value = (memory.getUB(i.address!!)-1u).toUByte() memory.setUB(i.address!!, value) statusbitsNZ(value.toInt(), i.type!!) } IRDataType.WORD -> { val value = (memory.getUW(i.address!!)-1u).toUShort() memory.setUW(i.address!!, value) statusbitsNZ(value.toInt(), i.type!!) } IRDataType.FLOAT -> memory.setFloat(i.address!!, memory.getFloat(i.address!!)-1f) } nextPc() } private fun InsNEG(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> { val value = -registers.getUB(i.reg1!!).toInt() registers.setUB(i.reg1!!, value.toUByte()) statusbitsNZ(value, IRDataType.BYTE) } IRDataType.WORD -> { val value = -registers.getUW(i.reg1!!).toInt() registers.setUW(i.reg1!!, value.toUShort()) statusbitsNZ(value, IRDataType.WORD) } IRDataType.FLOAT -> registers.setFloat(i.fpReg1!!, -registers.getFloat(i.fpReg1!!)) } nextPc() } private fun InsNEGM(i: IRInstruction) { val address = i.address!! when(i.type!!) { IRDataType.BYTE -> { val value = -memory.getUB(address).toInt() memory.setUB(address, value.toUByte()) statusbitsNZ(value, IRDataType.BYTE) } IRDataType.WORD -> { val value = -memory.getUW(address).toInt() memory.setUW(address, value.toUShort()) statusbitsNZ(value, IRDataType.WORD) } IRDataType.FLOAT -> memory.setFloat(address, -memory.getFloat(address)) } nextPc() } private fun InsADDR(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> plusMinusMultAnyByte("+", i.reg1!!, i.reg2!!) IRDataType.WORD -> plusMinusMultAnyWord("+", i.reg1!!, i.reg2!!) IRDataType.FLOAT -> { val left = registers.getFloat(i.fpReg1!!) val right = registers.getFloat(i.fpReg2!!) val result = arithFloat(left, "+", right) registers.setFloat(i.fpReg1!!, result) } } nextPc() } private fun InsADD(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> plusMinusMultConstByte("+", i.reg1!!, i.immediate!!.toUByte()) IRDataType.WORD -> plusMinusMultConstWord("+", i.reg1!!, i.immediate!!.toUShort()) IRDataType.FLOAT -> { val left = registers.getFloat(i.fpReg1!!) val result = arithFloat(left, "+", i.immediateFp!!) registers.setFloat(i.fpReg1!!, result) } } nextPc() } private fun InsADDM(i: IRInstruction) { val address = i.address!! when(i.type!!) { IRDataType.BYTE -> plusMinusMultAnyByteInplace("+", i.reg1!!, address) IRDataType.WORD -> plusMinusMultAnyWordInplace("+", i.reg1!!, address) IRDataType.FLOAT -> { val left = memory.getFloat(address) val right = registers.getFloat(i.fpReg1!!) val result = arithFloat(left, "+", right) memory.setFloat(address, result) } } nextPc() } private fun InsSUBR(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> plusMinusMultAnyByte("-", i.reg1!!, i.reg2!!) IRDataType.WORD -> plusMinusMultAnyWord("-", i.reg1!!, i.reg2!!) IRDataType.FLOAT -> { val left = registers.getFloat(i.fpReg1!!) val right = registers.getFloat(i.fpReg2!!) val result = arithFloat(left, "-", right) registers.setFloat(i.fpReg1!!, result) } } nextPc() } private fun InsSUB(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> plusMinusMultConstByte("-", i.reg1!!, i.immediate!!.toUByte()) IRDataType.WORD -> plusMinusMultConstWord("-", i.reg1!!, i.immediate!!.toUShort()) IRDataType.FLOAT -> { val left = registers.getFloat(i.fpReg1!!) val result = arithFloat(left, "-", i.immediateFp!!) registers.setFloat(i.fpReg1!!, result) } } nextPc() } private fun InsSUBM(i: IRInstruction) { val address = i.address!! when(i.type!!) { IRDataType.BYTE -> plusMinusMultAnyByteInplace("-", i.reg1!!, address) IRDataType.WORD -> plusMinusMultAnyWordInplace("-", i.reg1!!, address) IRDataType.FLOAT -> { val left = memory.getFloat(address) val right = registers.getFloat(i.fpReg1!!) val result = arithFloat(left, "-", right) memory.setFloat(address, result) } } nextPc() } private fun InsMULR(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> plusMinusMultAnyByte("*", i.reg1!!, i.reg2!!) IRDataType.WORD -> plusMinusMultAnyWord("*", i.reg1!!, i.reg2!!) IRDataType.FLOAT -> { val left = registers.getFloat(i.fpReg1!!) val right = registers.getFloat(i.fpReg2!!) val result = arithFloat(left, "*", right) registers.setFloat(i.fpReg1!!, result) } } nextPc() } private fun InsMUL(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> plusMinusMultConstByte("*", i.reg1!!, i.immediate!!.toUByte()) IRDataType.WORD -> plusMinusMultConstWord("*", i.reg1!!, i.immediate!!.toUShort()) IRDataType.FLOAT -> { val left = registers.getFloat(i.fpReg1!!) val result = arithFloat(left, "*", i.immediateFp!!) registers.setFloat(i.fpReg1!!, result) } } nextPc() } private fun InsMULM(i: IRInstruction) { val address = i.address!! when(i.type!!) { IRDataType.BYTE -> plusMinusMultAnyByteInplace("*", i.reg1!!, address) IRDataType.WORD -> plusMinusMultAnyWordInplace("*", i.reg1!!, address) IRDataType.FLOAT -> { val left = memory.getFloat(address) val right = registers.getFloat(i.fpReg1!!) val result = arithFloat(left, "*", right) memory.setFloat(address, result) } } nextPc() } private fun InsDIVR(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> divOrModByteUnsigned("/", i.reg1!!, i.reg2!!) IRDataType.WORD -> divOrModWordUnsigned("/", i.reg1!!, i.reg2!!) IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } nextPc() } private fun InsDIV(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> divOrModConstByteUnsigned("/", i.reg1!!, i.immediate!!.toUByte()) IRDataType.WORD -> divOrModConstWordUnsigned("/", i.reg1!!, i.immediate!!.toUShort()) IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } nextPc() } private fun InsDIVM(i: IRInstruction) { val address = i.address!! when(i.type!!) { IRDataType.BYTE -> divModByteUnsignedInplace("/", i.reg1!!, address) IRDataType.WORD -> divModWordUnsignedInplace("/", i.reg1!!, address) IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } nextPc() } private fun InsDIVSR(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> divModByteSigned("/", i.reg1!!, i.reg2!!) IRDataType.WORD -> divModWordSigned("/", i.reg1!!, i.reg2!!) IRDataType.FLOAT -> { val left = registers.getFloat(i.fpReg1!!) val right = registers.getFloat(i.fpReg2!!) val result = arithFloat(left, "/", right) registers.setFloat(i.fpReg1!!, result) } } nextPc() } private fun InsDIVS(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> divModConstByteSigned("/", i.reg1!!, i.immediate!!.toByte()) IRDataType.WORD -> divModConstWordSigned("/", i.reg1!!, i.immediate!!.toShort()) IRDataType.FLOAT -> { val left = registers.getFloat(i.fpReg1!!) val result = arithFloat(left, "/", i.immediateFp!!) registers.setFloat(i.fpReg1!!, result) } } nextPc() } private fun InsDIVSM(i: IRInstruction) { val address = i.address!! when(i.type!!) { IRDataType.BYTE -> divModByteSignedInplace("/", i.reg1!!, address) IRDataType.WORD -> divModWordSignedInplace("/", i.reg1!!, address) IRDataType.FLOAT -> { val left = memory.getFloat(address) val right = registers.getFloat(i.fpReg1!!) val result = arithFloat(left, "/", right) memory.setFloat(address, result) } } nextPc() } private fun InsMODR(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> divOrModByteUnsigned("%", i.reg1!!, i.reg2!!) IRDataType.WORD -> divOrModWordUnsigned("%", i.reg1!!, i.reg2!!) IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } nextPc() } private fun InsMOD(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> divOrModConstByteUnsigned("%", i.reg1!!, i.immediate!!.toUByte()) IRDataType.WORD -> divOrModConstWordUnsigned("%", i.reg1!!, i.immediate!!.toUShort()) IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } nextPc() } private fun InsDIVMODR(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> divAndModUByte(i.reg1!!, i.reg2!!) // division+remainder results on value stack IRDataType.WORD -> divAndModUWord(i.reg1!!, i.reg2!!) // division+remainder results on value stack IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } nextPc() } private fun InsDIVMOD(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> divAndModConstUByte(i.reg1!!, i.immediate!!.toUByte()) // division+remainder results on value stack IRDataType.WORD -> divAndModConstUWord(i.reg1!!, i.immediate!!.toUShort()) // division+remainder results on value stack IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } nextPc() } private fun InsSGN(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> registers.setSB(i.reg1!!, registers.getSB(i.reg2!!).toInt().sign.toByte()) IRDataType.WORD -> registers.setSB(i.reg1!!, registers.getSW(i.reg2!!).toInt().sign.toByte()) IRDataType.FLOAT -> registers.setSB(i.reg1!!, registers.getFloat(i.fpReg1!!).sign.toInt().toByte()) } nextPc() } private fun InsSQRT(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> registers.setUB(i.reg1!!, sqrt(registers.getUB(i.reg2!!).toDouble()).toInt().toUByte()) IRDataType.WORD -> registers.setUB(i.reg1!!, sqrt(registers.getUW(i.reg2!!).toDouble()).toInt().toUByte()) IRDataType.FLOAT -> registers.setFloat(i.fpReg1!!, sqrt(registers.getFloat(i.fpReg2!!))) } nextPc() } private fun InsSQUARE(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> { val value = registers.getUB(i.reg2!!).toDouble().toInt() registers.setUB(i.reg1!!, (value*value).toUByte()) } IRDataType.WORD -> { val value = registers.getUW(i.reg2!!).toDouble().toInt() registers.setUW(i.reg1!!, (value*value).toUShort()) } IRDataType.FLOAT -> { val value = registers.getFloat(i.fpReg2!!) registers.setFloat(i.fpReg1!!, value*value) } } nextPc() } private fun InsCMP(i: IRInstruction) { val comparison = when(i.type!!) { IRDataType.BYTE -> { val reg1 = registers.getUB(i.reg1!!) val reg2 = registers.getUB(i.reg2!!) reg1.toInt() - reg2.toInt() } IRDataType.WORD -> { val reg1 = registers.getUW(i.reg1!!) val reg2 = registers.getUW(i.reg2!!) reg1.toInt() - reg2.toInt() } IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } statusbitsComparison(comparison, i.type!!) nextPc() } private fun InsCMPI(i: IRInstruction) { val comparison = when(i.type!!) { IRDataType.BYTE -> { val reg1 = registers.getUB(i.reg1!!) reg1.toInt() - (i.immediate!! and 255) } IRDataType.WORD -> { val reg1 = registers.getUW(i.reg1!!) reg1.toInt() - (i.immediate!! and 65535) } IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } statusbitsComparison(comparison, i.type!!) nextPc() } private fun statusbitsNZ(value: Int, type: IRDataType) { statusZero = value==0 when(type) { IRDataType.BYTE -> statusNegative = (value and 0x80)==0x80 IRDataType.WORD -> statusNegative = (value and 0x8000)==0x8000 IRDataType.FLOAT -> { /* floats don't change the status bits */ } } } private fun statusbitsComparison(comparison: Int, type: IRDataType) { if(comparison==0) { statusZero = true statusCarry = true } else if(comparison>0) { statusZero = false statusCarry = true } else { statusZero = false statusCarry = false } when(type) { IRDataType.BYTE -> statusNegative = (comparison and 0x80)!=0 IRDataType.WORD -> statusNegative = (comparison and 0x8000)!=0 IRDataType.FLOAT -> { /* floats don't change the status bits */ } } // TODO statusOverflow } private fun plusMinusMultAnyByte(operator: String, reg1: Int, reg2: Int) { val left = registers.getUB(reg1) val right = registers.getUB(reg2) val result = when(operator) { "+" -> left + right "-" -> left - right "*" -> left * right else -> throw IllegalArgumentException("operator byte $operator") } registers.setUB(reg1, result.toUByte()) } private fun plusMinusMultConstByte(operator: String, reg1: Int, value: UByte) { val left = registers.getUB(reg1) val result = when(operator) { "+" -> left + value "-" -> left - value "*" -> left * value else -> throw IllegalArgumentException("operator byte $operator") } registers.setUB(reg1, result.toUByte()) } private fun plusMinusMultAnyByteInplace(operator: String, reg1: Int, address: Int) { val memvalue = memory.getUB(address) val operand = registers.getUB(reg1) val result = when(operator) { "+" -> memvalue + operand "-" -> memvalue - operand "*" -> memvalue * operand else -> throw IllegalArgumentException("operator byte $operator") } memory.setUB(address, result.toUByte()) } private fun divModByteSigned(operator: String, reg1: Int, reg2: Int) { val left = registers.getSB(reg1) val right = registers.getSB(reg2) val result = when(operator) { "/" -> { if(right==0.toByte()) 127 else left / right } "%" -> { if(right==0.toByte()) 127 else left % right } else -> throw IllegalArgumentException("operator byte $operator") } registers.setSB(reg1, result.toByte()) } private fun divModConstByteSigned(operator: String, reg1: Int, value: Byte) { val left = registers.getSB(reg1) val result = when(operator) { "/" -> { if(value==0.toByte()) 127 else left / value } "%" -> { if(value==0.toByte()) 127 else left % value } else -> throw IllegalArgumentException("operator byte $operator") } registers.setSB(reg1, result.toByte()) } private fun divModByteSignedInplace(operator: String, reg1: Int, address: Int) { val left = memory.getSB(address) val right = registers.getSB(reg1) val result = when(operator) { "/" -> { if(right==0.toByte()) 127 else left / right } "%" -> { if(right==0.toByte()) 127 else left % right } else -> throw IllegalArgumentException("operator byte $operator") } memory.setSB(address, result.toByte()) } private fun divOrModByteUnsigned(operator: String, reg1: Int, reg2: Int) { val left = registers.getUB(reg1) val right = registers.getUB(reg2) val result = when(operator) { "/" -> { if(right==0.toUByte()) 0xffu else left / right } "%" -> { if(right==0.toUByte()) 0u else left % right } else -> throw IllegalArgumentException("operator byte $operator") } registers.setUB(reg1, result.toUByte()) } private fun divOrModConstByteUnsigned(operator: String, reg1: Int, value: UByte) { val left = registers.getUB(reg1) val result = when(operator) { "/" -> { if(value==0.toUByte()) 0xffu else left / value } "%" -> { if(value==0.toUByte()) 0u else left % value } else -> throw IllegalArgumentException("operator byte $operator") } registers.setUB(reg1, result.toUByte()) } private fun divAndModUByte(reg1: Int, reg2: Int) { val left = registers.getUB(reg1) val right = registers.getUB(reg2) val division = if(right==0.toUByte()) 0xffu else left / right val remainder = if(right==0.toUByte()) 0u else left % right valueStack.push(division.toUByte()) valueStack.push(remainder.toUByte()) } private fun divAndModConstUByte(reg1: Int, value: UByte) { val left = registers.getUB(reg1) val division = if(value==0.toUByte()) 0xffu else left / value val remainder = if(value==0.toUByte()) 0u else left % value valueStack.push(division.toUByte()) valueStack.push(remainder.toUByte()) } private fun divAndModUWord(reg1: Int, reg2: Int) { val left = registers.getUW(reg1) val right = registers.getUW(reg2) val division = if(right==0.toUShort()) 0xffffu else left / right val remainder = if(right==0.toUShort()) 0u else left % right valueStack.pushw(division.toUShort()) valueStack.pushw(remainder.toUShort()) } private fun divAndModConstUWord(reg1: Int, value: UShort) { val left = registers.getUW(reg1) val division = if(value==0.toUShort()) 0xffffu else left / value val remainder = if(value==0.toUShort()) 0u else left % value valueStack.pushw(division.toUShort()) valueStack.pushw(remainder.toUShort()) } private fun divModByteUnsignedInplace(operator: String, reg1: Int, address: Int) { val left = memory.getUB(address) val right = registers.getUB(reg1) val result = when(operator) { "/" -> { if(right==0.toUByte()) 0xffu else left / right } "%" -> { if(right==0.toUByte()) 0u else left % right } else -> throw IllegalArgumentException("operator byte $operator") } memory.setUB(address, result.toUByte()) } private fun plusMinusMultAnyWord(operator: String, reg1: Int, reg2: Int) { val left = registers.getUW(reg1) val right = registers.getUW(reg2) val result: UInt when(operator) { "+" -> result = left + right "-" -> result = left - right "*" -> { result = left.toUInt() * right mul16_last_upper = result shr 16 } else -> throw IllegalArgumentException("operator word $operator") } registers.setUW(reg1, result.toUShort()) } private fun plusMinusMultConstWord(operator: String, reg1: Int, value: UShort) { val left = registers.getUW(reg1) val result: UInt when(operator) { "+" -> result = left + value "-" -> result = left - value "*" -> { result = left.toUInt() * value mul16_last_upper = result shr 16 } else -> throw IllegalArgumentException("operator word $operator") } registers.setUW(reg1, result.toUShort()) } private fun plusMinusMultAnyWordInplace(operator: String, reg1: Int, address: Int) { val memvalue = memory.getUW(address) val operand = registers.getUW(reg1) val result: UInt when(operator) { "+" -> result = memvalue + operand "-" -> result = memvalue - operand "*" -> { result = memvalue.toUInt() * operand mul16_last_upper = result shr 16 } else -> throw IllegalArgumentException("operator word $operator") } memory.setUW(address, result.toUShort()) } private fun divOrModWordUnsigned(operator: String, reg1: Int, reg2: Int) { val left = registers.getUW(reg1) val right = registers.getUW(reg2) val result = when(operator) { "/" -> { if(right==0.toUShort()) 0xffffu else left / right } "%" -> { if(right==0.toUShort()) 0u else left % right } else -> throw IllegalArgumentException("operator word $operator") } registers.setUW(reg1, result.toUShort()) } private fun divOrModConstWordUnsigned(operator: String, reg1: Int, value: UShort) { val left = registers.getUW(reg1) val result = when(operator) { "/" -> { if(value==0.toUShort()) 0xffffu else left / value } "%" -> { if(value==0.toUShort()) 0u else left % value } else -> throw IllegalArgumentException("operator word $operator") } registers.setUW(reg1, result.toUShort()) } private fun divModWordUnsignedInplace(operator: String, reg1: Int, address: Int) { val left = memory.getUW(address) val right = registers.getUW(reg1) val result = when(operator) { "/" -> { if(right==0.toUShort()) 0xffffu else left / right } "%" -> { if(right==0.toUShort()) 0u else left % right } else -> throw IllegalArgumentException("operator word $operator") } memory.setUW(address, result.toUShort()) } private fun divModWordSigned(operator: String, reg1: Int, reg2: Int) { val left = registers.getSW(reg1) val right = registers.getSW(reg2) val result = when(operator) { "/" -> { if(right==0.toShort()) 32767 else left / right } "%" -> { if(right==0.toShort()) 32767 else left % right } else -> throw IllegalArgumentException("operator word $operator") } registers.setSW(reg1, result.toShort()) } private fun divModConstWordSigned(operator: String, reg1: Int, value: Short) { val left = registers.getSW(reg1) val result = when(operator) { "/" -> { if(value==0.toShort()) 32767 else left / value } "%" -> { if(value==0.toShort()) 32767 else left % value } else -> throw IllegalArgumentException("operator word $operator") } registers.setSW(reg1, result.toShort()) } private fun divModWordSignedInplace(operator: String, reg1: Int, address: Int) { val left = memory.getSW(address) val right = registers.getSW(reg1) val result = when(operator) { "/" -> { if(right==0.toShort()) 32767 else left / right } "%" -> { if(right==0.toShort()) 32767 else left % right } else -> throw IllegalArgumentException("operator word $operator") } memory.setSW(address, result.toShort()) } private fun arithFloat(left: Double, operator: String, right: Double): Double = when(operator) { "+" -> left + right "-" -> left - right "*" -> left * right "/" -> { if(right==0.0) Double.MAX_VALUE else left / right } "%" -> { if(right==0.0) Double.MAX_VALUE else left % right } else -> throw IllegalArgumentException("operator word $operator") } private fun InsEXT(i: IRInstruction) { when(i.type!!){ IRDataType.BYTE -> registers.setUW(i.reg1!!, registers.getUB(i.reg2!!).toUShort()) IRDataType.WORD -> throw IllegalArgumentException("ext.w not yet supported, requires 32 bits registers") IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } nextPc() } private fun InsEXTS(i: IRInstruction) { when(i.type!!){ IRDataType.BYTE -> registers.setSW(i.reg1!!, registers.getSB(i.reg2!!).toShort()) IRDataType.WORD -> throw IllegalArgumentException("exts.w not yet supported, requires 32 bits registers") IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } nextPc() } private fun InsANDR(i: IRInstruction) { val (left: UInt, right: UInt) = getLogicalOperandsU(i) val value = (left and right).toInt() when(i.type!!) { IRDataType.BYTE -> registers.setUB(i.reg1!!, value.toUByte()) IRDataType.WORD -> registers.setUW(i.reg1!!, value.toUShort()) IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } statusbitsNZ(value, i.type!!) nextPc() } private fun InsAND(i: IRInstruction) { val value: Int when(i.type!!) { IRDataType.BYTE -> { value = registers.getUB(i.reg1!!).toInt() and i.immediate!! registers.setUB(i.reg1!!, value.toUByte()) } IRDataType.WORD -> { value = registers.getUW(i.reg1!!).toInt() and i.immediate!! registers.setUW(i.reg1!!, value.toUShort()) } IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } statusbitsNZ(value, i.type!!) nextPc() } private fun InsANDM(i: IRInstruction) { val value: Int val address = i.address!! when(i.type!!) { IRDataType.BYTE -> { val left = memory.getUB(address) val right = registers.getUB(i.reg1!!) value = left.toInt() and right.toInt() memory.setUB(address, value.toUByte()) } IRDataType.WORD -> { val left = memory.getUW(address) val right = registers.getUW(i.reg1!!) value = left.toInt() and right.toInt() memory.setUW(address, value.toUShort()) } IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } statusbitsNZ(value, i.type!!) nextPc() } private fun InsORR(i: IRInstruction) { val (left: UInt, right: UInt) = getLogicalOperandsU(i) val value = (left or right).toInt() when(i.type!!) { IRDataType.BYTE -> registers.setUB(i.reg1!!, value.toUByte()) IRDataType.WORD -> registers.setUW(i.reg1!!, value.toUShort()) IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } statusbitsNZ(value, i.type!!) nextPc() } private fun InsOR(i: IRInstruction) { val value: Int when(i.type!!) { IRDataType.BYTE -> { value = registers.getUB(i.reg1!!).toInt() or i.immediate!! registers.setUB(i.reg1!!, value.toUByte()) } IRDataType.WORD -> { value = registers.getUW(i.reg1!!).toInt() or i.immediate!! registers.setUW(i.reg1!!, value.toUShort()) } IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } statusbitsNZ(value, i.type!!) nextPc() } private fun InsORM(i: IRInstruction) { val value: Int val address = i.address!! when(i.type!!) { IRDataType.BYTE -> { val left = memory.getUB(address) val right = registers.getUB(i.reg1!!) value = left.toInt() or right.toInt() memory.setUB(address, value.toUByte()) } IRDataType.WORD -> { val left = memory.getUW(address) val right = registers.getUW(i.reg1!!) value = left.toInt() or right.toInt() memory.setUW(address, value.toUShort()) } IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } statusbitsNZ(value, i.type!!) nextPc() } private fun InsXORR(i: IRInstruction) { val (left: UInt, right: UInt) = getLogicalOperandsU(i) val value = (left xor right).toInt() when(i.type!!) { IRDataType.BYTE -> registers.setUB(i.reg1!!, value.toUByte()) IRDataType.WORD -> registers.setUW(i.reg1!!, value.toUShort()) IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } statusbitsNZ(value, i.type!!) nextPc() } private fun InsXOR(i: IRInstruction) { val value: Int when(i.type!!) { IRDataType.BYTE -> { value = registers.getUB(i.reg1!!).toInt() xor i.immediate!! registers.setUB(i.reg1!!, value.toUByte()) } IRDataType.WORD -> { value = registers.getUW(i.reg1!!).toInt() xor i.immediate!! registers.setUW(i.reg1!!, value.toUShort()) } IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } statusbitsNZ(value, i.type!!) nextPc() } private fun InsXORM(i: IRInstruction) { val value: Int val address = i.address!! when(i.type!!) { IRDataType.BYTE -> { val left = memory.getUB(address) val right = registers.getUB(i.reg1!!) value = left.toInt() xor right.toInt() memory.setUB(address, value.toUByte()) } IRDataType.WORD -> { val left = memory.getUW(address) val right = registers.getUW(i.reg1!!) value = left.toInt() xor right.toInt() memory.setUW(address, value.toUShort()) } IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } statusbitsNZ(value, i.type!!) nextPc() } private fun InsINV(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> registers.setUB(i.reg1!!, registers.getUB(i.reg1!!).inv()) IRDataType.WORD -> registers.setUW(i.reg1!!, registers.getUW(i.reg1!!).inv()) IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } nextPc() } private fun InsINVM(i: IRInstruction) { val address = i.address!! when(i.type!!) { IRDataType.BYTE -> memory.setUB(address, memory.getUB(address).inv()) IRDataType.WORD -> memory.setUW(address, memory.getUW(address).inv()) IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } nextPc() } private fun InsASRN(i: IRInstruction) { val (left: Int, right: Int) = getLogicalOperandsS(i) statusCarry = (left and 1)!=0 when(i.type!!) { IRDataType.BYTE -> registers.setSB(i.reg1!!, (left shr right).toByte()) IRDataType.WORD -> registers.setSW(i.reg1!!, (left shr right).toShort()) IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } nextPc() } private fun InsASRNM(i: IRInstruction) { val address = i.address!! val operand = registers.getUB(i.reg1!!).toInt() when(i.type!!) { IRDataType.BYTE -> { val memvalue = memory.getSB(address).toInt() statusCarry = (memvalue and 1)!=0 memory.setSB(address, (memvalue shr operand).toByte()) } IRDataType.WORD -> { val memvalue = memory.getSW(address).toInt() statusCarry = (memvalue and 1)!=0 memory.setSW(address, (memvalue shr operand).toShort()) } IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } nextPc() } private fun InsASR(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> { val value = registers.getSB(i.reg1!!).toInt() statusCarry = (value and 1)!=0 registers.setSB(i.reg1!!, (value shr 1).toByte()) } IRDataType.WORD -> { val value = registers.getSW(i.reg1!!).toInt() statusCarry = (value and 1)!=0 registers.setSW(i.reg1!!, (value shr 1).toShort()) } IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } nextPc() } private fun InsASRM(i: IRInstruction) { val address = i.address!! when(i.type!!) { IRDataType.BYTE -> { val value = memory.getSB(address).toInt() statusCarry = (value and 1)!=0 memory.setSB(address, (value shr 1).toByte()) } IRDataType.WORD -> { val value = memory.getSW(address).toInt() statusCarry = (value and 1)!=0 memory.setSW(address, (value shr 1).toShort()) } IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } nextPc() } private fun InsLSRN(i: IRInstruction) { val (left: UInt, right: UInt) = getLogicalOperandsU(i) statusCarry = (left and 1u)!=0u when(i.type!!) { IRDataType.BYTE -> registers.setUB(i.reg1!!, (left shr right.toInt()).toUByte()) IRDataType.WORD -> registers.setUW(i.reg1!!, (left shr right.toInt()).toUShort()) IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } nextPc() } private fun InsLSRNM(i: IRInstruction) { val address = i.address!! val operand = registers.getUB(i.reg1!!).toInt() when(i.type!!) { IRDataType.BYTE -> { val memvalue = memory.getUB(address).toInt() statusCarry = (memvalue and 1)!=0 memory.setUB(address, (memvalue shr operand).toUByte()) } IRDataType.WORD -> { val memvalue = memory.getUW(address).toInt() statusCarry = (memvalue and 1)!=0 memory.setUW(address, (memvalue shr operand).toUShort()) } IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } nextPc() } private fun InsLSR(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> { val value = registers.getUB(i.reg1!!).toInt() statusCarry = (value and 1)!=0 registers.setUB(i.reg1!!, (value shr 1).toUByte()) } IRDataType.WORD -> { val value = registers.getUW(i.reg1!!).toInt() statusCarry = (value and 1)!=0 registers.setUW(i.reg1!!, (value shr 1).toUShort()) } IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } nextPc() } private fun InsLSRM(i: IRInstruction) { val address = i.address!! when(i.type!!) { IRDataType.BYTE -> { val value = memory.getUB(address).toInt() statusCarry = (value and 1)!=0 memory.setUB(address, (value shr 1).toUByte()) } IRDataType.WORD -> { val value = memory.getUW(address).toInt() statusCarry = (value and 1)!=0 memory.setUW(address, (value shr 1).toUShort()) } IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } nextPc() } private fun InsLSLN(i: IRInstruction) { val (left: UInt, right: UInt) = getLogicalOperandsU(i) when(i.type!!) { IRDataType.BYTE -> { statusCarry = (left and 0x80u)!=0u registers.setUB(i.reg1!!, (left shl right.toInt()).toUByte()) } IRDataType.WORD -> { statusCarry = (left and 0x8000u)!=0u registers.setUW(i.reg1!!, (left shl right.toInt()).toUShort()) } IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } nextPc() } private fun InsLSLNM(i: IRInstruction) { val address = i.address!! val operand = registers.getUB(i.reg1!!).toInt() when(i.type!!) { IRDataType.BYTE -> { val memvalue = memory.getUB(address).toInt() statusCarry = (memvalue and 0x80)!=0 memory.setUB(address, (memvalue shl operand).toUByte()) } IRDataType.WORD -> { val memvalue = memory.getUW(address).toInt() statusCarry = (memvalue and 0x8000)!=0 memory.setUW(address, (memvalue shl operand).toUShort()) } IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } nextPc() } private fun InsLSL(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> { val value = registers.getUB(i.reg1!!).toInt() statusCarry = (value and 0x80)!=0 registers.setUB(i.reg1!!, (value shl 1).toUByte()) } IRDataType.WORD -> { val value = registers.getUW(i.reg1!!).toInt() statusCarry = (value and 0x8000)!=0 registers.setUW(i.reg1!!, (value shl 1).toUShort()) } IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } nextPc() } private fun InsLSLM(i: IRInstruction) { val address = i.address!! when(i.type!!) { IRDataType.BYTE -> { val value = memory.getUB(address).toInt() statusCarry = (value and 0x80)!=0 memory.setUB(address, (value shl 1).toUByte()) } IRDataType.WORD -> { val value = memory.getUW(address).toInt() statusCarry = (value and 0x8000)!=0 memory.setUW(address, (value shl 1).toUShort()) } IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } nextPc() } private fun InsROR(i: IRInstruction, useCarry: Boolean) { val newStatusCarry: Boolean when (i.type!!) { IRDataType.BYTE -> { val orig = registers.getUB(i.reg1!!) newStatusCarry = (orig.toInt() and 1) != 0 val rotated: UByte = if (useCarry) { val carry = if (statusCarry) 0x80u else 0x00u (orig.toUInt().rotateRight(1) or carry).toUByte() } else orig.rotateRight(1) registers.setUB(i.reg1!!, rotated) } IRDataType.WORD -> { val orig = registers.getUW(i.reg1!!) newStatusCarry = (orig.toInt() and 1) != 0 val rotated: UShort = if (useCarry) { val carry = if (statusCarry) 0x8000u else 0x0000u (orig.toUInt().rotateRight(1) or carry).toUShort() } else orig.rotateRight(1) registers.setUW(i.reg1!!, rotated) } IRDataType.FLOAT -> { throw IllegalArgumentException("can't ROR a float") } } nextPc() statusCarry = newStatusCarry } private fun InsRORM(i: IRInstruction, useCarry: Boolean) { val newStatusCarry: Boolean val address = i.address!! when (i.type!!) { IRDataType.BYTE -> { val orig = memory.getUB(address) newStatusCarry = (orig.toInt() and 1) != 0 val rotated: UByte = if (useCarry) { val carry = if (statusCarry) 0x80u else 0x00u (orig.toUInt().rotateRight(1) or carry).toUByte() } else orig.rotateRight(1) memory.setUB(address, rotated) } IRDataType.WORD -> { val orig = memory.getUW(address) newStatusCarry = (orig.toInt() and 1) != 0 val rotated: UShort = if (useCarry) { val carry = if (statusCarry) 0x8000u else 0x0000u (orig.toUInt().rotateRight(1) or carry).toUShort() } else orig.rotateRight(1) memory.setUW(address, rotated) } IRDataType.FLOAT -> { throw IllegalArgumentException("can't ROR a float") } } nextPc() statusCarry = newStatusCarry } private fun InsROL(i: IRInstruction, useCarry: Boolean) { val newStatusCarry: Boolean when (i.type!!) { IRDataType.BYTE -> { val orig = registers.getUB(i.reg1!!) newStatusCarry = (orig.toInt() and 0x80) != 0 val rotated: UByte = if (useCarry) { val carry = if (statusCarry) 1u else 0u (orig.toUInt().rotateLeft(1) or carry).toUByte() } else orig.rotateLeft(1) registers.setUB(i.reg1!!, rotated) } IRDataType.WORD -> { val orig = registers.getUW(i.reg1!!) newStatusCarry = (orig.toInt() and 0x8000) != 0 val rotated: UShort = if (useCarry) { val carry = if (statusCarry) 1u else 0u (orig.toUInt().rotateLeft(1) or carry).toUShort() } else orig.rotateLeft(1) registers.setUW(i.reg1!!, rotated) } IRDataType.FLOAT -> { throw IllegalArgumentException("can't ROL a float") } } nextPc() statusCarry = newStatusCarry } private fun InsROLM(i: IRInstruction, useCarry: Boolean) { val address = i.address!! val newStatusCarry: Boolean when (i.type!!) { IRDataType.BYTE -> { val orig = memory.getUB(address) newStatusCarry = (orig.toInt() and 0x80) != 0 val rotated: UByte = if (useCarry) { val carry = if (statusCarry) 1u else 0u (orig.toUInt().rotateLeft(1) or carry).toUByte() } else orig.rotateLeft(1) memory.setUB(address, rotated) } IRDataType.WORD -> { val orig = memory.getUW(address) newStatusCarry = (orig.toInt() and 0x8000) != 0 val rotated: UShort = if (useCarry) { val carry = if (statusCarry) 1u else 0u (orig.toUInt().rotateLeft(1) or carry).toUShort() } else orig.rotateLeft(1) memory.setUW(address, rotated) } IRDataType.FLOAT -> { throw IllegalArgumentException("can't ROL a float") } } nextPc() statusCarry = newStatusCarry } private fun InsMSIG(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> { val value = registers.getUW(i.reg2!!) val newValue = value.toInt() ushr 8 registers.setUB(i.reg1!!, newValue.toUByte()) } IRDataType.WORD -> throw IllegalArgumentException("msig.w not yet supported, requires 32-bits registers") IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } nextPc() } private fun InsCONCAT(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> { val msb = registers.getUB(i.reg2!!) val lsb = registers.getUB(i.reg3!!) registers.setUW(i.reg1!!, ((msb.toInt() shl 8) or lsb.toInt()).toUShort()) } IRDataType.WORD -> throw IllegalArgumentException("concat.w not yet supported, requires 32-bits registers") IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i") } nextPc() } private fun InsFFROMUB(i: IRInstruction) { registers.setFloat(i.fpReg1!!, registers.getUB(i.reg1!!).toDouble()) nextPc() } private fun InsFFROMSB(i: IRInstruction) { registers.setFloat(i.fpReg1!!, registers.getSB(i.reg1!!).toDouble()) nextPc() } private fun InsFFROMUW(i: IRInstruction) { registers.setFloat(i.fpReg1!!, registers.getUW(i.reg1!!).toDouble()) nextPc() } private fun InsFFROMSW(i: IRInstruction) { registers.setFloat(i.fpReg1!!, registers.getSW(i.reg1!!).toDouble()) nextPc() } private fun InsFTOUB(i: IRInstruction) { registers.setUB(i.reg1!!, registers.getFloat(i.fpReg1!!).toInt().toUByte()) nextPc() } private fun InsFTOUW(i: IRInstruction) { registers.setUW(i.reg1!!, registers.getFloat(i.fpReg1!!).toInt().toUShort()) nextPc() } private fun InsFTOSB(i: IRInstruction) { registers.setSB(i.reg1!!, registers.getFloat(i.fpReg1!!).toInt().toByte()) nextPc() } private fun InsFTOSW(i: IRInstruction) { registers.setSW(i.reg1!!, registers.getFloat(i.fpReg1!!).toInt().toShort()) nextPc() } private fun InsFPOW(i: IRInstruction) { val value = registers.getFloat(i.fpReg1!!) val exponent = registers.getFloat(i.fpReg2!!) registers.setFloat(i.fpReg1!!, value.pow(exponent)) nextPc() } private fun InsFSIN(i: IRInstruction) { val angle = registers.getFloat(i.fpReg2!!) registers.setFloat(i.fpReg1!!, sin(angle)) nextPc() } private fun InsFCOS(i: IRInstruction) { val angle = registers.getFloat(i.fpReg2!!) registers.setFloat(i.fpReg1!!, cos(angle)) nextPc() } private fun InsFTAN(i: IRInstruction) { val angle = registers.getFloat(i.fpReg2!!) registers.setFloat(i.fpReg1!!, tan(angle)) nextPc() } private fun InsFATAN(i: IRInstruction) { val angle = registers.getFloat(i.fpReg2!!) registers.setFloat(i.fpReg1!!, atan(angle)) nextPc() } private fun InsFABS(i: IRInstruction) { val value = registers.getFloat(i.fpReg2!!) registers.setFloat(i.fpReg1!!, abs(value)) nextPc() } private fun InsFLN(i: IRInstruction) { val value = registers.getFloat(i.fpReg2!!) registers.setFloat(i.fpReg1!!, ln(value)) nextPc() } private fun InsFLOG(i: IRInstruction) { val value = registers.getFloat(i.fpReg2!!) registers.setFloat(i.fpReg1!!, log2(value)) nextPc() } private fun InsFROUND(i: IRInstruction) { val value = registers.getFloat(i.fpReg2!!) registers.setFloat(i.fpReg1!!, round(value)) nextPc() } private fun InsFFLOOR(i: IRInstruction) { val value = registers.getFloat(i.fpReg2!!) registers.setFloat(i.fpReg1!!, floor(value)) nextPc() } private fun InsFCEIL(i: IRInstruction) { val value = registers.getFloat(i.fpReg2!!) registers.setFloat(i.fpReg1!!, ceil(value)) nextPc() } private fun InsFCOMP(i: IRInstruction) { val left = registers.getFloat(i.fpReg1!!) val right = registers.getFloat(i.fpReg2!!) val result = if(leftright) 1u else 0u registers.setUB(i.reg1!!, result.toUByte()) nextPc() } private fun getBranchOperands(i: IRInstruction): Pair { return when(i.type) { IRDataType.BYTE -> Pair(registers.getSB(i.reg1!!).toInt(), registers.getSB(i.reg2!!).toInt()) IRDataType.WORD -> Pair(registers.getSW(i.reg1!!).toInt(), registers.getSW(i.reg2!!).toInt()) IRDataType.FLOAT -> { throw IllegalArgumentException("can't use float here") } null -> throw IllegalArgumentException("need type for branch instruction") } } private fun getBranchOperandsImm(i: IRInstruction): Pair { return when(i.type) { IRDataType.BYTE -> Pair(registers.getSB(i.reg1!!).toInt(), i.immediate!!) IRDataType.WORD -> Pair(registers.getSW(i.reg1!!).toInt(), i.immediate!!) IRDataType.FLOAT -> { throw IllegalArgumentException("can't use float here") } null -> throw IllegalArgumentException("need type for branch instruction") } } private fun getBranchOperandsU(i: IRInstruction): Pair { return when(i.type) { IRDataType.BYTE -> Pair(registers.getUB(i.reg1!!).toUInt(), registers.getUB(i.reg2!!).toUInt()) IRDataType.WORD -> Pair(registers.getUW(i.reg1!!).toUInt(), registers.getUW(i.reg2!!).toUInt()) IRDataType.FLOAT -> { throw IllegalArgumentException("can't use float here") } null -> throw IllegalArgumentException("need type for branch instruction") } } private fun getBranchOperandsImmU(i: IRInstruction): Pair { return when(i.type) { IRDataType.BYTE -> Pair(registers.getUB(i.reg1!!).toUInt(), i.immediate!!.toUInt()) IRDataType.WORD -> Pair(registers.getUW(i.reg1!!).toUInt(), i.immediate!!.toUInt()) IRDataType.FLOAT -> { throw IllegalArgumentException("can't use float here") } null -> throw IllegalArgumentException("need type for branch instruction") } } private fun getLogicalOperandsU(i: IRInstruction): Pair { return when(i.type) { IRDataType.BYTE -> Pair(registers.getUB(i.reg1!!).toUInt(), registers.getUB(i.reg2!!).toUInt()) IRDataType.WORD -> Pair(registers.getUW(i.reg1!!).toUInt(), registers.getUW(i.reg2!!).toUInt()) IRDataType.FLOAT -> { throw IllegalArgumentException("can't use float here") } null -> throw IllegalArgumentException("need type for logical instruction") } } private fun getLogicalOperandsS(i: IRInstruction): Pair { return when(i.type) { IRDataType.BYTE -> Pair(registers.getSB(i.reg1!!).toInt(), registers.getSB(i.reg2!!).toInt()) IRDataType.WORD -> Pair(registers.getSW(i.reg1!!).toInt(), registers.getSW(i.reg2!!).toInt()) IRDataType.FLOAT -> { throw IllegalArgumentException("can't use float here") } null -> throw IllegalArgumentException("need type for logical instruction") } } private fun getSetOnConditionOperands(ins: IRInstruction): Pair { return when(ins.type) { IRDataType.BYTE -> Pair(registers.getSB(ins.reg2!!).toInt(), registers.getSB(ins.reg3!!).toInt()) IRDataType.WORD -> Pair(registers.getSW(ins.reg2!!).toInt(), registers.getSW(ins.reg3!!).toInt()) IRDataType.FLOAT -> { throw IllegalArgumentException("can't use float here") } null -> throw IllegalArgumentException("need type for branch instruction") } } private fun getSetOnConditionOperandsU(ins: IRInstruction): Pair { return when(ins.type) { IRDataType.BYTE -> Pair(registers.getUB(ins.reg2!!).toUInt(), registers.getUB(ins.reg3!!).toUInt()) IRDataType.WORD -> Pair(registers.getUW(ins.reg2!!).toUInt(), registers.getUW(ins.reg3!!).toUInt()) IRDataType.FLOAT -> { throw IllegalArgumentException("can't use float here") } null -> throw IllegalArgumentException("need type for branch instruction") } } private var window: GraphicsWindow? = null 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") } window!!.start() } fun gfx_clear(color: UByte) { window?.clear(color.toInt()) } fun gfx_plot(x: UShort, y: UShort, color: UByte) { window?.plot(x.toInt(), y.toInt(), color.toInt()) } fun gfx_getpixel(x: UShort, y: UShort): UByte { return if(window==null) 0u else { val color = Color(window!!.getpixel(x.toInt(), y.toInt())) color.green.toUByte() } } fun gfx_close() { window?.close() } fun waitvsync() { Toolkit.getDefaultToolkit().sync() // not really the same as wait on vsync, but there's noting else } fun randomSeed(seed1: UShort, seed2: UShort) { randomGenerator = Random(((seed1.toUInt() shl 16) or seed2.toUInt()).toInt()) } fun randomSeedFloat(seed: Double) { randomGeneratorFloats = Random(seed.toBits()) } } internal fun Stack.pushw(value: UShort) { push((value and 255u).toUByte()) push((value.toInt() ushr 8).toUByte()) } internal fun Stack.pushf(value: Double) { // push float; lsb first, msb last var bits = value.toBits() push(bits.toUByte()) bits = bits ushr 8 push(bits.toUByte()) bits = bits ushr 8 push(bits.toUByte()) bits = bits ushr 8 push(bits.toUByte()) bits = bits ushr 8 push(bits.toUByte()) bits = bits ushr 8 push(bits.toUByte()) bits = bits ushr 8 push(bits.toUByte()) bits = bits ushr 8 push(bits.toUByte()) } internal fun Stack.popw(): UShort { val msb = pop() val lsb = pop() return ((msb.toInt() shl 8) + lsb.toInt()).toUShort() } internal fun Stack.popf(): Double { // pop float; lsb is on bottom, msb on top val b0 = pop().toLong() val b1 = pop().toLong() val b2 = pop().toLong() val b3 = pop().toLong() val b4 = pop().toLong() val b5 = pop().toLong() val b6 = pop().toLong() val b7 = pop().toLong() val bits = b7 + (1L shl 8)*b6 + (1L shl 16)*b5 + (1L shl 24)*b4 + (1L shl 32)*b3 + (1L shl 40)*b2 + (1L shl 48)*b1 + (1L shl 56)*b0 return Double.fromBits(bits) } // probably called via reflection class VmRunner: IVirtualMachineRunner { override fun runProgram(irSource: String) { runAndTestProgram(irSource) { /* no tests */ } } fun runAndTestProgram(irSource: String, test: (VirtualMachine) -> Unit) { val irProgram = IRFileReader().read(irSource) val vm = VirtualMachine(irProgram) // vm.breakpointHandler = { pcChunk, pcIndex -> // println("UNHANDLED BREAKPOINT") // println(" IN CHUNK: $pcChunk(${pcChunk.label}) INDEX: $pcIndex = INSTR ${pcChunk.instructions[pcIndex]}") // } vm.run() test(vm) } }