package mtmc.emulator import mtmc.asm.instructions.Instruction import mtmc.os.MTOS import mtmc.os.fs.FileSystem import mtmc.util.BinaryUtils.getBits import mtmc.util.Runnable import kotlin.experimental.and import kotlin.experimental.inv import kotlin.experimental.or import kotlin.math.max import kotlin.math.min class MonTanaMiniComputer { // core model var registerFile: ShortArray = ShortArray(Register.entries.size)// 16 user visible + the instruction register var memory: ByteArray = ByteArray(MEMORY_SIZE) var breakpoints: ByteArray = ByteArray(MEMORY_SIZE) private var status: ComputerStatus? = ComputerStatus.READY var speed: Int = 1000000 set(speed) { field = speed this.notifyOfExecutionUpdate() } val iO: MTMCIO = MTMCIO() // helpers var os: MTOS = MTOS(this) var console: MTMCConsole = MTMCConsole(this) var display: MTMCDisplay = MTMCDisplay(this) var clock: MTMCClock = MTMCClock(this) var fileSystem: FileSystem = FileSystem(this) var rewindSteps = mutableListOf() // listeners private val observers = mutableListOf() var debugInfo: DebugInfo? = null private var currentRewindStep: RewindStep? = null init { initMemory() } fun initMemory() { registerFile = ShortArray(Register.entries.size) memory = ByteArray(MEMORY_SIZE) breakpoints = ByteArray(MEMORY_SIZE) rewindSteps.clear() setRegisterValue( Register.SP, MEMORY_SIZE.toShort().toInt() ) // default the stack pointer to the top of memory rewindSteps.clear() observers.forEach { obj -> obj.computerReset() } } fun load(code: ByteArray, data: ByteArray, debugInfo: DebugInfo?) { load(code, data, Array(0) { ByteArray(0) }, debugInfo) } fun load(code: ByteArray, data: ByteArray, graphics: Array, debugInfo: DebugInfo?) { this.debugInfo = debugInfo // reset memory initMemory() val codeBoundary = code.size code.copyInto(memory, 0, 0, codeBoundary) setRegisterValue(Register.CB, codeBoundary - 1) val dataBoundary = codeBoundary + data.size code.copyInto(memory, codeBoundary, 0, data.size) setRegisterValue(Register.DB, dataBoundary - 1) // base pointer starts just past the end of the data boundary setRegisterValue(Register.BP, dataBoundary) fetchCurrentInstruction() // fetch the initial instruction for display purposes notifyOfStepExecution() // prepare for step execution display.loadGraphics(graphics) // ready graphics for use // reset computer status setStatus(ComputerStatus.READY) } fun pulse(instructions: Long): Long { var count: Long = 0 var i: Long = 0 while (i < instructions && status == ComputerStatus.EXECUTING) { fetchAndExecute() count++ if (breakpoints[getRegisterValue(Register.PC).toInt()].toInt() != 0) { setStatus(ComputerStatus.BREAK) } i++ } return count } fun run() { setStatus(ComputerStatus.EXECUTING) clock.run() } fun back() { clock.back() } fun step() { clock.step() } fun setStatus(status: ComputerStatus?) { this.status = status this.notifyOfExecutionUpdate() if (status == ComputerStatus.FINISHED || status == ComputerStatus.BREAK) { this.notifyOfStepExecution() } } fun getStatus(): ComputerStatus? { return status } fun fetchAndExecute() { currentRewindStep = RewindStep() currentRewindStep?.let { rewindSteps.add(0, it) } fetchCurrentInstruction() val instruction = getRegisterValue(Register.IR) if (isDoubleWordInstruction(instruction)) { setRegisterValue( Register.PC, (getRegisterValue(Register.PC) + 2 * WORD_SIZE).toShort().toInt() ) } else { setRegisterValue(Register.PC, (getRegisterValue(Register.PC) + WORD_SIZE).toShort().toInt()) } execInstruction(instruction) } fun execInstruction(instruction: Short) { observers!!.forEach({ o: MTMCObserver? -> o!!.beforeExecution(instruction) }) val instructionType: Short = getBits(16, 4, instruction) if (instructionType.toInt() == 0x0000) { // MISC val topNibble: Int = getBits(12, 4, instruction).toInt() when (topNibble) { 0 -> { // sys call val sysCall: Short = getBits(8, 8, instruction) os.handleSysCall(sysCall) } 1 -> { // mov val to: Short = getBits(8, 4, instruction) val from: Short = getBits(4, 4, instruction) val value = getRegisterValue(from.toInt()) setRegisterValue(to.toInt(), value.toInt()) } 2 -> { // inc val target: Short = getBits(8, 4, instruction) val immediateValue: Short = getBits(4, 4, instruction) val registerValue = getRegisterValue(target.toInt()) val value = registerValue + immediateValue setRegisterValue(target.toInt(), value) } 3 -> { // dec val target: Short = getBits(8, 4, instruction) val immediateValue: Short = getBits(4, 4, instruction) val registerValue = getRegisterValue(target.toInt()) val value = registerValue - immediateValue setRegisterValue(target.toInt(), value) } 4 -> { // dec val target: Short = getBits(8, 4, instruction) val immediateValue: Short = getBits(4, 4, instruction) setRegisterValue(target.toInt(), immediateValue.toInt()) } 5 -> { // mcp var source: Short = getBits(8, 4, instruction) source = getRegisterValue(source.toInt()) var dest: Short = getBits(4, 4, instruction) dest = getRegisterValue(dest.toInt()) val size = getRegisterValue(Register.DR).toInt() for (i in 0.. { // debug val debugIndex: Short = getBits(8, 8, instruction) debugInfo!!.handleDebugString(debugIndex, this) } 15 -> { // noop } else -> badInstruction(instruction) } } else if (instructionType.toInt() == 0x0001) { // ALU var opCode: Short = getBits(12, 4, instruction) val targetReg: Short = getBits(8, 4, instruction) val sourceReg: Short if (opCode.toInt() == 15) { // immediate, source is data register, opcode is the lowest nibble sourceReg = Register.DR.ordinal.toShort() opCode = getBits(4, 4, instruction) } else if (opCode.toInt() == 12 || opCode.toInt() == 13 || opCode.toInt() == 14) { // unary sourceReg = targetReg } else { // binary op, source is in the lowest nibble sourceReg = getBits(4, 4, instruction) } val sourceValue = getRegisterValue(sourceReg.toInt()) val targetValue = getRegisterValue(targetReg.toInt()) var result = 0 when (opCode.toInt()) { 0 -> { result = targetValue + sourceValue setRegisterValue(targetReg.toInt(), result) } 1 -> { // sub result = targetValue - sourceValue setRegisterValue(targetReg.toInt(), result) } 2 -> { // mul result = targetValue * sourceValue setRegisterValue(targetReg.toInt(), result) } 3 -> { // div result = targetValue / sourceValue setRegisterValue(targetReg.toInt(), result) } 4 -> { // mod result = targetValue % sourceValue setRegisterValue(targetReg.toInt(), result) } 5 -> { // and result = targetValue.toInt() and sourceValue.toInt() setRegisterValue(targetReg.toInt(), result) } 6 -> { // or result = targetValue.toInt() or sourceValue.toInt() setRegisterValue(targetReg.toInt(), result) } 7 -> { // xor result = targetValue.toInt() xor sourceValue.toInt() setRegisterValue(targetReg.toInt(), result) } 8 -> { // shift left result = targetValue.toInt() shl sourceValue.toInt() setRegisterValue(targetReg.toInt(), result) } 9 -> { result = targetValue.toInt() ushr sourceValue.toInt() setRegisterValue(targetReg.toInt(), result) } 10 -> { result = min(targetValue.toInt(), sourceValue.toInt()) setRegisterValue(targetReg.toInt(), result) } 11 -> { result = max(targetValue.toInt(), sourceValue.toInt()) setRegisterValue(targetReg.toInt(), result) } 12 -> { result = targetValue.inv().toInt() setRegisterValue(targetReg.toInt(), result.toShort().toInt()) } 13 -> { result = if (targetValue.toInt() == 0) 1 else 0 setRegisterValue(targetReg.toInt(), result) } 14 -> { // negate result = -targetValue setRegisterValue(targetReg.toInt(), result.toShort().toInt()) } else -> badInstruction(instruction) } setFlagTestBit(result != 0) } else if (instructionType.toInt() == 2) { val opcode: Short = getBits(12, 4, instruction) val stackReg: Short = getBits(4, 4, instruction) when (opcode.toInt()) { 0 -> { // push val sourceRegister: Short = getBits(8, 4, instruction) // decrement the stack pointer setRegisterValue(stackReg.toInt(), getRegisterValue(stackReg.toInt()) - WORD_SIZE) // write the value out to the location val stackPointerValue = getRegisterValue(stackReg.toInt()) val valueToPush = getRegisterValue(sourceRegister.toInt()) writeWordToMemory(stackPointerValue.toInt(), valueToPush.toInt()) } 1 -> { // pop val targetRegister: Short = getBits(8, 4, instruction) val stackPointerValue = getRegisterValue(stackReg.toInt()) val value = fetchWordFromMemory(stackPointerValue.toInt()) setRegisterValue(targetRegister.toInt(), value.toInt()) setRegisterValue(stackReg.toInt(), getRegisterValue(stackReg.toInt()) + WORD_SIZE) } 2 -> { // dup val currentVal = fetchWordFromMemory(getRegisterValue(stackReg.toInt()).toInt()) setRegisterValue(stackReg.toInt(), getRegisterValue(stackReg.toInt()) - WORD_SIZE) writeWordToMemory(getRegisterValue(stackReg.toInt()).toInt(), currentVal.toInt()) } 3 -> { // swap val currentTop = fetchWordFromMemory(getRegisterValue(stackReg.toInt()).toInt()) val nextDown = fetchWordFromMemory(getRegisterValue(stackReg.toInt()) + WORD_SIZE) writeWordToMemory(getRegisterValue(stackReg.toInt()).toInt(), nextDown.toInt()) writeWordToMemory(getRegisterValue(stackReg.toInt()) + WORD_SIZE, currentTop.toInt()) } 4 -> // drop setRegisterValue(stackReg.toInt(), getRegisterValue(stackReg.toInt()) + WORD_SIZE) 5 -> { // over val nextDown = fetchWordFromMemory(getRegisterValue(stackReg.toInt()) + WORD_SIZE) setRegisterValue(stackReg.toInt(), getRegisterValue(stackReg.toInt()) - WORD_SIZE) // write the value out to the location writeWordToMemory(getRegisterValue(stackReg.toInt()).toInt(), nextDown.toInt()) } 6 -> { // rot val currentTop = fetchWordFromMemory(getRegisterValue(stackReg.toInt()).toInt()) val nextDown = fetchWordFromMemory(getRegisterValue(stackReg.toInt()) + WORD_SIZE) val thirdDown = fetchWordFromMemory(getRegisterValue(stackReg.toInt()) + 2 * WORD_SIZE) writeWordToMemory(getRegisterValue(stackReg.toInt()).toInt(), thirdDown.toInt()) writeWordToMemory(getRegisterValue(stackReg.toInt()) + WORD_SIZE, currentTop.toInt()) writeWordToMemory(getRegisterValue(stackReg.toInt()) + 2 * WORD_SIZE, nextDown.toInt()) } 7 -> { // sop val aluOpCode: Short = getBits(8, 4, instruction) when (aluOpCode.toInt()) { 0 -> { val currentTop = fetchWordFromMemory(getRegisterValue(stackReg.toInt()).toInt()) val nextDown = fetchWordFromMemory(getRegisterValue(stackReg.toInt()) + WORD_SIZE) setRegisterValue(stackReg.toInt(), getRegisterValue(stackReg.toInt()) + WORD_SIZE) val value = nextDown + currentTop writeWordToMemory(getRegisterValue(stackReg.toInt()).toInt(), value) } 1 -> { val currentTop = fetchWordFromMemory(getRegisterValue(stackReg.toInt()).toInt()) val nextDown = fetchWordFromMemory(getRegisterValue(stackReg.toInt()) + WORD_SIZE) setRegisterValue(stackReg.toInt(), getRegisterValue(stackReg.toInt()) + WORD_SIZE) val value = nextDown - currentTop writeWordToMemory(getRegisterValue(stackReg.toInt()).toInt(), value) } 2 -> { val currentTop = fetchWordFromMemory(getRegisterValue(stackReg.toInt()).toInt()) val nextDown = fetchWordFromMemory(getRegisterValue(stackReg.toInt()) + WORD_SIZE) setRegisterValue(stackReg.toInt(), getRegisterValue(stackReg.toInt()) + WORD_SIZE) val value = nextDown * currentTop writeWordToMemory(getRegisterValue(stackReg.toInt()).toInt(), value) } 3 -> { val currentTop = fetchWordFromMemory(getRegisterValue(stackReg.toInt()).toInt()) val nextDown = fetchWordFromMemory(getRegisterValue(stackReg.toInt()) + WORD_SIZE) setRegisterValue(stackReg.toInt(), getRegisterValue(stackReg.toInt()) + WORD_SIZE) val value = nextDown / currentTop writeWordToMemory(getRegisterValue(stackReg.toInt()).toInt(), value) } 4 -> { val currentTop = fetchWordFromMemory(getRegisterValue(stackReg.toInt()).toInt()) val nextDown = fetchWordFromMemory(getRegisterValue(stackReg.toInt()) + WORD_SIZE) setRegisterValue(stackReg.toInt(), getRegisterValue(stackReg.toInt()) + WORD_SIZE) val value = nextDown % currentTop writeWordToMemory(getRegisterValue(stackReg.toInt()).toInt(), value) } 5 -> { val currentTop = fetchWordFromMemory(getRegisterValue(stackReg.toInt()).toInt()) val nextDown = fetchWordFromMemory(getRegisterValue(stackReg.toInt()) + WORD_SIZE) setRegisterValue(stackReg.toInt(), getRegisterValue(stackReg.toInt()) + WORD_SIZE) val value = nextDown.toInt() and currentTop.toInt() writeWordToMemory(getRegisterValue(stackReg.toInt()).toInt(), value) } 6 -> { val currentTop = fetchWordFromMemory(getRegisterValue(stackReg.toInt()).toInt()) val nextDown = fetchWordFromMemory(getRegisterValue(stackReg.toInt()) + WORD_SIZE) setRegisterValue(stackReg.toInt(), getRegisterValue(stackReg.toInt()) + WORD_SIZE) val value = nextDown.toInt() or currentTop.toInt() writeWordToMemory(getRegisterValue(stackReg.toInt()).toInt(), value) } 7 -> { val currentTop = fetchWordFromMemory(getRegisterValue(stackReg.toInt()).toInt()) val nextDown = fetchWordFromMemory(getRegisterValue(stackReg.toInt()) + WORD_SIZE) setRegisterValue(stackReg.toInt(), getRegisterValue(stackReg.toInt()) + WORD_SIZE) val value = nextDown.toInt() xor currentTop.toInt() writeWordToMemory(getRegisterValue(stackReg.toInt()).toInt(), value) } 8 -> { val currentTop = fetchWordFromMemory(getRegisterValue(stackReg.toInt()).toInt()) val nextDown = fetchWordFromMemory(getRegisterValue(stackReg.toInt()) + WORD_SIZE) setRegisterValue(stackReg.toInt(), getRegisterValue(stackReg.toInt()) + WORD_SIZE) val value = nextDown.toInt() shl currentTop.toInt() writeWordToMemory(getRegisterValue(stackReg.toInt()).toInt(), value) } 9 -> { val currentTop = fetchWordFromMemory(getRegisterValue(stackReg.toInt()).toInt()) val nextDown = fetchWordFromMemory(getRegisterValue(stackReg.toInt()) + WORD_SIZE) setRegisterValue(stackReg.toInt(), getRegisterValue(stackReg.toInt()) + WORD_SIZE) val value = nextDown.toInt() ushr currentTop.toInt() writeWordToMemory(getRegisterValue(stackReg.toInt()).toInt(), value) } 10 -> { val currentTop = fetchWordFromMemory(getRegisterValue(stackReg.toInt()).toInt()) val nextDown = fetchWordFromMemory(getRegisterValue(stackReg.toInt()) + WORD_SIZE) setRegisterValue(stackReg.toInt(), getRegisterValue(stackReg.toInt()) + WORD_SIZE) val min = min(currentTop.toInt(), nextDown.toInt()) writeWordToMemory(getRegisterValue(stackReg.toInt()).toInt(), min) } 11 -> { val currentTop = fetchWordFromMemory(getRegisterValue(stackReg.toInt()).toInt()) val nextDown = fetchWordFromMemory(getRegisterValue(stackReg.toInt()) + WORD_SIZE) setRegisterValue(stackReg.toInt(), getRegisterValue(stackReg.toInt()) + WORD_SIZE) val max = max(currentTop.toInt(), nextDown.toInt()) writeWordToMemory(getRegisterValue(stackReg.toInt()).toInt(), max) } 12 -> { val currentTop = fetchWordFromMemory(getRegisterValue(stackReg.toInt()).toInt()) val value = currentTop.toInt().inv() writeWordToMemory(getRegisterValue(stackReg.toInt()).toInt(), value) } 13 -> { val currentTop = fetchWordFromMemory(getRegisterValue(stackReg.toInt()).toInt()) val value = if (currentTop.toInt() == 0) 1 else 0 writeWordToMemory(getRegisterValue(stackReg.toInt()).toInt(), value) } 14 -> { val currentTop = fetchWordFromMemory(getRegisterValue(stackReg.toInt()).toInt()) val value = -currentTop.toInt() writeWordToMemory(getRegisterValue(stackReg.toInt()).toInt(), value) } else -> badInstruction(instruction) } } 15 -> { val immediateValue = getRegisterValue(Register.DR) setRegisterValue(stackReg.toInt(), getRegisterValue(stackReg.toInt()) - WORD_SIZE) writeWordToMemory(getRegisterValue(stackReg.toInt()).toInt(), immediateValue.toInt()) } else -> badInstruction(instruction) } } else if (instructionType.toInt() == 3) { val opCode: Int = getBits(12, 4, instruction).toInt() val lhs: Int = getBits(8, 4, instruction).toInt() val rhs: Int = getBits(4, 4, instruction).toInt() when (opCode) { 0 -> { val lhsVal = getRegisterValue(lhs) val rhsVal = getRegisterValue(rhs) setFlagTestBit(lhsVal == rhsVal) } 1 -> { val lhsVal = getRegisterValue(lhs) val rhsVal = getRegisterValue(rhs) setFlagTestBit(lhsVal != rhsVal) } 2 -> { val lhsVal = getRegisterValue(lhs) val rhsVal = getRegisterValue(rhs) setFlagTestBit(lhsVal > rhsVal) } 3 -> { val lhsVal = getRegisterValue(lhs) val rhsVal = getRegisterValue(rhs) setFlagTestBit(lhsVal >= rhsVal) } 4 -> { val lhsVal = getRegisterValue(lhs) val rhsVal = getRegisterValue(rhs) setFlagTestBit(lhsVal < rhsVal) } 5 -> { val lhsVal = getRegisterValue(lhs) val rhsVal = getRegisterValue(rhs) setFlagTestBit(lhsVal <= rhsVal) } 8 -> { val lhsVal = getRegisterValue(lhs) val rhsVal = rhs.toShort() setFlagTestBit(lhsVal == rhsVal) } 9 -> { val lhsVal = getRegisterValue(lhs) val rhsVal = rhs.toShort() setFlagTestBit(lhsVal != rhsVal) } 10 -> { val lhsVal = getRegisterValue(lhs) val rhsVal = rhs.toShort() setFlagTestBit(lhsVal > rhsVal) } 11 -> { val lhsVal = getRegisterValue(lhs) val rhsVal = rhs.toShort() setFlagTestBit(lhsVal >= rhsVal) } 12 -> { val lhsVal = getRegisterValue(lhs) val rhsVal = rhs.toShort() setFlagTestBit(lhsVal < rhsVal) } 13 -> { val lhsVal = getRegisterValue(lhs) val rhsVal = rhs.toShort() setFlagTestBit(lhsVal <= rhsVal) } else -> badInstruction(instruction) } } else if (8 == instructionType.toInt()) { // load/store val opCode: Int = getBits(12, 4, instruction).toInt() val reg: Int = getBits(8, 4, instruction).toInt() val offsetReg: Int = getBits(4, 4, instruction).toInt() val address = getRegisterValue(Register.DR).toInt() when (opCode) { 0 -> { val value = fetchWordFromMemory(address) setRegisterValue(reg, value.toInt()) } 1 -> { val value = fetchWordFromMemory(address + getRegisterValue(offsetReg)) setRegisterValue(reg, value.toInt()) } 2 -> { val value = fetchByteFromMemory(address).toShort() setRegisterValue(reg, value.toInt()) } 3 -> { val value = fetchByteFromMemory(address + getRegisterValue(offsetReg)).toShort() setRegisterValue(reg, value.toInt()) } 4 -> { val value = getRegisterValue(reg) writeWordToMemory(address, value.toInt()) } 5 -> { val value = getRegisterValue(reg) writeWordToMemory(address + getRegisterValue(offsetReg), value.toInt()) } 6 -> { val value = getRegisterValue(reg).toByte() writeByteToMemory(address, value) } 7 -> { val value = getRegisterValue(reg).toByte() writeByteToMemory(address + getRegisterValue(offsetReg), value) } 15 -> { setRegisterValue(reg, address) } else -> badInstruction(instruction) } } else if (4 <= instructionType && instructionType <= 7) { // load/store register val loadStoreType: Short = getBits(14, 2, instruction) val targetRegister: Short = getBits(12, 4, instruction) val addressRegister: Short = getBits(8, 4, instruction) val offsetRegister: Short = getBits(4, 4, instruction) val targetAddress = getRegisterValue(addressRegister.toInt()) + getRegisterValue(offsetRegister.toInt()) if (loadStoreType.toInt() == 0x0) { setRegisterValue(targetRegister.toInt(), fetchWordFromMemory(targetAddress).toInt()) } else if (loadStoreType.toInt() == 0x1) { setRegisterValue(targetRegister.toInt(), fetchByteFromMemory(targetAddress).toInt()) } else if (loadStoreType.toInt() == 0x2) { writeWordToMemory(targetAddress, getRegisterValue(targetRegister.toInt()).toInt()) } else if (loadStoreType.toInt() == 0x3) { writeByteToMemory(targetAddress, getRegisterValue(targetRegister.toInt()).toByte()) } } else if (9 == instructionType.toInt()) { // jump reg val reg: Short = getBits(4, 4, instruction) val location = getRegisterValue(reg.toInt()) setRegisterValue(Register.PC, location.toInt()) } else if (12 <= instructionType && instructionType <= 15) { // jumps val jumpType: Short = getBits(14, 2, instruction) if (jumpType.toInt() == 0) { // unconditional val location: Short = getBits(12, 12, instruction) setRegisterValue(Register.PC, location.toInt()) } else if (jumpType.toInt() == 1) { // jz val location: Short = getBits(12, 12, instruction) if (!this.isFlagTestBitSet) { setRegisterValue(Register.PC, location.toInt()) } } else if (jumpType.toInt() == 2) { // jnz val location: Short = getBits(12, 12, instruction) if (this.isFlagTestBitSet) { setRegisterValue(Register.PC, location.toInt()) } } else if (jumpType.toInt() == 3) { // jump & link val location: Short = getBits(12, 12, instruction) setRegisterValue(Register.RA, getRegisterValue(Register.PC).toInt()) setRegisterValue(Register.PC, location.toInt()) } } else { badInstruction(instruction) } observers.forEach { o: MTMCObserver? -> o!!.afterExecution(instruction) } } val isFlagTestBitSet: Boolean get() { val i = getRegisterValue(Register.FLAGS).toInt() and 1 val b = i != 0 return b } fun setFlagTestBit(testVal: Boolean) { var value = getRegisterValue(Register.FLAGS) value = if (testVal) { value or 1 } else { value and 14 } setRegisterValue(Register.FLAGS, value.toInt()) } private fun badInstruction(instruction: Short) { setStatus(ComputerStatus.PERMANENT_ERROR) // TODO implement flags console.println("BAD INSTRUCTION: 0x" + (instruction.toInt() and 0xFFFF).toHexString()) } fun fetchCurrentInstruction() { val pc = getRegisterValue(Register.PC) val instruction = fetchWordFromMemory(pc.toInt()) setRegisterValue(Register.IR, instruction.toInt()) if (isDoubleWordInstruction(instruction)) { val data = fetchWordFromMemory(pc + WORD_SIZE) setRegisterValue(Register.DR, data.toInt()) } else { setRegisterValue(Register.DR, 0) } observers.forEach { o: MTMCObserver? -> o!!.instructionFetched(instruction) } } fun fetchWordFromMemory(address: Int): Short { val upperByte = fetchByteFromMemory(address).toShort() val lowerByte = fetchByteFromMemory(address + 1) var value = (upperByte.toInt() shl 8).toShort() val i = value.toInt() or lowerByte.toInt() value = i.toShort() return value } fun fetchByteFromMemory(address: Int): kotlin.Byte { if (address < 0 || address >= memory.size) { setStatus(ComputerStatus.PERMANENT_ERROR) console.println( "BAD MEMORY LOCATION ON READ: $address (0x" + (address and 0xFFFF).toHexString() + ")" ) return 0 } else { return memory[address] } } fun writeWordToMemory(address: Int, value: Int) { val i = (value ushr 8).toByte() writeByteToMemory(address, i) writeByteToMemory(address + 1, (value and 255).toByte()) } fun writeByteToMemory(address: Int, value: kotlin.Byte) { if (address < 0 || address >= memory.size) { setStatus(ComputerStatus.PERMANENT_ERROR) console.println( "BAD MEMORY LOCATION ON WRITE: " + address + " (0x" + (address and 0xFFFF).toHexString() + ")" ) return } val currentValue = memory[address] addRewindStep { memory[address] = currentValue } memory[address] = value observers!!.forEach { o: MTMCObserver? -> o!!.memoryUpdated(address, value) } } private fun addRewindStep(runnable: Runnable?) { if (currentRewindStep != null && rewindSteps != null) { currentRewindStep!!.addSubStep(runnable) if (rewindSteps.size > MAX_REWIND_STEPS) { rewindSteps.removeLast() } } } fun setRegisterValue(register: Register, value: Int) { setRegisterValue(register.ordinal, value) } fun setRegisterValue(register: Int, value: Int) { if (Short.Companion.MAX_VALUE < value || value < Short.Companion.MIN_VALUE) { // TODO mark as overflow } val currentValue = registerFile[register] addRewindStep { registerFile[register] = currentValue } registerFile[register] = value.toShort() observers!!.forEach { o: MTMCObserver? -> o!!.registerUpdated(register, value) } } fun getRegisterValue(register: Register): Short { return registerFile[register.ordinal] } fun getRegisterValue(register: Int): Short { return registerFile[register] } fun getBreakpoints(): IntArray { val list = ArrayList() for (i in breakpoints.indices) { if (breakpoints[i].toInt() != 0) list.add(i) } val result = IntArray(list.size) for (i in result.indices) result[i] = list.get(i)!! return result } fun setBreakpoint(address: Int, active: Boolean) { breakpoints[address] = (if (active) 1.toByte() else 0.toByte()) } fun start() { console.start() // start the interactive console } fun getBytesFromMemory(address: Int, length: Int): ByteArray { val result = ByteArray(length) memory.copyInto(result, 0, address, address + length) return result } val oS: MTOS get() = os val memoryAddresses: Iterable get() = Iterable { (0..MEMORY_SIZE).iterator() } fun addObserver(observer: MTMCObserver?) { observers.add(observer!!) } fun removeObserver(observer: MTMCObserver?) { observers.remove(observer!!) } fun pause() { setStatus(ComputerStatus.READY) } fun notifyOfConsoleUpdate() { for (observer in observers) { observer.consoleUpdated() } } fun notifyOfConsolePrinting() { for (observer in observers) { observer.consolePrinting() } } fun notifyOfDisplayUpdate() { for (observer in observers) { observer.displayUpdated() } } fun notifyOfFileSystemUpdate() { for (observer in observers) { observer.filesystemUpdated() } } fun notifyOfExecutionUpdate() { for (observer in observers) { observer.executionUpdated() } } fun notifyOfStepExecution() { for (observer in observers) { observer.stepExecution() } } fun notifyOfRequestString() { for (observer in observers) { observer.requestString() } } fun notifyOfRequestCharacter() { for (observer in observers) { observer.requestCharacter() } } fun notifyOfRequestInteger() { for (observer in observers) { observer.requestInteger() } } val iOState: Int get() = iO.value fun setArg(arg: String) { if (!arg.isEmpty()) { val start = getRegisterValue(Register.BP) val bytes = arg.encodeToByteArray() writeStringToMemory(start.toInt(), bytes) setRegisterValue(Register.A0, start.toInt()) setRegisterValue(Register.BP, start + bytes.size + 1) } } fun writeStringToMemory(start: Int, bytes: ByteArray) { for (i in bytes.indices) { val aByte = bytes[i] writeByteToMemory(start + i, aByte) } // null terminate writeByteToMemory(start + bytes.size, 0.toByte()) } fun rewind() { val latestRewindStep = rewindSteps.removeFirst() latestRewindStep.rewind() } val isBackAvailable: Boolean get() = !rewindSteps!!.isEmpty() enum class ComputerStatus { READY, EXECUTING, PERMANENT_ERROR, FINISHED, WAITING, BREAK } override fun toString(): String { val s = StringBuilder() s.append("CI: ").append( Instruction.disassemble( getRegisterValue(Register.IR), getRegisterValue( Register.DR ) ) ) s.append(", Status: ").append(getStatus()!!.name) val fp = getRegisterValue(Register.FP) val sp = getRegisterValue(Register.SP) s.append(", FP:SP=").append(fp.toInt()).append(':').append(sp.toInt()) s.append(", STACK: ") for (i in fp downTo sp + 1) { val value = getBytesFromMemory(i - 2, 2) val v = value[1].toInt() ushr 8 or value[0].toInt() s.append(v).append(' ') } return s.toString() } companion object { // constants const val WORD_SIZE: Short = 2 const val MEMORY_SIZE: Int = 4096 const val MAX_REWIND_STEPS: Int = 100 fun isDoubleWordInstruction(instruction: Short): Boolean { val isLoadStore = getBits(16, 4, instruction) == 8.toShort() if (isLoadStore) { return true } val isALUImmediate = getBits(16, 8, instruction) == 31.toShort() if (isALUImmediate) { return true } val isPushImmediate = getBits(16, 8, instruction) == 47.toShort() if (isPushImmediate) { return true } val isMcp = getBits(16, 8, instruction) == 5.toShort() return isMcp } } }