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

1031 lines
33 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 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<RewindStep>? = LinkedList<RewindStep>()
// listeners
private val observers: MutableList<MTMCObserver> = ArrayList<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 = null
setRegisterValue(
Register.SP,
MEMORY_SIZE.toShort().toInt()
) // default the stack pointer to the top of memory
rewindSteps = LinkedList<RewindStep>()
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<ByteArray>, 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..<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(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<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())
}
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<Int?>
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<String>) {
val computer = MonTanaMiniComputer()
computer.speed = 1 // default to 1hz
computer.start()
}
}
}