Remove legacy JVM-specific file system, shell, and related implementations; migrate to platform-agnostic and common main modules.

This commit is contained in:
2025-08-14 16:04:13 +02:00
parent 63f9a1f928
commit c7552c2a95
133 changed files with 981 additions and 898 deletions

View File

@@ -0,0 +1,18 @@
package mtmc.asm
import mtmc.tokenizer.MTMCToken
abstract class ASMElement(
val labels: List<MTMCToken>,
var lineNumber: Int
) : HasLocation {
var errors: MutableList<ASMError> = ArrayList()
override var location: Int = 0
override var sizeInBytes: Int = 0
fun addError(token: MTMCToken, error: String) {
errors.add(ASMError(token, error))
}
abstract fun addError(integerValueRequired: String)
}

View File

@@ -0,0 +1,12 @@
package mtmc.asm
import mtmc.tokenizer.MTMCToken
data class ASMError(
val token: MTMCToken,
val error: String
) {
fun formattedErrorMessage(): String {
return "Line " + token.line + ": " + error
}
}

View File

@@ -0,0 +1,810 @@
package mtmc.asm
import mtmc.asm.data.Data
import mtmc.asm.graphics.Graphic
import mtmc.asm.instructions.ALUInstruction
import mtmc.asm.instructions.ALUOp.Companion.isALUOp
import mtmc.asm.instructions.ErrorInstruction
import mtmc.asm.instructions.Instruction
import mtmc.asm.instructions.InstructionType
import mtmc.asm.instructions.InstructionType.Companion.fromString
import mtmc.asm.instructions.InstructionType.InstructionClass
import mtmc.asm.instructions.JumpInstruction
import mtmc.asm.instructions.JumpRegisterInstruction
import mtmc.asm.instructions.LoadStoreInstruction
import mtmc.asm.instructions.LoadStoreRegisterInstruction
import mtmc.asm.instructions.MetaInstruction
import mtmc.asm.instructions.MiscInstruction
import mtmc.asm.instructions.StackInstruction
import mtmc.asm.instructions.TestInstruction
import mtmc.emulator.DebugInfo
import mtmc.emulator.DebugInfo.GlobalInfo
import mtmc.emulator.DebugInfo.LocalInfo
import mtmc.emulator.MonTanaMiniComputer
import mtmc.emulator.Register.Companion.isReadable
import mtmc.emulator.Register.Companion.isWriteable
import mtmc.os.SysCall
import mtmc.os.exec.Executable
import mtmc.os.fs.File
import mtmc.tokenizer.MTMCToken
import mtmc.tokenizer.MTMCTokenizer
fun MutableList<MTMCToken>.poll() = this.removeFirst()
fun MutableList<MTMCToken>.peekFirst() = if (this.isEmpty()) {
null
} else {
this[0]
}
class Assembler {
var instructions: MutableList<Instruction> = ArrayList<Instruction>()
var instructionsSizeInBytes: Int = 0
var data: MutableList<Data> = ArrayList<Data>()
var graphics: MutableList<Graphic> = ArrayList<Graphic>()
var dataSize: Int = 0
var labels: HashMap<String?, HasLocation?>
private var mode: ASMMode = ASMMode.TEXT
var tokenizer: MTMCTokenizer? = null
private var lastLabels: MutableList<MTMCToken> = mutableListOf()
private var srcName = "disk/file.asm"
private var debugStrings: MutableList<String> = mutableListOf()
init {
labels = HashMap<String?, HasLocation?>()
}
fun assemble(asm: String): AssemblyResult {
return assemble(null, asm)
}
fun assemble(file: String?, asm: String): AssemblyResult {
tokenizer = MTMCTokenizer(asm, "#")
debugStrings = ArrayList<String>()
parseAssembly()
resolveLocations()
val errors = collectErrors()
var debugInfo: DebugInfo? = null
var code: ByteArray = ByteArray(0)
var data: ByteArray = ByteArray(0)
var graphics: Array<ByteArray> = arrayOf()
if (errors.isEmpty()) {
code = genCode()
data = genData()
graphics = genGraphics()
debugInfo = genDebugInfo(file, asm, code)
}
return AssemblyResult(code, data, graphics, debugInfo, errors)
}
private fun genDebugInfo(
assemblyFile: String?,
assemblySource: String,
code: ByteArray
): DebugInfo {
val assemblyLineNumbers = IntArray(code.size)
var originalFile: String = ""
val originalLineNumbers = IntArray(code.size)
val globals: MutableList<GlobalInfo> = ArrayList<GlobalInfo>()
val locals = Array<Array<LocalInfo?>>(code.size) { arrayOf() }
var location = 0
var originalLineNumber = 0
val currentLocals: MutableMap<String?, LocalInfo?> = mutableMapOf()
for (instruction in instructions) {
val asmLineNumber = instruction.lineNumber
if (instruction is MetaInstruction) {
if (instruction.isFileDirective) {
originalFile = instruction.getOriginalFilePath()!!
} else if (instruction.isLineDirective) {
originalLineNumber = instruction.originalLineNumber
} else if (instruction.isGlobalDirective) {
globals.add(
GlobalInfo(
instruction.getGlobalName(),
instruction.getGlobalLocation(),
instruction.getGlobalType()
)
)
} else if (instruction.isLocalDirective) {
currentLocals.put(
instruction.getLocalName(),
LocalInfo(
instruction.getLocalName(),
instruction.getLocalOffset(),
instruction.getLocalType()
)
)
} else if (instruction.isEndLocalDirective) {
currentLocals.remove(instruction.getLocalName())
}
}
while (location < instruction.location + instruction.sizeInBytes) {
assemblyLineNumbers[location] = asmLineNumber
originalLineNumbers[location] = originalLineNumber
locals[location] = currentLocals.values.toTypedArray<LocalInfo?>()
location++
}
}
val debugInfo = DebugInfo(
debugStrings,
assemblyFile,
assemblySource,
assemblyLineNumbers,
originalFile,
originalLineNumbers,
globals.toTypedArray<GlobalInfo>(),
locals
)
return debugInfo
}
fun assembleExecutable(srcName: String, asm: String): Executable {
this.srcName = "disk/" + srcName //TODO: The prefix should be replaced with FileSystem access
val result = assemble(srcName, asm)
if (!result.errors.isEmpty()) {
throw RuntimeException(
"Errors:\n" + result.errors.joinToString("\n") { e: ASMError? -> " - " + e!!.formattedErrorMessage() }
)
}
return Executable(
Executable.Format.Orc1,
result.code,
result.data,
result.graphics,
srcName,
result.debugInfo
)
}
private fun parseAssembly() {
while (tokenizer!!.more()) {
parseLine()
}
}
private fun genData(): ByteArray {
val dataBytes = ByteArray(dataSize)
for (dataElt in data) {
dataElt.genData(dataBytes, this)
}
return dataBytes
}
private fun genCode(): ByteArray {
val code = ByteArray(this.instructionsSizeInBytes)
for (instruction in instructions) {
instruction.genCode(code, this)
}
return code
}
private fun genOriginalLineNumbers(length: Int): IntArray {
val asmLineNumbers = IntArray(length)
var currentLineNumber = 0
var location = 0
for (instruction in instructions) {
if (instruction is MetaInstruction && instruction.isLineDirective) {
currentLineNumber = instruction.originalLineNumber
}
while (location < instruction.location + instruction.sizeInBytes) {
asmLineNumbers[location] = currentLineNumber
location++
}
}
return asmLineNumbers
}
private fun genGraphics(): Array<ByteArray> {
val graphics = Array<ByteArray>(this.graphics.size) { index ->
graphics[index].imageData
}
return graphics
}
private fun collectErrors(): MutableList<ASMError> {
val errors: MutableList<ASMError> = ArrayList<ASMError>()
for (instruction in instructions) {
errors.addAll(instruction.errors)
}
for (data in this.data) {
errors.addAll(data.errors)
}
for (graphic in this.graphics) {
errors.addAll(graphic.errors)
}
return errors
}
private fun resolveLocations() {
// assign locations
var offset = 0
for (instruction in instructions) {
instruction.location = offset
offset += instruction.sizeInBytes
this.instructionsSizeInBytes += instruction.sizeInBytes
}
for (data in data) {
data.location = offset
offset += data.sizeInBytes
dataSize += data.sizeInBytes
}
for (instruction in instructions) {
instruction.validateLabel(this)
}
}
private fun parseLine() {
val tokens = this.tokensForLine
if (parseMetaDirective(tokens)) {
return
}
val newMode = parseModeFlag(tokens)
if (newMode != null) {
mode = newMode
return
}
// label only lines are ok and propagate to the next instruction
val labelTokens: MutableList<MTMCToken> = maybeGetLabels(tokens)
val labels = ArrayList<MTMCToken>(lastLabels)
labels.addAll(labelTokens)
lastLabels = labels
if (tokens.isEmpty()) {
for (labelToken in labelTokens) {
val labelData = Data(labelTokens, labelToken.line)
if (hasLabel(labelToken.stringValue)) {
labelData.addError(
tokens.removeFirst(),
"Label already defined: " + labelToken.stringValue
)
} else {
this.labels.put(labelToken.labelValue(), labelData)
}
data.add(labelData)
}
} else if (mode == ASMMode.TEXT) {
parseInstruction(tokens, labels)
} else {
parseData(tokens, labels)
}
}
private fun parseMetaDirective(tokens: MutableList<MTMCToken>): Boolean {
if (tokens.size >= 2 && tokens.get(0).type == MTMCToken.TokenType.AT) {
tokens.removeFirst()
val metaInstruction = MetaInstruction(tokens.removeFirst())
if (metaInstruction.isFileDirective) {
val path = requireString(tokens, metaInstruction)
metaInstruction.setOriginalFilePath(path)
} else if (metaInstruction.isGlobalDirective) {
val name = requireString(tokens, metaInstruction)
val location = requireIntegerToken(tokens, metaInstruction, Int.Companion.MAX_VALUE)
val type = requireString(tokens, metaInstruction)
metaInstruction.setGlobalInfo(name, location, type)
} else if (metaInstruction.isLocalDirective) {
val name = requireString(tokens, metaInstruction)
val offset = requireIntegerToken(tokens, metaInstruction, Int.Companion.MAX_VALUE)
val type = requireString(tokens, metaInstruction)
metaInstruction.setLocalInfo(name, offset, type)
} else if (metaInstruction.isEndLocalDirective) {
val name = requireString(tokens, metaInstruction)
metaInstruction.setEndLocalInfo(name)
} else if (metaInstruction.isLineDirective) {
val lineNumber = requireIntegerToken(tokens, metaInstruction, Int.Companion.MAX_VALUE)
metaInstruction.setOriginalLineNumber(lineNumber)
} else {
metaInstruction.addError("Unknown meta directive")
}
instructions.add(metaInstruction)
return true
}
return false
}
private fun parseModeFlag(tokens: MutableList<MTMCToken>): ASMMode? {
if (tokens.size >= 2 && tokens.get(0).type == MTMCToken.TokenType.DOT && tokens.get(1).type == MTMCToken.TokenType.IDENTIFIER && tokens.get(
0
).end == tokens.get(1).start && (tokens.get(1).stringValue == "data" ||
tokens.get(1).stringValue == "text")
) {
return ASMMode.valueOf(tokens.get(1).stringValue.uppercase())
}
return null
}
private fun parseData(tokens: MutableList<MTMCToken>, labelTokens: MutableList<MTMCToken>) {
lastLabels = mutableListOf()
var dataToken = tokens.removeFirst()
var dataElt = Data(labelTokens, if (dataToken == null) 0 else dataToken.line)
if (dataToken != null) {
if (dataToken.type == MTMCToken.TokenType.STRING) {
val stringBytes = dataToken.stringValue.encodeToByteArray()
val nullTerminated = ByteArray(stringBytes.size + 1)
stringBytes.copyInto(nullTerminated, 0, 0, stringBytes.size)
nullTerminated[stringBytes.size] = '\u0000'.code.toByte()
dataElt.setValue(dataToken, nullTerminated)
} else if (isInteger(dataToken)) {
val integerValue = dataToken.intValue()
if (integerValue > Short.Companion.MAX_VALUE || integerValue < Short.Companion.MIN_VALUE) {
dataElt.addError(dataToken, "Number is too large")
}
dataElt.setValue(
dataToken,
byteArrayOf((integerValue ushr 8).toByte(), integerValue.toByte())
)
} else if (dataToken.type == MTMCToken.TokenType.DOT) {
dataToken = tokens.removeFirst()
dataElt = Data(labelTokens, dataToken.line)
if (dataToken.stringValue == "int") {
val intToken = requireIntegerToken(tokens, dataElt, MonTanaMiniComputer.MEMORY_SIZE)
if (intToken != null) {
dataElt.setValue(intToken, ByteArray(intToken.intValue() * 2))
}
} else if (dataToken.stringValue == "byte") {
val intToken = requireIntegerToken(tokens, dataElt, MonTanaMiniComputer.MEMORY_SIZE)
if (intToken != null) {
dataElt.setValue(intToken, ByteArray(intToken.intValue()))
}
} else if (dataToken.stringValue == "image") {
val stringToken = requireString(tokens, dataElt)
if (stringToken != null) {
loadGraphic(labelTokens, dataElt, stringToken)
}
} else {
dataElt.addError(dataToken, "only data types are .int, .byte, and .image")
}
} else if (dataToken.type == MTMCToken.TokenType.MINUS) {
val nextToken = tokens.removeFirst() // get next
if (nextToken == null || (nextToken.type != MTMCToken.TokenType.INTEGER && nextToken.type != MTMCToken.TokenType.HEX && nextToken.type != MTMCToken.TokenType.BINARY)) {
dataElt.addError(dataToken, "Number is too negative")
} else {
val integerValue = -1 * nextToken.intValue()
if (integerValue < Short.Companion.MIN_VALUE) {
dataElt.addError(dataToken, "Number is too negative")
}
val joinToken = MTMCToken.join(dataToken, nextToken, MTMCToken.TokenType.INTEGER)
dataElt.setValue(
joinToken,
byteArrayOf((integerValue ushr 8).toByte(), integerValue.toByte())
)
}
} else {
dataElt.addError(dataToken, "Unknown token type: " + dataToken.toString())
}
}
for (labelToken in labelTokens) {
if (hasLabel(labelToken.stringValue)) {
dataElt.addError(tokens.removeFirst(), "Label already defined: " + labelToken.stringValue)
} else {
labels.put(labelToken.labelValue(), dataElt)
}
}
data.add(dataElt)
}
private fun loadGraphic(
labelTokens: MutableList<MTMCToken>,
data: Data,
token: MTMCToken
): Graphic {
val graphic = Graphic(labelTokens, token.line)
val filename = token.stringValue
val file = File(File(this.srcName).getParent(), filename)
val index = graphics.size
data.setValue(token, byteArrayOf(((index shr 8) and 0xFF).toByte(), (index and 0xFF).toByte()))
graphic.setImage(file.getPath())
graphics.add(graphic)
return graphic
}
private fun parseInstruction(
tokens: MutableList<MTMCToken>,
labelTokens: MutableList<MTMCToken>
) {
var tokens = tokens
var instructionToken = if (tokens.isEmpty()) {
null
} else {
tokens[0]
}
if (instructionToken == null) return
lastLabels = mutableListOf()
if (instructionToken.type != MTMCToken.TokenType.IDENTIFIER) {
instructions.add(ErrorInstruction(labelTokens, instructionToken, "Invalid Token"))
return
}
tokens = transformSyntheticInstructions(tokens)
instructionToken = tokens.poll()
val type = fromString(instructionToken.stringValue)
if (type == null) {
instructions.add(
ErrorInstruction(
labelTokens,
instructionToken,
"Unknown instruction token type: " + instructionToken.stringValue
)
)
return
}
val instruction: Instruction?
if (type.instructionClass == InstructionClass.MISC) {
val miscInst = MiscInstruction(type, labelTokens, instructionToken)
if (type == InstructionType.SYS) {
val sysCallType = requireSysCall(tokens, miscInst)
miscInst.setSyscallType(sysCallType)
} else if (type == InstructionType.MOV) {
val toRegister = requireWriteableRegister(tokens, miscInst)
miscInst.setTo(toRegister)
val fromRegister = requireReadableRegister(tokens, miscInst)
miscInst.setFrom(fromRegister)
} else if (type == InstructionType.INC || type == InstructionType.DEC) {
val toRegister = requireWriteableRegister(tokens, miscInst)
miscInst.setTo(toRegister)
if (!tokens.isEmpty()) {
val value = requireIntegerToken(tokens, miscInst, 15)
miscInst.setValue(value)
}
} else if (type == InstructionType.SETI) {
val toRegister = requireWriteableRegister(tokens, miscInst)
miscInst.setTo(toRegister)
val value = requireIntegerToken(tokens, miscInst, 15)
miscInst.setValue(value)
} else if (type == InstructionType.MCP) {
val fromRegister = requireWriteableRegister(tokens, miscInst)
miscInst.setFrom(fromRegister)
val toRegister = requireWriteableRegister(tokens, miscInst)
miscInst.setTo(toRegister)
val value = requireIntegerToken(tokens, miscInst, Short.Companion.MAX_VALUE.toInt())
miscInst.setValue(value)
} else if (type == InstructionType.DEBUG) {
val debugString = requireString(tokens, miscInst)
// create a dummy int token representing the offset of the debug string in the debug info
val debugStringIndex = debugStrings!!.size
debugStrings!!.add(debugString.stringValue)
val value = MTMCToken(0, 0, 0, 0, debugStringIndex.toString(), MTMCToken.TokenType.INTEGER)
miscInst.setValue(value)
}
instruction = miscInst
} else if (type.instructionClass == InstructionClass.ALU) {
val aluInst = ALUInstruction(type, labelTokens, instructionToken)
if (aluInst.isBinaryOp) {
val toRegister = requireWriteableRegister(tokens, aluInst)
aluInst.setTo(toRegister)
val fromRegister = requireReadableRegister(tokens, aluInst)
aluInst.setFrom(fromRegister)
} else if (aluInst.isImmediateOp()) {
val immediateOp = requireALUOp(tokens, aluInst)
// TODO - validate is max or lower op
aluInst.setImmediateOp(immediateOp)
val toRegister = requireWriteableRegister(tokens, aluInst)
aluInst.setTo(toRegister)
val value = requireIntegerToken(tokens, aluInst, Short.Companion.MAX_VALUE.toInt())
aluInst.setImmediateValue(value)
} else {
val toRegister = requireWriteableRegister(tokens, aluInst)
aluInst.setTo(toRegister)
}
instruction = aluInst
} else if (type.instructionClass == InstructionClass.STACK) {
val stackInst = StackInstruction(type, labelTokens, instructionToken)
if (type == InstructionType.PUSH) {
val targetRegister = requireReadableRegister(tokens, stackInst)
stackInst.setTarget(targetRegister)
} else if (type == InstructionType.POP) {
val toRegister = requireWriteableRegister(tokens, stackInst)
stackInst.setTarget(toRegister)
} else if (type == InstructionType.SOP) {
val aluOp = requireAluOp(tokens, stackInst)
stackInst.setALUOp(aluOp)
} else if (type == InstructionType.PUSHI) {
val value = requireIntegerToken(tokens, stackInst, Short.Companion.MAX_VALUE.toInt())
stackInst.setValue(value)
}
// if there is a stack register specified, consume it
if (!tokens.isEmpty() && tokens.peekFirst()?.type == MTMCToken.TokenType.IDENTIFIER) {
val stackReg = requireReadableRegister(tokens, stackInst)
stackInst.setStackRegister(stackReg)
}
instruction = stackInst
} else if (type.instructionClass == InstructionClass.TEST) {
val testInst = TestInstruction(type, labelTokens, instructionToken)
testInst.setFirst(requireReadableRegister(tokens, testInst))
if (testInst.isImmediate) {
testInst.setImmediateValue(requireIntegerToken(tokens, testInst, 15))
} else {
testInst.setSecond(requireReadableRegister(tokens, testInst))
}
instruction = testInst
} else if (type.instructionClass == InstructionClass.LOAD_STORE) {
val loadInst = LoadStoreInstruction(type, labelTokens, instructionToken)
val targetReg = requireReadableRegister(tokens, loadInst)
loadInst.setTargetToken(targetReg)
if (loadInst.isOffset) {
val offsetReg = requireReadableRegister(tokens, loadInst)
loadInst.setOffsetToken(offsetReg)
}
val value = requireIntegerOrLabelReferenceToken(tokens, loadInst)
loadInst.setValue(value)
instruction = loadInst
} else if (type.instructionClass == InstructionClass.LOAD_STORE_REGISTER) {
val loadInst = LoadStoreRegisterInstruction(type, labelTokens, instructionToken)
val targetReg = requireWriteableRegister(tokens, loadInst)
loadInst.setTargetToken(targetReg)
val pointerReg = requireWriteableRegister(tokens, loadInst)
loadInst.setPointerToken(pointerReg)
// if there is an offset register specified, consume it
if (!tokens.isEmpty() && tokens.peekFirst()?.type == MTMCToken.TokenType.IDENTIFIER) {
val offsetReg = requireReadableRegister(tokens, loadInst)
loadInst.setOffsetToken(offsetReg)
}
instruction = loadInst
} else if (type.instructionClass == InstructionClass.JUMP_REGISTER) {
val jumpInst = JumpRegisterInstruction(type, labelTokens, instructionToken)
val register = requireReadableRegister(tokens, jumpInst)
jumpInst.setRegister(register)
instruction = jumpInst
} else if (type.instructionClass == InstructionClass.JUMP) {
val jumpInst = JumpInstruction(type, labelTokens, instructionToken)
val labelValue: MTMCToken? = maybeGetLabelReference(tokens)
val valueToken: MTMCToken?
if (labelValue != null) {
valueToken = labelValue
} else {
valueToken = requireIntegerToken(tokens, jumpInst, MonTanaMiniComputer.MEMORY_SIZE)
}
jumpInst.setAddressToken(valueToken)
instruction = jumpInst
} else {
instruction = ErrorInstruction(
listOf(),
instructionToken,
"Unexpected Token"
)
}
for (labelToken in labelTokens) {
if (hasLabel(labelToken.stringValue)) {
instruction.addError(tokens.poll(), "Label already defined: " + labelToken.stringValue)
} else {
labels.put(labelToken.labelValue(), instruction)
}
}
// add error if any tokens are left on the line
if (!tokens.isEmpty()) {
instruction.addError(tokens.poll(), "Unexpected Token")
}
instructions.add(instruction)
}
//===================================================
// tokenization helper functions
//===================================================
private fun requireSysCall(tokens: MutableList<MTMCToken>, inst: Instruction): MTMCToken {
val sysCallType = tokens.poll()
if (sysCallType == null) {
inst.addError("Syscall required")
} else if (sysCallType.type != MTMCToken.TokenType.IDENTIFIER) {
inst.addError(sysCallType, "Syscall required")
} else if (!SysCall.isSysCall(sysCallType.stringValue)) {
inst.addError(sysCallType, "Unknown syscall : " + sysCallType.stringValue)
}
return sysCallType!!
}
private fun requireAluOp(tokens: MutableList<MTMCToken>, inst: Instruction): MTMCToken {
val sysCallType = tokens.poll()
if (sysCallType == null) {
inst.addError("Syscall required")
} else if (sysCallType.type != MTMCToken.TokenType.IDENTIFIER) {
inst.addError(sysCallType, "Syscall required")
} else if (!isALUOp(sysCallType.stringValue)) {
inst.addError(sysCallType, "Unknown alu operation : " + sysCallType.stringValue)
}
return sysCallType!!
}
private fun requireWriteableRegister(
tokens: MutableList<MTMCToken>,
instruction: Instruction
): MTMCToken {
val nextToken = tokens.poll()
if (nextToken == null) {
instruction.addError("Register required")
} else if (nextToken.type != MTMCToken.TokenType.IDENTIFIER) {
instruction.addError(nextToken, "Invalid Register : " + nextToken.stringValue)
} else if (!isWriteable(nextToken.stringValue)) {
instruction.addError(nextToken, "Register not writeable : " + nextToken.stringValue)
}
return nextToken!!
}
private fun requireReadableRegister(
tokens: MutableList<MTMCToken>,
instruction: Instruction
): MTMCToken {
val nextToken = tokens.poll()
if (nextToken == null) {
instruction.addError("Register required")
} else if (nextToken.type != MTMCToken.TokenType.IDENTIFIER) {
instruction.addError(nextToken, "Invalid Register : " + nextToken.stringValue)
} else if (!isReadable(nextToken.stringValue)) {
instruction.addError(nextToken, "Register not readable : " + nextToken.stringValue)
}
return nextToken!!
}
private fun requireALUOp(tokens: MutableList<MTMCToken>, instruction: Instruction): MTMCToken {
val nextToken = tokens.poll()
if (nextToken == null || nextToken.type != MTMCToken.TokenType.IDENTIFIER || !isALUOp(nextToken.stringValue)) {
instruction.addError("ALU operation required")
}
return nextToken!!
}
private fun requireString(tokens: MutableList<MTMCToken>, instruction: ASMElement): MTMCToken {
val nextToken = tokens.poll()
if (nextToken == null || nextToken.type != MTMCToken.TokenType.STRING) {
instruction.addError("String required")
}
return nextToken!!
}
private fun requireIntegerToken(
tokens: MutableList<MTMCToken>,
inst: ASMElement,
max: Int
): MTMCToken {
val token = tokens.poll()
if (token == null) {
inst.addError("Integer value required")
} else if (isInteger(token)) {
val integerValue = token.intValue()
if (integerValue < 0 || max < integerValue) {
inst.addError(token, "Integer value out of range: 0-" + max)
}
} else {
inst.addError(token, "Integer value expected")
}
return token!!
}
private fun requireToken(
tokens: MutableList<MTMCToken>,
type: MTMCToken.TokenType,
inst: ASMElement
): MTMCToken? {
val token = tokens.poll()
if (token == null || token.type != type) {
inst.addError(token, "Token " + type.name + " required")
}
return token
}
private fun requireIntegerOrLabelReferenceToken(
tokens: MutableList<MTMCToken>,
inst: LoadStoreInstruction
): MTMCToken {
val token = tokens.poll()
if (token == null) {
inst.addError("Integer or label value required")
} else if (!isInteger(token) && token.type != MTMCToken.TokenType.IDENTIFIER) {
inst.addError(token, "Integer or label value expected")
}
return token!!
}
private val tokensForLine: MutableList<MTMCToken>
get() {
val tokens = mutableListOf<MTMCToken>()
if (tokenizer!!.more()) {
val first = tokenizer!!.consume()
tokens.add(first)
while (tokenizer!!.more() &&
first.line == tokenizer!!.currentToken().line
) {
tokens.add(tokenizer!!.consume())
}
}
return tokens
}
fun hasLabel(label: String?): Boolean {
return labels.containsKey(label)
}
fun resolveLabel(label: String?): Int {
return labels.get(label)!!.location
}
internal enum class ASMMode {
DATA,
TEXT
}
companion object {
fun transformSyntheticInstructions(tokens: MutableList<MTMCToken>): MutableList<MTMCToken> {
if (!tokens.isEmpty()) {
val first = tokens.first()
if (first.type == MTMCToken.TokenType.IDENTIFIER) {
val stringVal = first.stringValue
if (stringVal.endsWith("i")) {
val op = stringVal.substring(0, stringVal.length - 1)
if (isALUOp(op)) {
val syntheticImmediate = tokens.removeFirst()
tokens.add(0, syntheticImmediate.cloneWithVal(op))
tokens.add(0, syntheticImmediate.cloneWithVal("imm"))
}
} else if (stringVal.startsWith("s")) {
val op = stringVal.substring(1, stringVal.length)
if (isALUOp(op)) {
val syntheticImmediate = tokens.removeFirst()
tokens.add(0, syntheticImmediate.cloneWithVal(op))
tokens.add(0, syntheticImmediate.cloneWithVal("sop"))
}
} else if (stringVal == "la") {
val syntheticImmediate = tokens.removeFirst()
tokens.add(0, syntheticImmediate.cloneWithVal("li"))
} else if (stringVal == "ret") {
val syntheticImmediate = tokens.removeFirst()
tokens.add(0, syntheticImmediate.cloneWithVal("ra"))
tokens.add(0, syntheticImmediate.cloneWithVal("jr"))
}
}
}
return tokens
}
private fun maybeGetLabels(tokens: MutableList<MTMCToken>): MutableList<MTMCToken> {
val labels = mutableListOf<MTMCToken>()
while (!tokens.isEmpty() && tokens.first().type == MTMCToken.TokenType.LABEL) {
val label = tokens.removeFirst()
labels.add(label!!)
}
return labels
}
private fun maybeGetLabelReference(tokens: MutableList<MTMCToken>): MTMCToken? {
var label: MTMCToken? = null
if (tokens.first().type == MTMCToken.TokenType.IDENTIFIER) {
label = tokens.removeFirst()
}
return label
}
private fun isInteger(token: MTMCToken?): Boolean {
return token != null && (token.type == MTMCToken.TokenType.INTEGER || token.type == MTMCToken.TokenType.HEX || token.type == MTMCToken.TokenType.BINARY)
}
}
}

