Files
mtmc-web/src/commonMain/kotlin/mtmc/emulator/MonTanaMiniComputer.kt

1018 lines
32 KiB
Kotlin

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<RewindStep>()
// listeners
private val observers = mutableListOf<MTMCObserver>()
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<ByteArray>, 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..<size) {
val value = fetchByteFromMemory(source + i)
writeByteToMemory(dest + i, value)
}
}
8 -> {
// 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<Int?>()
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<Int?>
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
}
}
}