generated from rnentjes/kotlin-server-web-undertow
1018 lines
32 KiB
Kotlin
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
|
|
}
|
|
}
|
|
}
|