View File

@@ -0,0 +1,19 @@
package mtmc.asm
import mtmc.emulator.DebugInfo
data class AssemblyResult(
val code: ByteArray,
val data: ByteArray,
val graphics: Array<ByteArray>,
val debugInfo: DebugInfo?,
val errors: MutableList<ASMError>
) {
fun printErrors(): String {
val builder = StringBuilder("Errors:\n")
for (error in errors) {
builder.append(" Line " + error.token.line + ": " + error.error).append('\n')
}
return builder.toString()
}
}

View File

@@ -0,0 +1,6 @@
package mtmc.asm
interface HasLocation {
var location: Int
var sizeInBytes: Int
}

View File

@@ -0,0 +1,37 @@
package mtmc.asm.data
import mtmc.asm.ASMElement
import mtmc.asm.Assembler
import mtmc.tokenizer.MTMCToken
class Data(labels: MutableList<MTMCToken>, lineNumber: Int) : ASMElement(labels, lineNumber) {
var valueToken: MTMCToken? = null
private var value: ByteArray = ByteArray(0)
override var sizeInBytes: Int
get() = value.size
set(value) {}
fun genData(dataBytes: ByteArray, assembler: Assembler) {
val offset = location - assembler.instructionsSizeInBytes
for (i in 0..<sizeInBytes) {
val dataByte = value[i]
dataBytes[offset + i] = dataByte
}
}
fun setValue(src: MTMCToken, value: ByteArray) {
this.valueToken = src
this.value = value
}
override fun toString(): String {
return "Data{" +
"value=" + value.contentToString() +
'}'
}
override fun addError(err: String) {
addError(labels.last(), err)
}
}

