package mtmc.emulator import mtmc.asm.instructions.Instruction import mtmc.os.MTOS import mtmc.os.fs.FileSystem import mtmc.util.BinaryUtils.getBits import java.lang.Byte import java.util.* import java.util.function.Consumer import java.util.stream.IntStream import kotlin.Array import kotlin.Boolean import kotlin.ByteArray import kotlin.Int import kotlin.IntArray import kotlin.Long import kotlin.Short import kotlin.ShortArray import kotlin.String 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: LinkedList? = LinkedList() // listeners private val observers: MutableList = ArrayList() 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 = null setRegisterValue( Register.SP, MEMORY_SIZE.toShort().toInt() ) // default the stack pointer to the top of memory rewindSteps = LinkedList() observers!!.forEach(Consumer { obj: MTMCObserver? -> 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 System.arraycopy(code, 0, memory, 0, codeBoundary) setRegisterValue(Register.CB, codeBoundary - 1) val dataBoundary = codeBoundary + data.size System.arraycopy(data, 0, memory, codeBoundary, 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() rewindSteps!!.push(currentRewindStep) 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(Consumer { 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(Consumer { 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" + Integer.toHexString(instruction.toInt() and 0xFFFF)) } 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(Consumer { 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 Byte.toUnsignedInt(lowerByte) 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" + Integer.toHexString( address and 0xFFFF ) + ")" ) 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" + Integer.toHexString( address and 0xFFFF ) + ")" ) return } val currentValue = memory[address] addRewindStep(Runnable { memory[address] = currentValue }) memory[address] = value observers!!.forEach(Consumer { 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(Runnable { registerFile[register] = currentValue }) registerFile[register] = value.toShort() observers!!.forEach(Consumer { 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()) } private fun start() { console.start() // start the interactive console } fun getBytesFromMemory(address: Int, length: Int): ByteArray { return Arrays.copyOfRange(memory, address, address + length) } val oS: MTOS get() = os val memoryAddresses: Iterable get() = Iterable { IntStream.range( 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.toByteArray() 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!!.pop() 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() if (isMcp) { return true } return false } @JvmStatic fun main(args: Array) { val computer = MonTanaMiniComputer() computer.speed = 1 // default to 1hz computer.start() } } }