View File

@@ -0,0 +1,45 @@
package mtmc.asm.graphics
import mtmc.asm.ASMElement
import mtmc.tokenizer.MTMCToken
/**
*
* @author jbanes
*/
class Graphic(labels: MutableList<MTMCToken>, lineNumber: Int) : ASMElement(labels, lineNumber) {
private var filename: String? = null
var imageData: ByteArray = ByteArray(0)
fun setImage(filename: String) {
/* try {
var image = ImageIO.read(File(filename))
val buffer = ByteArrayOutputStream()
if (image.getWidth() > 1024 || image.getHeight() > 1024) {
addError(filename + " is too large. Maximum image size is 1024x1024")
}
image = convertImage(image)
ImageIO.write(image, "png", buffer)
this.filename = filename
this.imageData = buffer.toByteArray()
} catch (e: FileNotFoundException) {
e.printStackTrace()
addError("$filename not found")
} catch (e: IOException) {
e.printStackTrace()
addError(e.message ?: "error in Graphic") // TODO: Verify these messages are meaningful
}*/
}
override fun addError(err: String) {
addError(labels.last(), err)
}
override var sizeInBytes: Int
get() = 2
set(value) {}
}

View File

@@ -0,0 +1,87 @@
package mtmc.asm.instructions
import mtmc.asm.Assembler
import mtmc.emulator.Register.Companion.fromInteger
import mtmc.emulator.Register.Companion.toInteger
import mtmc.tokenizer.MTMCToken
import mtmc.util.BinaryUtils
class ALUInstruction(
type: InstructionType,
label: List<MTMCToken>,
instructionToken: MTMCToken
) : Instruction(type, label, instructionToken) {
private var toToken: MTMCToken? = null
private var fromToken: MTMCToken? = null
private var immediateOp: MTMCToken? = null
private var value: MTMCToken? = null
fun setTo(to: MTMCToken) {
this.toToken = to
}
fun setFrom(from: MTMCToken?) {
this.fromToken = from
}
fun setImmediateValue(value: MTMCToken) {
this.value = value
}
fun setImmediateOp(immediateOp: MTMCToken) {
this.immediateOp = immediateOp
}
fun isImmediateOp(): Boolean {
return this.type == InstructionType.IMM
}
val isBinaryOp: Boolean
get() = !ALUOp.valueOf(instructionToken.stringValue.uppercase()).isUnary
override fun genCode(output: ByteArray, assembler: Assembler) {
val opCode = ALUOp.toInteger(instructionToken.stringValue)
val to = toInteger(toToken!!.stringValue)
var from = 0
if (fromToken != null) {
from = toInteger(fromToken!!.stringValue)
}
output[location] = (16 or opCode).toByte()
if (this.isBinaryOp) {
output[location + 1] = (to shl 4 or from).toByte()
} else if (isImmediateOp()) {
val immediateValue = value!!.intValue()
val immediateOpValue = ALUOp.toInteger(immediateOp!!.stringValue)
output[location + 1] = (to shl 4 or immediateOpValue).toByte()
output[location + 2] = (immediateValue ushr 8).toByte()
output[location + 3] = immediateValue.toByte()
} else { // unary op
output[location + 1] = (to shl 4).toByte()
}
}
companion object {
fun disassemble(instruction: Short): String? {
if (BinaryUtils.getBits(16, 4, instruction).toInt() == 1) {
val builder = StringBuilder()
val opCode = BinaryUtils.getBits(12, 4, instruction)
val op = ALUOp.fromInt(opCode)
val aluOp = ALUOp.valueOf(op.uppercase())
if (aluOp == ALUOp.IMM) {
builder.append(op).append(" ")
builder.append(fromInteger(BinaryUtils.getBits(8, 4, instruction).toInt())).append(" ")
builder.append(ALUOp.fromInt(BinaryUtils.getBits(4, 4, instruction))).append(" ")
} else if (aluOp.isUnary) {
builder.append(op).append(" ")
builder.append(fromInteger(BinaryUtils.getBits(8, 4, instruction).toInt())).append(" ")
} else {
builder.append(op).append(" ")
builder.append(fromInteger(BinaryUtils.getBits(8, 4, instruction).toInt())).append(" ")
builder.append(fromInteger(BinaryUtils.getBits(4, 4, instruction).toInt())).append(" ")
}
return builder.toString()
}
return null
}
}
}

View File

@@ -0,0 +1,39 @@
package mtmc.asm.instructions
enum class ALUOp(val opCode: Int, val isUnary: Boolean) {
ADD(0x0000, false),
SUB(0x0001, false),
MUL(0x0002, false),
DIV(0x0003, false),
MOD(0x0004, false),
AND(0x0005, false),
OR(0x0006, false),
XOR(0x0007, false),
SHL(0x0008, false),
SHR(0x0009, false),
MIN(0x000A, false),
MAX(0x000B, false),
NOT(0x000C, true),
LNOT(0x000D, true),
NEG(0x000E, true),
IMM(0x000F, true);
companion object {
fun toInteger(instruction: String): Int {
return valueOf(instruction.uppercase()).opCode
}
fun fromInt(opCode: Short): String {
return entries[opCode.toInt()].name.lowercase()
}
fun isALUOp(op: String): Boolean {
try {
val aluOp = valueOf(op.uppercase())
return true
} catch (e: IllegalArgumentException) {
return false
}
}
}
}

View File

@@ -0,0 +1,23 @@
package mtmc.asm.instructions
import mtmc.asm.Assembler
import mtmc.tokenizer.MTMCToken
class ErrorInstruction(
labels: List<MTMCToken>,
instruction: MTMCToken,
error: String
) : Instruction(
InstructionType.ERROR,
labels,
instruction
) {
init {
addError(instruction, error)
}
override fun genCode(output: ByteArray, assembler: Assembler) {
// do nothing
}
}

View File

@@ -0,0 +1,71 @@
package mtmc.asm.instructions
import mtmc.asm.ASMElement
import mtmc.asm.Assembler
import mtmc.asm.instructions.ALUInstruction.Companion.disassemble
import mtmc.emulator.MonTanaMiniComputer.Companion.isDoubleWordInstruction
import mtmc.tokenizer.MTMCToken
abstract class Instruction(
val type: InstructionType,
labels: List<MTMCToken>,
val instructionToken: MTMCToken
) : ASMElement(labels, instructionToken.line) {
open fun validateLabel(assembler: Assembler) {
// default does nothing
}
override fun addError(error: String) {
addError(instructionToken, error)
}
abstract fun genCode(output: ByteArray, assembler: Assembler)
override var sizeInBytes: Int = type.sizeInBytes
get() = type.sizeInBytes
companion object {
fun isInstruction(cmd: String): Boolean {
return InstructionType.fromString(cmd) != null
}
fun disassemble(instruction: Short, previousInstruction: Short): String {
if (isDoubleWordInstruction(previousInstruction)) {
return instruction.toInt().toString()
}
val misc = MiscInstruction.disassemble(instruction)
if (misc != null) {
return misc
}
val aluOp = disassemble(instruction)
if (aluOp != null) {
return aluOp
}
val stack = StackInstruction.disassemble(instruction)
if (stack != null) {
return stack
}
val test = TestInstruction.disassemble(instruction)
if (test != null) {
return test
}
val lsr = LoadStoreRegisterInstruction.disassemble(instruction)
if (lsr != null) {
return lsr
}
val ls = LoadStoreInstruction.disassemble(instruction)
if (ls != null) {
return ls
}
val jumpReg = JumpInstruction.disassemble(instruction)
if (jumpReg != null) {
return jumpReg
}
val jump = JumpInstruction.disassemble(instruction)
if (jump != null) {
return jump
}
return "<unknown>"
}
}
}

View File

@@ -0,0 +1,95 @@
package mtmc.asm.instructions
enum class InstructionType constructor(
val instructionClass: InstructionClass?,
val sizeInBytes: Int = 2
) {
SYS(InstructionClass.MISC),
MOV(InstructionClass.MISC),
INC(InstructionClass.MISC),
DEC(InstructionClass.MISC),
SETI(InstructionClass.MISC),
NOP(InstructionClass.MISC),
MCP(InstructionClass.MISC, 4),
DEBUG(InstructionClass.MISC),
ADD(InstructionClass.ALU),
SUB(InstructionClass.ALU),
MUL(InstructionClass.ALU),
DIV(InstructionClass.ALU),
MOD(InstructionClass.ALU),
AND(InstructionClass.ALU),
OR(InstructionClass.ALU),
XOR(InstructionClass.ALU),
SHL(InstructionClass.ALU),
SHR(InstructionClass.ALU),
MIN(InstructionClass.ALU),
MAX(InstructionClass.ALU),
NOT(InstructionClass.ALU),
LNOT(InstructionClass.ALU),
NEG(InstructionClass.ALU),
IMM(InstructionClass.ALU, 4),
PUSH(InstructionClass.STACK),
POP(InstructionClass.STACK),
DUP(InstructionClass.STACK),
SWAP(InstructionClass.STACK),
DROP(InstructionClass.STACK),
OVER(InstructionClass.STACK),
ROT(InstructionClass.STACK),
SOP(InstructionClass.STACK),
PUSHI(InstructionClass.STACK, 4),
EQ(InstructionClass.TEST),
NEQ(InstructionClass.TEST),
GT(InstructionClass.TEST),
GTE(InstructionClass.TEST),
LT(InstructionClass.TEST),
LTE(InstructionClass.TEST),
EQI(InstructionClass.TEST),
NEQI(InstructionClass.TEST),
GTI(InstructionClass.TEST),
GTEI(InstructionClass.TEST),
LTI(InstructionClass.TEST),
LTEI(InstructionClass.TEST),
LWR(InstructionClass.LOAD_STORE_REGISTER),
LBR(InstructionClass.LOAD_STORE_REGISTER),
SWR(InstructionClass.LOAD_STORE_REGISTER),
SBR(InstructionClass.LOAD_STORE_REGISTER),
LW(InstructionClass.LOAD_STORE, 4),
LWO(InstructionClass.LOAD_STORE, 4),
LI(InstructionClass.LOAD_STORE, 4),
LB(InstructionClass.LOAD_STORE, 4),
LBO(InstructionClass.LOAD_STORE, 4),
SW(InstructionClass.LOAD_STORE, 4),
SWO(InstructionClass.LOAD_STORE, 4),
SB(InstructionClass.LOAD_STORE, 4),
SBO(InstructionClass.LOAD_STORE, 4),
JR(InstructionClass.JUMP_REGISTER),
J(InstructionClass.JUMP),
JZ(InstructionClass.JUMP),
JNZ(InstructionClass.JUMP),
JAL(InstructionClass.JUMP),
META(InstructionClass.MISC),
ERROR(InstructionClass.MISC),
;
enum class InstructionClass {
MISC,
ALU,
STACK,
TEST,
LOAD_STORE_REGISTER,
LOAD_STORE,
JUMP_REGISTER,
JUMP
}
companion object {
fun fromString(string: String): InstructionType? {
try {
return valueOf(string.uppercase())
} catch (e: IllegalArgumentException) {
return null
}
}
}
}

View File

@@ -0,0 +1,69 @@
package mtmc.asm.instructions
import mtmc.asm.Assembler
import mtmc.tokenizer.MTMCToken
import mtmc.util.BinaryUtils
class JumpInstruction(
type: InstructionType,
label: List<MTMCToken>,
instructionToken: MTMCToken
) : Instruction(type, label, instructionToken) {
private var addressToken: MTMCToken? = null
fun setAddressToken(addressToken: MTMCToken) {
this.addressToken = addressToken
}
override fun validateLabel(assembler: Assembler) {
if (addressToken!!.type == MTMCToken.TokenType.IDENTIFIER) {
if (!assembler.hasLabel(addressToken!!.stringValue)) {
addError("Unresolved label: " + addressToken!!.stringValue)
}
}
}
private fun resolveTargetAddress(assembler: Assembler): Int? {
if (addressToken!!.type == MTMCToken.TokenType.IDENTIFIER) {
return assembler.resolveLabel(addressToken!!.stringValue)
} else {
return addressToken!!.intValue()
}
}
override fun genCode(output: ByteArray, assembler: Assembler) {
var opcode = 0
when (type) {
InstructionType.J -> opcode = 12
InstructionType.JZ -> opcode = 13
InstructionType.JNZ -> opcode = 14
InstructionType.JAL -> opcode = 15
else -> println("Invalid instruction type")
}
val address = resolveTargetAddress(assembler)!!
output[location] = (opcode shl 4 or (address ushr 8)).toByte()
output[location + 1] = address.toByte()
}
companion object {
fun disassemble(instruction: Short): String? {
if (BinaryUtils.getBits(16, 2, instruction).toInt() == 3) {
val jumpType = BinaryUtils.getBits(14, 2, instruction)
val sb = StringBuilder()
if (jumpType.toInt() == 0) {
sb.append("j")
} else if (jumpType.toInt() == 1) {
sb.append("jz")
} else if (jumpType.toInt() == 2) {
sb.append("jnz")
} else if (jumpType.toInt() == 3) {
sb.append("jal")
}
val target = BinaryUtils.getBits(12, 12, instruction)
sb.append(" ").append(target.toInt())
return sb.toString()
}
return null
}
}
}

View File

@@ -0,0 +1,37 @@
package mtmc.asm.instructions
import mtmc.asm.Assembler
import mtmc.emulator.Register.Companion.fromInteger
import mtmc.emulator.Register.Companion.toInteger
import mtmc.tokenizer.MTMCToken
import mtmc.util.BinaryUtils
class JumpRegisterInstruction(
type: InstructionType,
label: List<MTMCToken>,
instructionToken: MTMCToken
) : Instruction(type, label, instructionToken) {
private var register: MTMCToken? = null
fun setRegister(register: MTMCToken) {
this.register = register
}
public override fun genCode(output: ByteArray, assembler: Assembler) {
val opcode = 9
output[location] = (opcode shl 4).toByte()
val reg = toInteger(register!!.stringValue)
output[location + 1] = reg.toByte()
}
companion object {
fun disassemble(instruction: Short): String? {
if (BinaryUtils.getBits(16, 5, instruction).toInt() == 9) {
val reg = BinaryUtils.getBits(4, 4, instruction)
val sb = StringBuilder("jr")
sb.append(fromInteger(reg.toInt()))
}
return null
}
}
}

View File

@@ -0,0 +1,115 @@
package mtmc.asm.instructions
import mtmc.asm.Assembler
import mtmc.emulator.Register.Companion.fromInteger
import mtmc.emulator.Register.Companion.toInteger
import mtmc.tokenizer.MTMCToken
import mtmc.util.BinaryUtils
class LoadStoreInstruction(
type: InstructionType,
label: List<MTMCToken>,
instructionToken: MTMCToken
) : Instruction(type, label, instructionToken) {
private var targetToken: MTMCToken? = null
private var offsetToken: MTMCToken? = null
private var value: MTMCToken? = null
fun setTargetToken(targetToken: MTMCToken) {
this.targetToken = targetToken
}
fun setOffsetToken(offsetToken: MTMCToken) {
this.offsetToken = offsetToken
}
fun setValue(value: MTMCToken) {
this.value = value
}
val isOffset: Boolean
get() = type.name.endsWith("O")
public override fun validateLabel(assembler: Assembler) {
if (value!!.type == MTMCToken.TokenType.IDENTIFIER) {
if (!assembler.hasLabel(value!!.stringValue)) {
addError("Unresolved label: " + value!!.stringValue)
}
}
}
private fun resolveValue(assembler: Assembler): Int? {
if (value!!.type == MTMCToken.TokenType.IDENTIFIER) {
return assembler.resolveLabel(value!!.stringValue)
} else {
return value!!.intValue()
}
}
override fun genCode(output: ByteArray, assembler: Assembler) {
val upperByte = when (type) {
InstructionType.LW -> 128
InstructionType.LWO -> 129
InstructionType.LB -> 130
InstructionType.LBO -> 131
InstructionType.SW -> 132
InstructionType.SWO -> 133
InstructionType.SB -> 134
InstructionType.SBO -> 135
InstructionType.LI -> 143
else -> 0
}
val target = toInteger(targetToken!!.stringValue)
output[location] = upperByte.toByte()
if (this.isOffset) {
val offsetReg = toInteger(offsetToken!!.stringValue)
output[location + 1] = (target shl 4 or offsetReg).toByte()
} else {
output[location + 1] = (target shl 4).toByte()
}
val numericValue = resolveValue(assembler)!!
output[location + 2] = (numericValue ushr 8).toByte()
output[location + 3] = numericValue.toByte()
}
companion object {
fun disassemble(instruction: Short): String? {
if (BinaryUtils.getBits(16, 4, instruction).toInt() == 8) {
val topNibble = BinaryUtils.getBits(12, 4, instruction)
val sb = StringBuilder()
if (topNibble.toInt() == 15) {
sb.append("li ")
} else if (topNibble.toInt() == 0) {
sb.append("lw ")
} else if (topNibble.toInt() == 1) {
sb.append("lwo ")
} else if (topNibble.toInt() == 2) {
sb.append("lb ")
} else if (topNibble.toInt() == 3) {
sb.append("lbo ")
} else if (topNibble.toInt() == 4) {
sb.append("sw ")
} else if (topNibble.toInt() == 5) {
sb.append("swo ")
} else if (topNibble.toInt() == 6) {
sb.append("sb ")
} else if (topNibble.toInt() == 7) {
sb.append("sbo ")
}
val target = BinaryUtils.getBits(8, 4, instruction)
val reg = fromInteger(target.toInt())
sb.append(reg)
if (topNibble.toInt() == 1 || topNibble.toInt() == 3 || topNibble.toInt() == 5 || topNibble.toInt() == 7) {
val offset = BinaryUtils.getBits(4, 4, instruction)
val offsetReg = fromInteger(offset.toInt())
sb.append(" ").append(offsetReg)
}
return sb.toString()
}
return null
}
}
}

View File

@@ -0,0 +1,75 @@
package mtmc.asm.instructions
import mtmc.asm.Assembler
import mtmc.emulator.Register
import mtmc.emulator.Register.Companion.fromInteger
import mtmc.emulator.Register.Companion.toInteger
import mtmc.tokenizer.MTMCToken
import mtmc.util.BinaryUtils
class LoadStoreRegisterInstruction(
type: InstructionType,
label: List<MTMCToken>,
instructionToken: MTMCToken
) : Instruction(type, label, instructionToken) {
private var targetToken: MTMCToken? = null
private var pointerToken: MTMCToken? = null
private var offsetToken: MTMCToken? = null
fun setTargetToken(targetToken: MTMCToken) {
this.targetToken = targetToken
}
fun setPointerToken(pointerToken: MTMCToken) {
this.pointerToken = pointerToken
}
fun setOffsetToken(offsetToken: MTMCToken?) {
this.offsetToken = offsetToken
}
override fun genCode(output: ByteArray, assembler: Assembler) {
var opcode = 0
when (type) {
InstructionType.LWR -> opcode = 4
InstructionType.LBR -> opcode = 5
InstructionType.SWR -> opcode = 6
InstructionType.SBR -> opcode = 7
else -> println("Invalid instruction type")
}
val target = toInteger(targetToken!!.stringValue)
val pointer = toInteger(pointerToken!!.stringValue)
var offset = Register.PC.ordinal
if (offsetToken != null) {
offset = toInteger(offsetToken!!.stringValue)
}
output[location] = (opcode shl 4 or target).toByte()
output[location + 1] = (pointer shl 4 or offset).toByte()
}
companion object {
fun disassemble(instruction: Short): String? {
if (BinaryUtils.getBits(16, 2, instruction).toInt() == 1) {
val builder = StringBuilder()
val type = BinaryUtils.getBits(14, 2, instruction)
if (type.toInt() == 0) {
builder.append("lwr ")
} else if (type.toInt() == 1) {
builder.append("lbr ")
} else if (type.toInt() == 2) {
builder.append("swr ")
} else if (type.toInt() == 3) {
builder.append("sbr ")
}
val srcDestReg = BinaryUtils.getBits(12, 4, instruction)
val addrReg = BinaryUtils.getBits(8, 4, instruction)
val offsetReg = BinaryUtils.getBits(4, 4, instruction)
builder.append(fromInteger(srcDestReg.toInt())).append(" ")
builder.append(fromInteger(addrReg.toInt())).append(" ")
builder.append(fromInteger(offsetReg.toInt()))
return builder.toString()
}
return null
}
}
}

View File

@@ -0,0 +1,90 @@
package mtmc.asm.instructions
import mtmc.asm.Assembler
import mtmc.tokenizer.MTMCToken
class MetaInstruction(instruction: MTMCToken) :
Instruction(InstructionType.META, listOf(), instruction) {
private var originalFilePath: MTMCToken? = null
private var originaLineNumber: MTMCToken? = null
private var globalName: MTMCToken? = null
private var globalLocation: MTMCToken? = null
private var globalType: MTMCToken? = null
private var localName: MTMCToken? = null
private var localOffset: MTMCToken? = null
private var localType: MTMCToken? = null
override fun genCode(output: ByteArray, assembler: Assembler) {
// do nothing
}
val isFileDirective: Boolean
get() = "file" == this.instructionToken.stringValue
val isLineDirective: Boolean
get() = "line" == this.instructionToken.stringValue
val isGlobalDirective: Boolean
get() = "global" == this.instructionToken.stringValue
val isLocalDirective: Boolean
get() = "local" == this.instructionToken.stringValue
val isEndLocalDirective: Boolean
get() = "endlocal" == this.instructionToken.stringValue
fun setOriginalFilePath(path: MTMCToken) {
this.originalFilePath = path
}
fun setOriginalLineNumber(lineNumber: MTMCToken) {
this.originaLineNumber = lineNumber
}
val originalLineNumber: Int
get() = this.originaLineNumber!!.intValue()
fun setGlobalInfo(name: MTMCToken, location: MTMCToken, type: MTMCToken) {
this.globalName = name
this.globalLocation = location
this.globalType = type
}
fun setLocalInfo(name: MTMCToken, offset: MTMCToken, type: MTMCToken) {
this.localName = name
this.localOffset = offset
this.localType = type
}
fun setEndLocalInfo(name: MTMCToken) {
this.localName = name
}
fun getOriginalFilePath(): String? {
return this.originalFilePath!!.stringValue
}
fun getGlobalName(): String? {
return this.globalName!!.stringValue
}
fun getGlobalLocation(): Int {
return this.globalLocation!!.intValue()
}
fun getGlobalType(): String? {
return this.globalType!!.stringValue
}
fun getLocalName(): String? {
return this.localName!!.stringValue
}
fun getLocalOffset(): Int {
return this.localOffset!!.intValue()
}
fun getLocalType(): String? {
return this.localType!!.stringValue
}
}

View File

@@ -0,0 +1,145 @@
package mtmc.asm.instructions
import mtmc.asm.Assembler
import mtmc.emulator.Register.Companion.fromInteger
import mtmc.emulator.Register.Companion.toInteger
import mtmc.os.SysCall
import mtmc.tokenizer.MTMCToken
import mtmc.util.BinaryUtils
class MiscInstruction(
type: InstructionType,
labels: List<MTMCToken>,
instructionToken: MTMCToken
) : Instruction(type, labels, instructionToken) {
private var syscallType: MTMCToken? = null
private var fromRegister: MTMCToken? = null
private var toRegister: MTMCToken? = null
private var value: MTMCToken? = null
fun setSyscallType(type: MTMCToken) {
this.syscallType = type
}
fun setFrom(fromRegister: MTMCToken) {
this.fromRegister = fromRegister
}
fun setTo(toRegister: MTMCToken) {
this.toRegister = toRegister
}
fun setValue(value: MTMCToken?) {
this.value = value
}
override fun genCode(output: ByteArray, assembler: Assembler) {
if (type == InstructionType.SYS) {
output[location] = 0
output[location + 1] = SysCall.getValue(this.syscallType!!.stringValue)
} else if (type == InstructionType.MOV) {
val to = toInteger(toRegister!!.stringValue)
val from = toInteger(fromRegister!!.stringValue)
output[location] = 1
output[location + 1] = (to shl 4 or from).toByte()
} else if (type == InstructionType.INC) {
output[location] = 2
val to = toInteger(toRegister!!.stringValue)
var immediateVal = 1
if (value != null) {
immediateVal = value!!.intValue()
}
output[location + 1] = (to shl 4 or immediateVal).toByte()
} else if (type == InstructionType.DEC) {
output[location] = 3
val to = toInteger(toRegister!!.stringValue)
var immediateVal = 1
if (value != null) {
immediateVal = value!!.intValue()
}
output[location + 1] = (to shl 4 or immediateVal).toByte()
} else if (type == InstructionType.SETI) {
output[location] = 4
val to = toInteger(toRegister!!.stringValue)
val immediateVal = value!!.intValue()
output[location + 1] = (to shl 4 or immediateVal).toByte()
} else if (type == InstructionType.MCP) {
output[location] = 5
val from = toInteger(fromRegister!!.stringValue)
val to = toInteger(toRegister!!.stringValue)
val value = this.value!!.intValue()
output[location + 1] = (from shl 4 or to).toByte()
output[location + 2] = (value shl 8).toByte()
output[location + 3] = (value and 0xFF).toByte()
} else if (type == InstructionType.DEBUG) {
output[location] = 8
output[location + 1] = value!!.intValue().toByte()
} else if (type == InstructionType.NOP) {
output[location] = 15
output[location + 1] = 255.toByte()
}
}
companion object {
fun disassemble(instruction: Short): String? {
if (BinaryUtils.getBits(16, 4, instruction).toInt() == 0) {
val topNibble = BinaryUtils.getBits(12, 4, instruction)
if (topNibble.toInt() == 0) {
val builder = StringBuilder("sys ")
val bits = BinaryUtils.getBits(8, 8, instruction)
val name = SysCall.getString(bits.toByte())
builder.append(name)
return builder.toString()
} else if (topNibble.toInt() == 1) {
val builder = StringBuilder("mov ")
val to = BinaryUtils.getBits(8, 4, instruction)
val from = BinaryUtils.getBits(4, 4, instruction)
val toName = fromInteger(to.toInt())
builder.append(toName).append(" ")
val fromName = fromInteger(from.toInt())
builder.append(fromName)
return builder.toString()
} else if (topNibble.toInt() == 2) {
val builder = StringBuilder("inc ")
val to = BinaryUtils.getBits(8, 4, instruction)
val amount = BinaryUtils.getBits(4, 4, instruction)
val toName = fromInteger(to.toInt())
builder.append(toName).append(" ")
builder.append(amount.toInt())
return builder.toString()
} else if (topNibble.toInt() == 3) {
val builder = StringBuilder("dec ")
val to = BinaryUtils.getBits(8, 4, instruction)
val amount = BinaryUtils.getBits(4, 4, instruction)
val toName = fromInteger(to.toInt())
builder.append(toName).append(" ")
builder.append(amount.toInt())
return builder.toString()
} else if (topNibble.toInt() == 4) {
val builder = StringBuilder("seti ")
val to = BinaryUtils.getBits(8, 4, instruction)
val amount = BinaryUtils.getBits(4, 4, instruction)
val toName = fromInteger(to.toInt())
builder.append(toName).append(" ")
builder.append(amount.toInt())
return builder.toString()
} else if (topNibble.toInt() == 5) {
val from = BinaryUtils.getBits(8, 4, instruction)
val fromName = fromInteger(from.toInt())
val to = BinaryUtils.getBits(4, 4, instruction)
val toName = fromInteger(to.toInt())
return "mcp " + fromName + " " + toName
} else if (topNibble.toInt() == 8) {
val builder = StringBuilder("debug ")
val stringIndex = BinaryUtils.getBits(8, 8, instruction)
builder.append(stringIndex.toInt())
return builder.toString()
} else if (topNibble.toInt() == 15) {
val builder = StringBuilder("noop")
return builder.toString()
}
}
return null
}
}
}

View File

@@ -0,0 +1,108 @@
package mtmc.asm.instructions
import mtmc.asm.Assembler
import mtmc.asm.instructions.ALUOp.Companion.fromInt
import mtmc.emulator.Register
import mtmc.emulator.Register.Companion.fromInteger
import mtmc.tokenizer.MTMCToken
import mtmc.util.BinaryUtils
class StackInstruction(
type: InstructionType,
label: List<MTMCToken>,
instructionToken: MTMCToken
) : Instruction(type, label, instructionToken) {
private var targetToken: MTMCToken? = null
private var stackRegisterToken: MTMCToken? = null
private var aluOpToken: MTMCToken? = null
private var value: MTMCToken? = null
fun setTarget(target: MTMCToken) {
this.targetToken = target
}
fun setStackRegister(stackRegister: MTMCToken?) {
this.stackRegisterToken = stackRegister
}
fun setALUOp(aluOp: MTMCToken) {
this.aluOpToken = aluOp
}
fun setValue(value: MTMCToken) {
this.value = value
}
override fun genCode(output: ByteArray, assembler: Assembler) {
var stackReg = Register.SP.ordinal
if (stackRegisterToken != null) {
stackReg = Register.Companion.toInteger(stackRegisterToken!!.stringValue)
}
if (type == InstructionType.PUSH) {
val target = Register.Companion.toInteger(targetToken!!.stringValue)
output[location] = 32
output[location + 1] = (target shl 4 or stackReg).toByte()
} else if (type == InstructionType.POP) {
val target = Register.Companion.toInteger(targetToken!!.stringValue)
output[location] = 33
output[location + 1] = (target shl 4 or stackReg).toByte()
} else if (type == InstructionType.SOP) {
val aluOp = ALUOp.toInteger(aluOpToken!!.stringValue)
output[location] = 39
output[location + 1] = (aluOp shl 4 or stackReg).toByte()
} else if (type == InstructionType.PUSHI) {
val immediateValue = value!!.intValue()
output[location] = 47
output[location + 1] = stackReg.toByte()
output[location + 2] = (immediateValue ushr 8).toByte()
output[location + 3] = immediateValue.toByte()
} else {
val stackOp: Int
stackOp = when (type) {
InstructionType.DUP -> 34
InstructionType.SWAP -> 35
InstructionType.DROP -> 36
InstructionType.OVER -> 37
InstructionType.ROT -> 38
else -> 0
}
output[location] = stackOp.toByte()
output[location + 1] = stackReg.toByte()
}
}
companion object {
fun disassemble(instruction: Short): String? {
if (BinaryUtils.getBits(16, 4, instruction).toInt() == 2) {
val opCode = BinaryUtils.getBits(12, 4, instruction)
val stackReg = BinaryUtils.getBits(4, 4, instruction)
if (opCode.toInt() == 0) {
val sourceReg = BinaryUtils.getBits(8, 4, instruction)
return "push " + fromInteger(sourceReg.toInt()) + " " + fromInteger(stackReg.toInt())
}
if (opCode.toInt() == 1) {
val destReg = BinaryUtils.getBits(8, 4, instruction)
return "pop " + fromInteger(destReg.toInt()) + " " + fromInteger(stackReg.toInt())
}
if (opCode.toInt() == 2) {
return "dup " + fromInteger(stackReg.toInt())
} else if (opCode.toInt() == 3) {
return "swap " + fromInteger(stackReg.toInt())
} else if (opCode.toInt() == 4) {
return "drop " + fromInteger(stackReg.toInt())
} else if (opCode.toInt() == 5) {
return "over " + fromInteger(stackReg.toInt())
} else if (opCode.toInt() == 6) {
return "rot " + fromInteger(stackReg.toInt())
} else if (opCode.toInt() == 7) {
val aluOp = BinaryUtils.getBits(8, 4, instruction)
val op = fromInt(aluOp)
return "sop " + op + " " + fromInteger(stackReg.toInt())
} else if (opCode.toInt() == 15) {
return "pushi " + fromInteger(stackReg.toInt())
}
}
return null
}
}
}

View File

@@ -0,0 +1,126 @@
package mtmc.asm.instructions
import mtmc.asm.Assembler
import mtmc.emulator.Register.Companion.fromInteger
import mtmc.emulator.Register.Companion.toInteger
import mtmc.tokenizer.MTMCToken
import mtmc.util.BinaryUtils
class TestInstruction(
type: InstructionType,
label: List<MTMCToken>,
instructionToken: MTMCToken
) : Instruction(type, label, instructionToken) {
private var first: MTMCToken? = null
private var second: MTMCToken? = null
private var value: MTMCToken? = null
fun setFirst(to: MTMCToken) {
this.first = to
}
fun setSecond(from: MTMCToken) {
this.second = from
}
fun setImmediateValue(value: MTMCToken) {
this.value = value
}
val isImmediate: Boolean
get() = type.name.endsWith("I")
override fun genCode(output: ByteArray, assembler: Assembler) {
val firstReg = toInteger(first!!.stringValue)
if (this.isImmediate) {
val upperByte = when (type) {
InstructionType.EQI -> 56
InstructionType.NEQI -> 57
InstructionType.GTI -> 58
InstructionType.GTEI -> 59
InstructionType.LTI -> 60
InstructionType.LTEI -> 61
else -> 0
}
val immediateValue = value!!.intValue()
output[location] = upperByte.toByte()
output[location + 1] = (firstReg shl 4 or immediateValue).toByte()
} else {
val upperByte = when (type) {
InstructionType.EQ -> 48
InstructionType.NEQ -> 49
InstructionType.GT -> 50
InstructionType.GTE -> 51
InstructionType.LT -> 52
InstructionType.LTE -> 53
else -> 0
}
val secondReg = toInteger(second!!.stringValue)
output[location] = upperByte.toByte()
output[location + 1] = (firstReg shl 4 or secondReg).toByte()
}
}
companion object {
fun disassemble(instruction: Short): String? {
if (BinaryUtils.getBits(16, 4, instruction).toInt() == 3) {
val sb = StringBuilder()
val opCode = BinaryUtils.getBits(12, 4, instruction)
val thirdNibble = BinaryUtils.getBits(8, 4, instruction)
val fourthNibble = BinaryUtils.getBits(4, 4, instruction)
if (opCode.toInt() == 0) {
sb.append("eq ")
sb.append(fromInteger(thirdNibble.toInt()))
.append(" ").append(fromInteger(fourthNibble.toInt()))
} else if (opCode.toInt() == 1) {
sb.append("neq ")
sb.append(fromInteger(thirdNibble.toInt()))
.append(" ").append(fromInteger(fourthNibble.toInt()))
} else if (opCode.toInt() == 2) {
sb.append("gt ")
sb.append(fromInteger(thirdNibble.toInt()))
.append(" ").append(fromInteger(fourthNibble.toInt()))
} else if (opCode.toInt() == 3) {
sb.append("gte ")
sb.append(fromInteger(thirdNibble.toInt()))
.append(" ").append(fromInteger(fourthNibble.toInt()))
} else if (opCode.toInt() == 4) {
sb.append("lt ")
sb.append(fromInteger(thirdNibble.toInt()))
.append(" ").append(fromInteger(fourthNibble.toInt()))
} else if (opCode.toInt() == 5) {
sb.append("lte ")
sb.append(fromInteger(thirdNibble.toInt()))
.append(" ").append(fromInteger(fourthNibble.toInt()))
}
if (opCode.toInt() == 8) {
sb.append("eqi ")
sb.append(fromInteger(thirdNibble.toInt()))
.append(" ").append(fourthNibble.toInt())
} else if (opCode.toInt() == 9) {
sb.append("neqi ")
sb.append(fromInteger(thirdNibble.toInt()))
.append(" ").append(fourthNibble.toInt())
} else if (opCode.toInt() == 10) {
sb.append("gti ")
sb.append(fromInteger(thirdNibble.toInt()))
.append(" ").append(fourthNibble.toInt())
} else if (opCode.toInt() == 11) {
sb.append("gtei ")
sb.append(fromInteger(thirdNibble.toInt()))
.append(" ").append(fourthNibble.toInt())
} else if (opCode.toInt() == 12) {
sb.append("lti ")
sb.append(fromInteger(thirdNibble.toInt()))
.append(" ").append(fourthNibble.toInt())
} else if (opCode.toInt() == 13) {
sb.append("ltei ")
sb.append(fromInteger(thirdNibble.toInt()))
.append(" ").append(fourthNibble.toInt())
}
return sb.toString()
}
return null
}
}
}