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

2
.idea/misc.xml generated
View File

@@ -3,7 +3,7 @@
<component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="corretto-21" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="corretto-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
<component name="accountSettings">

View File

@@ -1,70 +1,75 @@
@file:OptIn(ExperimentalDistributionDsl::class)
import org.codehaus.groovy.tools.shell.util.Logger.io
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalDistributionDsl
plugins {
kotlin("multiplatform") version "2.1.20"
kotlin("multiplatform") version "2.1.20"
}
group = "nl.astraeus"
version = "0.1.0-SNAPSHOT"
repositories {
mavenCentral()
maven {
url = uri("https://gitea.astraeus.nl/api/packages/rnentjes/maven")
}
maven {
url = uri("https://gitea.astraeus.nl:8443/api/packages/rnentjes/maven")
}
mavenCentral()
maven {
url = uri("https://gitea.astraeus.nl/api/packages/rnentjes/maven")
}
maven {
url = uri("https://gitea.astraeus.nl:8443/api/packages/rnentjes/maven")
}
}
kotlin {
jvmToolchain(17)
jvm()
js {
binaries.executable()
browser {
distribution {
outputDirectory.set(File("$projectDir/web/"))
}
}
jvmToolchain(21)
jvm()
js {
binaries.executable()
browser {
distribution {
outputDirectory.set(File("$projectDir/web/"))
}
}
sourceSets {
val commonMain by getting {
dependencies {
api("nl.astraeus:kotlin-simple-logging:1.1.1")
api("nl.astraeus:kotlin-css-generator:1.0.10")
}
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.0")
}
}
val commonTest by getting
val jvmMain by getting {
dependencies {
implementation("io.undertow:undertow-core:2.3.14.Final")
implementation("org.jetbrains.kotlinx:kotlinx-html-jvm:0.11.0")
// Add this to enable ExperimentalStdlibApi globally
sourceSets.all {
languageSettings.optIn("kotlin.ExperimentalStdlibApi")
}
implementation("org.xerial:sqlite-jdbc:3.32.3.2")
implementation("com.zaxxer:HikariCP:4.0.3")
implementation("nl.astraeus:simple-jdbc-stats:1.6.1") {
exclude(group = "org.slf4j", module = "slf4j-api")
}
sourceSets {
val commonMain by getting {
dependencies {
api("nl.astraeus:kotlin-simple-logging:1.1.1")
api("nl.astraeus:kotlin-css-generator:1.0.10")
implementation("io.pebbletemplates:pebble:3.2.3")
implementation("com.google.code.gson:gson:2.12.1")
}
}
val jvmTest by getting {
dependencies {
}
}
val jsMain by getting {
dependencies {
implementation("nl.astraeus:kotlin-komponent:1.2.5")
}
}
val jsTest by getting
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.0")
}
}
}
val commonTest by getting
val jvmMain by getting {
dependencies {
implementation("io.undertow:undertow-core:2.3.14.Final")
implementation("org.jetbrains.kotlinx:kotlinx-html-jvm:0.11.0")
implementation("org.xerial:sqlite-jdbc:3.32.3.2")
implementation("com.zaxxer:HikariCP:4.0.3")
implementation("nl.astraeus:simple-jdbc-stats:1.6.1") {
exclude(group = "org.slf4j", module = "slf4j-api")
}
implementation("io.pebbletemplates:pebble:3.2.3")
implementation("com.google.code.gson:gson:2.12.1")
}
}
val jvmTest by getting {
dependencies {
}
}
val jsMain by getting {
dependencies {
implementation("nl.astraeus:kotlin-komponent:1.2.5")
}
}
val jsTest by getting
}
}

View File

@@ -4,9 +4,8 @@ import mtmc.tokenizer.MTMCToken
abstract class ASMElement(
val labels: List<MTMCToken>,
@JvmField var lineNumber: Int
var lineNumber: Int
) : HasLocation {
@JvmField
var errors: MutableList<ASMError> = ArrayList()
override var location: Int = 0
override var sizeInBytes: Int = 0

View File

@@ -2,10 +2,9 @@ package mtmc.asm
import mtmc.tokenizer.MTMCToken
@JvmRecord
data class ASMError(
@JvmField val token: MTMCToken,
@JvmField val error: String
val token: MTMCToken,
val error: String
) {
fun formattedErrorMessage(): String {
return "Line " + token.line + ": " + error

View File

@@ -25,12 +25,16 @@ 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
import java.io.File
import java.nio.charset.StandardCharsets
import java.util.*
import java.util.stream.Collectors
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>()
@@ -88,7 +92,7 @@ class Assembler {
var location = 0
var originalLineNumber = 0
val currentLocals: MutableMap<String?, LocalInfo?> = TreeMap<String?, LocalInfo?>()
val currentLocals: MutableMap<String?, LocalInfo?> = mutableMapOf()
for (instruction in instructions) {
val asmLineNumber = instruction.lineNumber
if (instruction is MetaInstruction) {
@@ -145,10 +149,8 @@ class Assembler {
val result = assemble(srcName, asm)
if (!result.errors.isEmpty()) {
throw RuntimeException(
"Errors:\n" + result.errors
.stream()
.map<String> { e: ASMError? -> " - " + e!!.formattedErrorMessage() }
.collect(Collectors.joining("\n")))
"Errors:\n" + result.errors.joinToString("\n") { e: ASMError? -> " - " + e!!.formattedErrorMessage() }
)
}
return Executable(
@@ -261,7 +263,10 @@ class Assembler {
for (labelToken in labelTokens) {
val labelData = Data(labelTokens, labelToken.line)
if (hasLabel(labelToken.stringValue)) {
labelData.addError(tokens.poll(), "Label already defined: " + labelToken.stringValue)
labelData.addError(
tokens.removeFirst(),
"Label already defined: " + labelToken.stringValue
)
} else {
this.labels.put(labelToken.labelValue(), labelData)
}
@@ -274,7 +279,7 @@ class Assembler {
}
}
private fun parseMetaDirective(tokens: LinkedList<MTMCToken>): Boolean {
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())
@@ -306,26 +311,26 @@ class Assembler {
return false
}
private fun parseModeFlag(tokens: LinkedList<MTMCToken>): ASMMode? {
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(Locale.getDefault()))
return ASMMode.valueOf(tokens.get(1).stringValue.uppercase())
}
return null
}
private fun parseData(tokens: LinkedList<MTMCToken>, labelTokens: MutableList<MTMCToken>) {
private fun parseData(tokens: MutableList<MTMCToken>, labelTokens: MutableList<MTMCToken>) {
lastLabels = mutableListOf()
var dataToken = tokens.poll()
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.toByteArray(StandardCharsets.US_ASCII)
val stringBytes = dataToken.stringValue.encodeToByteArray()
val nullTerminated = ByteArray(stringBytes.size + 1)
System.arraycopy(stringBytes, 0, nullTerminated, 0, stringBytes.size)
stringBytes.copyInto(nullTerminated, 0, 0, stringBytes.size)
nullTerminated[stringBytes.size] = '\u0000'.code.toByte()
dataElt.setValue(dataToken, nullTerminated)
} else if (isInteger(dataToken)) {
@@ -338,7 +343,7 @@ class Assembler {
byteArrayOf((integerValue ushr 8).toByte(), integerValue.toByte())
)
} else if (dataToken.type == MTMCToken.TokenType.DOT) {
dataToken = tokens.poll()
dataToken = tokens.removeFirst()
dataElt = Data(labelTokens, dataToken.line)
if (dataToken.stringValue == "int") {
val intToken = requireIntegerToken(tokens, dataElt, MonTanaMiniComputer.MEMORY_SIZE)
@@ -359,7 +364,7 @@ class Assembler {
dataElt.addError(dataToken, "only data types are .int, .byte, and .image")
}
} else if (dataToken.type == MTMCToken.TokenType.MINUS) {
val nextToken = tokens.poll() // get next
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 {
@@ -380,7 +385,7 @@ class Assembler {
for (labelToken in labelTokens) {
if (hasLabel(labelToken.stringValue)) {
dataElt.addError(tokens.poll(), "Label already defined: " + labelToken.stringValue)
dataElt.addError(tokens.removeFirst(), "Label already defined: " + labelToken.stringValue)
} else {
labels.put(labelToken.labelValue(), dataElt)
}
@@ -405,9 +410,16 @@ class Assembler {
return graphic
}
private fun parseInstruction(tokens: LinkedList<MTMCToken>, labelTokens: MutableList<MTMCToken>) {
private fun parseInstruction(
tokens: MutableList<MTMCToken>,
labelTokens: MutableList<MTMCToken>
) {
var tokens = tokens
var instructionToken = tokens.peekFirst()
var instructionToken = if (tokens.isEmpty()) {
null
} else {
tokens[0]
}
if (instructionToken == null) return
lastLabels = mutableListOf()
@@ -510,7 +522,7 @@ class Assembler {
}
// if there is a stack register specified, consume it
if (!tokens.isEmpty() && tokens.peekFirst().type == MTMCToken.TokenType.IDENTIFIER) {
if (!tokens.isEmpty() && tokens.peekFirst()?.type == MTMCToken.TokenType.IDENTIFIER) {
val stackReg = requireReadableRegister(tokens, stackInst)
stackInst.setStackRegister(stackReg)
}
@@ -550,7 +562,7 @@ class Assembler {
loadInst.setPointerToken(pointerReg)
// if there is an offset register specified, consume it
if (!tokens.isEmpty() && tokens.peekFirst().type == MTMCToken.TokenType.IDENTIFIER) {
if (!tokens.isEmpty() && tokens.peekFirst()?.type == MTMCToken.TokenType.IDENTIFIER) {
val offsetReg = requireReadableRegister(tokens, loadInst)
loadInst.setOffsetToken(offsetReg)
}
@@ -598,7 +610,7 @@ class Assembler {
//===================================================
// tokenization helper functions
//===================================================
private fun requireSysCall(tokens: LinkedList<MTMCToken>, inst: Instruction): MTMCToken {
private fun requireSysCall(tokens: MutableList<MTMCToken>, inst: Instruction): MTMCToken {
val sysCallType = tokens.poll()
if (sysCallType == null) {
inst.addError("Syscall required")
@@ -610,7 +622,7 @@ class Assembler {
return sysCallType!!
}
private fun requireAluOp(tokens: LinkedList<MTMCToken>, inst: Instruction): MTMCToken {
private fun requireAluOp(tokens: MutableList<MTMCToken>, inst: Instruction): MTMCToken {
val sysCallType = tokens.poll()
if (sysCallType == null) {
inst.addError("Syscall required")
@@ -623,7 +635,7 @@ class Assembler {
}
private fun requireWriteableRegister(
tokens: LinkedList<MTMCToken>,
tokens: MutableList<MTMCToken>,
instruction: Instruction
): MTMCToken {
val nextToken = tokens.poll()
@@ -638,7 +650,7 @@ class Assembler {
}
private fun requireReadableRegister(
tokens: LinkedList<MTMCToken>,
tokens: MutableList<MTMCToken>,
instruction: Instruction
): MTMCToken {
val nextToken = tokens.poll()
@@ -652,7 +664,7 @@ class Assembler {
return nextToken!!
}
private fun requireALUOp(tokens: LinkedList<MTMCToken>, instruction: Instruction): MTMCToken {
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")
@@ -660,7 +672,7 @@ class Assembler {
return nextToken!!
}
private fun requireString(tokens: LinkedList<MTMCToken>, instruction: ASMElement): MTMCToken {
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")
@@ -669,7 +681,7 @@ class Assembler {
}
private fun requireIntegerToken(
tokens: LinkedList<MTMCToken>,
tokens: MutableList<MTMCToken>,
inst: ASMElement,
max: Int
): MTMCToken {
@@ -688,7 +700,7 @@ class Assembler {
}
private fun requireToken(
tokens: LinkedList<MTMCToken>,
tokens: MutableList<MTMCToken>,
type: MTMCToken.TokenType,
inst: ASMElement
): MTMCToken? {
@@ -700,7 +712,7 @@ class Assembler {
}
private fun requireIntegerOrLabelReferenceToken(
tokens: LinkedList<MTMCToken>,
tokens: MutableList<MTMCToken>,
inst: LoadStoreInstruction
): MTMCToken {
val token = tokens.poll()
@@ -713,9 +725,9 @@ class Assembler {
}
private val tokensForLine: LinkedList<MTMCToken>
private val tokensForLine: MutableList<MTMCToken>
get() {
val tokens = LinkedList<MTMCToken>()
val tokens = mutableListOf<MTMCToken>()
if (tokenizer!!.more()) {
val first = tokenizer!!.consume()
tokens.add(first)
@@ -742,51 +754,51 @@ class Assembler {
}
companion object {
fun transformSyntheticInstructions(tokens: LinkedList<MTMCToken>): LinkedList<MTMCToken> {
fun transformSyntheticInstructions(tokens: MutableList<MTMCToken>): MutableList<MTMCToken> {
if (!tokens.isEmpty()) {
val first = tokens.peekFirst()
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.addFirst(syntheticImmediate.cloneWithVal(op))
tokens.addFirst(syntheticImmediate.cloneWithVal("imm"))
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.addFirst(syntheticImmediate.cloneWithVal(op))
tokens.addFirst(syntheticImmediate.cloneWithVal("sop"))
tokens.add(0, syntheticImmediate.cloneWithVal(op))
tokens.add(0, syntheticImmediate.cloneWithVal("sop"))
}
} else if (stringVal == "la") {
val syntheticImmediate = tokens.removeFirst()
tokens.addFirst(syntheticImmediate.cloneWithVal("li"))
tokens.add(0, syntheticImmediate.cloneWithVal("li"))
} else if (stringVal == "ret") {
val syntheticImmediate = tokens.removeFirst()
tokens.addFirst(syntheticImmediate.cloneWithVal("ra"))
tokens.addFirst(syntheticImmediate.cloneWithVal("jr"))
tokens.add(0, syntheticImmediate.cloneWithVal("ra"))
tokens.add(0, syntheticImmediate.cloneWithVal("jr"))
}
}
}
return tokens
}
private fun maybeGetLabels(tokens: LinkedList<MTMCToken>): MutableList<MTMCToken> {
val labels = LinkedList<MTMCToken>()
while (!tokens.isEmpty() && tokens.getFirst().type == MTMCToken.TokenType.LABEL) {
val label = tokens.poll()
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: LinkedList<MTMCToken>): MTMCToken? {
private fun maybeGetLabelReference(tokens: MutableList<MTMCToken>): MTMCToken? {
var label: MTMCToken? = null
if (tokens.getFirst().type == MTMCToken.TokenType.IDENTIFIER) {
label = tokens.poll()
if (tokens.first().type == MTMCToken.TokenType.IDENTIFIER) {
label = tokens.removeFirst()
}
return label
}

View File

@@ -2,13 +2,12 @@ package mtmc.asm
import mtmc.emulator.DebugInfo
@JvmRecord
data class AssemblyResult(
@JvmField val code: ByteArray,
val code: ByteArray,
val data: ByteArray,
val graphics: Array<ByteArray>,
@JvmField val debugInfo: DebugInfo?,
@JvmField val errors: MutableList<ASMError>
val debugInfo: DebugInfo?,
val errors: MutableList<ASMError>
) {
fun printErrors(): String {
val builder = StringBuilder("Errors:\n")

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

@@ -5,7 +5,6 @@ import mtmc.emulator.Register.Companion.fromInteger
import mtmc.emulator.Register.Companion.toInteger
import mtmc.tokenizer.MTMCToken
import mtmc.util.BinaryUtils
import java.util.Locale
class ALUInstruction(
type: InstructionType,
@@ -38,7 +37,7 @@ class ALUInstruction(
}
val isBinaryOp: Boolean
get() = !ALUOp.valueOf(instructionToken.stringValue.uppercase(Locale.getDefault())).isUnary
get() = !ALUOp.valueOf(instructionToken.stringValue.uppercase()).isUnary
override fun genCode(output: ByteArray, assembler: Assembler) {
val opCode = ALUOp.toInteger(instructionToken.stringValue)
@@ -62,13 +61,12 @@ class ALUInstruction(
}
companion object {
@JvmStatic
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(Locale.getDefault()))
val aluOp = ALUOp.valueOf(op.uppercase())
if (aluOp == ALUOp.IMM) {
builder.append(op).append(" ")
builder.append(fromInteger(BinaryUtils.getBits(8, 4, instruction).toInt())).append(" ")

View File

@@ -1,7 +1,5 @@
package mtmc.asm.instructions
import java.util.*
enum class ALUOp(val opCode: Int, val isUnary: Boolean) {
ADD(0x0000, false),
SUB(0x0001, false),
@@ -21,20 +19,17 @@ enum class ALUOp(val opCode: Int, val isUnary: Boolean) {
IMM(0x000F, true);
companion object {
@JvmStatic
fun toInteger(instruction: String): Int {
return valueOf(instruction.uppercase(Locale.getDefault())).opCode
return valueOf(instruction.uppercase()).opCode
}
@JvmStatic
fun fromInt(opCode: Short): String {
return entries[opCode.toInt()].name.lowercase(Locale.getDefault())
return entries[opCode.toInt()].name.lowercase()
}
@JvmStatic
fun isALUOp(op: String): Boolean {
try {
val aluOp = valueOf(op.uppercase(Locale.getDefault()))
val aluOp = valueOf(op.uppercase())
return true
} catch (e: IllegalArgumentException) {
return false

View File

@@ -7,9 +7,9 @@ import mtmc.emulator.MonTanaMiniComputer.Companion.isDoubleWordInstruction
import mtmc.tokenizer.MTMCToken
abstract class Instruction(
@JvmField val type: InstructionType,
val type: InstructionType,
labels: List<MTMCToken>,
@JvmField val instructionToken: MTMCToken
val instructionToken: MTMCToken
) : ASMElement(labels, instructionToken.line) {
open fun validateLabel(assembler: Assembler) {
// default does nothing
@@ -25,7 +25,6 @@ abstract class Instruction(
get() = type.sizeInBytes
companion object {
@JvmStatic
fun isInstruction(cmd: String): Boolean {
return InstructionType.fromString(cmd) != null
}

View File

@@ -1,9 +1,7 @@
package mtmc.asm.instructions
import java.util.*
enum class InstructionType @JvmOverloads constructor(
@JvmField val instructionClass: InstructionClass?,
enum class InstructionType constructor(
val instructionClass: InstructionClass?,
val sizeInBytes: Int = 2
) {
SYS(InstructionClass.MISC),
@@ -86,10 +84,9 @@ enum class InstructionType @JvmOverloads constructor(
}
companion object {
@JvmStatic
fun fromString(string: String): InstructionType? {
try {
return valueOf(string.uppercase(Locale.getDefault()))
return valueOf(string.uppercase())
} catch (e: IllegalArgumentException) {
return null
}

View File

@@ -0,0 +1,47 @@
package mtmc.emulator
import nl.astraeus.logger.getTimestamp
data class DebugInfo(
val debugStrings: MutableList<String>,
val assemblyFile: String?,
val assemblySource: String,
val assemblyLineNumbers: IntArray,
val originalFile: String,
val originalLineNumbers: IntArray,
val globals: Array<GlobalInfo>,
val locals: Array<Array<LocalInfo?>>
) {
fun handleDebugString(
debugIndex: Short,
monTanaMiniComputer: MonTanaMiniComputer
) {
val debugString = debugStrings!!.get(debugIndex.toInt())
/*
val compile = Pattern.compile("(\\$[a-zA-Z][a-zA-Z0-9])")
val matcher = compile.matcher(debugString)
val formattedString = StringBuilder()
var start = 0
var end: Int
while (matcher.find()) {
val match = matcher.group().substring(1)
try {
end = matcher.start()
formattedString.append(debugString, start, end)
val register = Register.valueOf(match.uppercase())
formattedString.append(monTanaMiniComputer.getRegisterValue(register).toInt())
start = matcher.end()
} catch (e: Exception) {
formattedString.append(match)
}
}
formattedString.append(debugString.substring(start))
println("DEBUG[" + getTimestamp() + "] : " + formattedString)
*/
println("DEBUG[" + getTimestamp() + "] : " + debugString)
}
data class GlobalInfo(val name: String?, val location: Int, val type: String?)
data class LocalInfo(val name: String?, val offset: Int, val type: String?)
}

View File

@@ -1,21 +1,23 @@
package mtmc.emulator
import mtmc.emulator.MonTanaMiniComputer.ComputerStatus
import mtmc.util.currentTimeMillis
import kotlin.math.max
/**
*
* @author jbanes
*/
class MTMCClock
(private val computer: MonTanaMiniComputer) {
class MTMCClock(
private val computer: MonTanaMiniComputer
) {
fun run() {
var instructions: Long = 0
var ips: Long = 0
var expected: Long = 0
var virtual: Long = 0
var startTime = System.currentTimeMillis()
var startTime = currentTimeMillis()
var deltaStart: Long
var delta: Long
@@ -28,8 +30,8 @@ class MTMCClock
pulse = (if (speed <= 0) 1000000 else max(speed / 100, 1))
ms = (if (pulse < 10) 1000 / speed else 10)
deltaStart = System.currentTimeMillis()
delta = ms - (System.currentTimeMillis() - deltaStart)
deltaStart = currentTimeMillis()
delta = ms - (currentTimeMillis() - deltaStart)
/* We've lost more than a second. Recalibrate. */
@@ -50,11 +52,11 @@ class MTMCClock
instructions += computer.pulse(pulse)
virtual += pulse
ips = (virtual * 1000) / max(1, System.currentTimeMillis() - startTime)
expected = (System.currentTimeMillis() - startTime) * speed / 1000
ips = (virtual * 1000) / max(1, currentTimeMillis() - startTime)
expected = (currentTimeMillis() - startTime) * speed / 1000
}
System.err.println("Executed " + instructions + " instructions at a rate of " + ips + " ips (speed = " + speed + ")")
//println("Executed " + instructions + " instructions at a rate of " + ips + " ips (speed = " + speed + ")")
}
fun step() {

View File

@@ -1,16 +1,17 @@
package mtmc.emulator
import mtmc.os.fs.Console
import mtmc.os.fs.System
import mtmc.os.shell.Shell
import mtmc.tokenizer.MTMCScanner
import mtmc.tokenizer.MTMCToken
import java.io.Console
class MTMCConsole(private val computer: MonTanaMiniComputer) {
var mode: Mode = Mode.NON_INTERACTIVE
var sysConsole: Console? = null
// non-interactive data
private val output = StringBuffer()
private val output = StringBuilder()
private var shortValueSet = false
private var shortValue: Short = 0
private var stringValue: String? = null
@@ -18,7 +19,7 @@ class MTMCConsole(private val computer: MonTanaMiniComputer) {
// TODO invert so shell is driving and console is just IO
fun start() {
mode = Mode.INTERACTIVE
sysConsole = System.console()
sysConsole = System.console
Shell.printShellWelcome(computer)
while (true) {
val cmd = sysConsole!!.readLine("mtmc > ")
@@ -48,7 +49,7 @@ class MTMCConsole(private val computer: MonTanaMiniComputer) {
if (mode == Mode.INTERACTIVE) {
val tokens = MTMCScanner(sysConsole!!.readLine(), "#").tokenize()
val token = tokens.first()
assert(token.type === MTMCToken.TokenType.CHAR)
check(token.type === MTMCToken.TokenType.CHAR)
return token.charValue()
} else {
this.shortValueSet = false
@@ -88,7 +89,7 @@ class MTMCConsole(private val computer: MonTanaMiniComputer) {
val text = if (index >= 0) output.substring(0, index + 1) else ""
if (index >= 0) {
output.delete(0, index + 1)
output.removeRange(0, index + 1)
}
return text
@@ -121,7 +122,7 @@ class MTMCConsole(private val computer: MonTanaMiniComputer) {
}
fun resetOutput() {
output.delete(0, output.length)
output.removeRange(0, output.length)
}
enum class Mode {

View File

@@ -1,7 +1,5 @@
package mtmc.emulator
import java.util.*
class MTMCIO {
var value: Int = 0
@@ -17,12 +15,12 @@ class MTMCIO {
}
fun keyPressed(key: String) {
val button = Buttons.valueOf(key.uppercase(Locale.getDefault()))
val button = Buttons.valueOf(key.uppercase())
value = value or button.mask
}
fun keyReleased(key: String) {
val button = Buttons.valueOf(key.uppercase(Locale.getDefault()))
val button = Buttons.valueOf(key.uppercase())
value = value and button.mask.inv()
}
}

View File

@@ -4,19 +4,7 @@ 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 mtmc.util.Runnable
import kotlin.experimental.and
import kotlin.experimental.inv
import kotlin.experimental.or
@@ -43,10 +31,10 @@ class MonTanaMiniComputer {
var display: MTMCDisplay = MTMCDisplay(this)
var clock: MTMCClock = MTMCClock(this)
var fileSystem: FileSystem = FileSystem(this)
var rewindSteps: LinkedList<RewindStep>? = LinkedList<RewindStep>()
var rewindSteps = mutableListOf<RewindStep>()
// listeners
private val observers: MutableList<MTMCObserver> = ArrayList<MTMCObserver>()
private val observers = mutableListOf<MTMCObserver>()
var debugInfo: DebugInfo? = null
private var currentRewindStep: RewindStep? = null
@@ -58,13 +46,15 @@ class MonTanaMiniComputer {
registerFile = ShortArray(Register.entries.size)
memory = ByteArray(MEMORY_SIZE)
breakpoints = ByteArray(MEMORY_SIZE)
rewindSteps = null
rewindSteps.clear()
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() })
rewindSteps.clear()
observers.forEach { obj ->
obj.computerReset()
}
}
fun load(code: ByteArray, data: ByteArray, debugInfo: DebugInfo?) {
@@ -79,11 +69,11 @@ class MonTanaMiniComputer {
val codeBoundary = code.size
System.arraycopy(code, 0, memory, 0, codeBoundary)
code.copyInto(memory, 0, 0, codeBoundary)
setRegisterValue(Register.CB, codeBoundary - 1)
val dataBoundary = codeBoundary + data.size
System.arraycopy(data, 0, memory, codeBoundary, data.size)
code.copyInto(memory, codeBoundary, 0, data.size)
setRegisterValue(Register.DB, dataBoundary - 1)
@@ -143,7 +133,9 @@ class MonTanaMiniComputer {
fun fetchAndExecute() {
currentRewindStep = RewindStep()
rewindSteps!!.push(currentRewindStep)
currentRewindStep?.let {
rewindSteps.add(0, it)
}
fetchCurrentInstruction()
val instruction = getRegisterValue(Register.IR)
if (isDoubleWordInstruction(instruction)) {
@@ -158,7 +150,7 @@ class MonTanaMiniComputer {
}
fun execInstruction(instruction: Short) {
observers!!.forEach(Consumer { o: MTMCObserver? -> o!!.beforeExecution(instruction) })
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()
@@ -710,7 +702,9 @@ class MonTanaMiniComputer {
} else {
badInstruction(instruction)
}
observers.forEach(Consumer { o: MTMCObserver? -> o!!.afterExecution(instruction) })
observers.forEach { o: MTMCObserver? ->
o!!.afterExecution(instruction)
}
}
val isFlagTestBitSet: Boolean
@@ -733,7 +727,7 @@ class MonTanaMiniComputer {
private fun badInstruction(instruction: Short) {
setStatus(ComputerStatus.PERMANENT_ERROR)
// TODO implement flags
console.println("BAD INSTRUCTION: 0x" + Integer.toHexString(instruction.toInt() and 0xFFFF))
console.println("BAD INSTRUCTION: 0x" + (instruction.toInt() and 0xFFFF).toHexString())
}
fun fetchCurrentInstruction() {
@@ -746,14 +740,16 @@ class MonTanaMiniComputer {
} else {
setRegisterValue(Register.DR, 0)
}
observers!!.forEach(Consumer { o: MTMCObserver? -> o!!.instructionFetched(instruction) })
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 Byte.toUnsignedInt(lowerByte)
val i = value.toInt() or lowerByte.toInt()
value = i.toShort()
return value
}
@@ -762,9 +758,8 @@ class MonTanaMiniComputer {
if (address < 0 || address >= memory.size) {
setStatus(ComputerStatus.PERMANENT_ERROR)
console.println(
"BAD MEMORY LOCATION ON READ: " + address + " (0x" + Integer.toHexString(
address and 0xFFFF
) + ")"
"BAD MEMORY LOCATION ON READ: $address (0x" +
(address and 0xFFFF).toHexString() + ")"
)
return 0
} else {
@@ -782,23 +777,24 @@ class MonTanaMiniComputer {
if (address < 0 || address >= memory.size) {
setStatus(ComputerStatus.PERMANENT_ERROR)
console.println(
"BAD MEMORY LOCATION ON WRITE: " + address + " (0x" + Integer.toHexString(
address and 0xFFFF
) + ")"
"BAD MEMORY LOCATION ON WRITE: " + address + " (0x" +
(address and 0xFFFF).toHexString() + ")"
)
return
}
val currentValue = memory[address]
addRewindStep(Runnable { memory[address] = currentValue })
addRewindStep { memory[address] = currentValue }
memory[address] = value
observers!!.forEach(Consumer { o: MTMCObserver? -> o!!.memoryUpdated(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()
if (rewindSteps.size > MAX_REWIND_STEPS) {
rewindSteps.removeLast()
}
}
}
@@ -812,11 +808,13 @@ class MonTanaMiniComputer {
// TODO mark as overflow
}
val currentValue = registerFile[register]
addRewindStep(Runnable {
addRewindStep {
registerFile[register] = currentValue
})
}
registerFile[register] = value.toShort()
observers!!.forEach(Consumer { o: MTMCObserver? -> o!!.registerUpdated(register, value) })
observers!!.forEach { o: MTMCObserver? ->
o!!.registerUpdated(register, value)
}
}
fun getRegisterValue(register: Register): Short {
@@ -850,7 +848,9 @@ class MonTanaMiniComputer {
}
fun getBytesFromMemory(address: Int, length: Int): ByteArray {
return Arrays.copyOfRange(memory, address, address + length)
val result = ByteArray(length)
memory.copyInto(result, 0, address, address + length)
return result
}
val oS: MTOS
@@ -858,18 +858,15 @@ class MonTanaMiniComputer {
val memoryAddresses: Iterable<Int?>
get() = Iterable {
IntStream.range(
0,
MEMORY_SIZE
).iterator()
(0..MEMORY_SIZE).iterator()
}
fun addObserver(observer: MTMCObserver?) {
observers!!.add(observer!!)
observers.add(observer!!)
}
fun removeObserver(observer: MTMCObserver?) {
observers!!.remove(observer!!)
observers.remove(observer!!)
}
fun pause() {
@@ -936,7 +933,7 @@ class MonTanaMiniComputer {
fun setArg(arg: String) {
if (!arg.isEmpty()) {
val start = getRegisterValue(Register.BP)
val bytes = arg.toByteArray()
val bytes = arg.encodeToByteArray()
writeStringToMemory(start.toInt(), bytes)
setRegisterValue(Register.A0, start.toInt())
setRegisterValue(Register.BP, start + bytes.size + 1)
@@ -953,7 +950,7 @@ class MonTanaMiniComputer {
}
fun rewind() {
val latestRewindStep = rewindSteps!!.pop()
val latestRewindStep = rewindSteps.removeFirst()
latestRewindStep.rewind()
}
@@ -1020,7 +1017,6 @@ class MonTanaMiniComputer {
return false
}
@JvmStatic
fun main(args: Array<String>) {
val computer = MonTanaMiniComputer()
computer.speed = 1 // default to 1hz

View File

@@ -73,4 +73,4 @@ enum class Register {
}
}
}
}
}

View File

@@ -1,10 +1,12 @@
package mtmc.emulator
import mtmc.util.Runnable
class RewindStep {
var subSteps: MutableList<Runnable?> = ArrayList<Runnable?>()
fun rewind() {
subSteps.reversed().forEach({ obj: Runnable? -> obj!!.run() })
subSteps.reversed().forEach({ obj: Runnable? -> obj!!.invoke() })
}
fun addSubStep(subStep: Runnable?) {

View File

@@ -3,18 +3,17 @@ package mtmc.os
import mtmc.emulator.MonTanaMiniComputer
import mtmc.emulator.Register
import mtmc.os.SysCall.Companion.getValue
import mtmc.os.fs.File
import mtmc.os.fs.Files
import mtmc.os.shell.Shell
import java.io.File
import java.io.IOException
import java.nio.file.Files
import java.util.*
import mtmc.util.currentTimeMillis
import kotlin.math.max
import kotlin.math.min
import kotlin.text.Charsets.US_ASCII
import kotlin.random.Random
class MTOS(private val computer: MonTanaMiniComputer) {
private var timer: Long = 0
var random: Random = Random()
var random: Random = Random.Default
// Editor support
var currentFile: String? = null
@@ -98,7 +97,7 @@ class MTOS(private val computer: MonTanaMiniComputer) {
}
}
val string = computer.console.readString()
val bytes = string!!.toByteArray(US_ASCII)
val bytes = string!!.encodeToByteArray()
val bytesToRead = min(bytes.size, maxLen.toInt())
for (i in 0..<bytesToRead) {
val aByte = bytes[i]
@@ -242,8 +241,8 @@ class MTOS(private val computer: MonTanaMiniComputer) {
// special handling for game-of-life cell files
if ("cells" == fileType) {
val str = Files.readString(file.toPath())
val lines = Arrays
.stream<String>(str.split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray())
val lines: List<String> =
str.split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
.filter { s: String? -> !s!!.startsWith("!") }
.toList()
@@ -251,7 +250,7 @@ class MTOS(private val computer: MonTanaMiniComputer) {
val cappedLines = min(linesTotal, maxSize2.toInt())
for (lineNum in 0..<cappedLines) {
val line = lines.get(lineNum)
val line: String = lines.get(lineNum)
for (colNum in 0..<maxSize1) {
val offset = lineNum * maxSize1 + colNum
val byteOffset = offset / 8
@@ -275,7 +274,7 @@ class MTOS(private val computer: MonTanaMiniComputer) {
}
}
computer.setRegisterValue(Register.RV, 0)
} catch (e: IOException) {
} catch (e: Exception) {
computer.setRegisterValue(Register.RV, -1)
e.printStackTrace() // debugging
}
@@ -290,7 +289,6 @@ class MTOS(private val computer: MonTanaMiniComputer) {
computer.writeByteToMemory(destination + i, aByte)
}
//TODO: Should this return the length with or without the null terminator?
computer.writeByteToMemory(destination + maxSize - 1, 0.toByte())
computer.setRegisterValue(Register.RV, maxSize - 1)
@@ -300,18 +298,18 @@ class MTOS(private val computer: MonTanaMiniComputer) {
if (computer.fileSystem.exists(dir)) {
computer.setRegisterValue(Register.RV, 0)
computer.fileSystem.setCWD(dir)
computer.fileSystem.cWD = dir
} else {
computer.setRegisterValue(Register.RV, 1)
}
} else if (syscallNumber == getValue("timer").toShort()) {
val value = computer.getRegisterValue(Register.A0)
if (value > 0) this.timer = System.currentTimeMillis() + value
if (value > 0) this.timer = currentTimeMillis() + value
computer.setRegisterValue(
Register.RV,
max(0, this.timer - System.currentTimeMillis()).toInt()
max(0, this.timer - currentTimeMillis()).toInt()
)
} else if (syscallNumber == getValue("drawimg").toShort()) {
val image = computer.getRegisterValue(Register.A0)
@@ -401,10 +399,13 @@ class MTOS(private val computer: MonTanaMiniComputer) {
}
val file = list[offset.toInt()]
val name = file.getName()
val name = file.name
val size = min(maxSizeOut - 1, name.length)
computer.writeWordToMemory(destination.toInt(), if (file.isDirectory()) 1 else 0)
computer.writeWordToMemory(
destination.toInt(),
if (file.directory) 1 else 0
)
for (i in 0..<size) {
val aByte = name.get(i).code.toByte()
@@ -433,9 +434,9 @@ class MTOS(private val computer: MonTanaMiniComputer) {
length++
}
try {
val outputString = String(computer.memory, pointer.toInt(), length.toInt(), US_ASCII)
val outputString = computer.memory.decodeToString(pointer.toInt(), pointer.toInt() + length)
return outputString
} catch (ignored: StringIndexOutOfBoundsException) {
} catch (ignored: IndexOutOfBoundsException) {
computer.setStatus(MonTanaMiniComputer.ComputerStatus.PERMANENT_ERROR)
return ""
}
@@ -447,9 +448,12 @@ class MTOS(private val computer: MonTanaMiniComputer) {
}
}
fun loadFile(path: String?): File {
val fs = computer.fileSystem
val file = fs.getRealPath(path).toFile()
return file
}
fun loadFile(path: String?): File = File(path ?: "<empty>")
/*
{
val fs = computer.fileSystem
val file = fs.getRealPath(path).toFile()
return file
}
*/
}

View File

@@ -1,7 +1,5 @@
package mtmc.os
import java.util.*
enum class SysCall(value: Int) {
EXIT(0x00),
RINT(0x01),
@@ -50,25 +48,24 @@ enum class SysCall(value: Int) {
companion object {
fun isSysCall(call: String): Boolean {
try {
valueOf(call.uppercase(Locale.getDefault()))
valueOf(call.uppercase())
return true
} catch (e: IllegalArgumentException) {
return false
}
}
@JvmStatic
fun getValue(call: String): Byte {
return valueOf(call.uppercase(Locale.getDefault())).value
return valueOf(call.uppercase()).value
}
fun getString(syscallCode: Byte): String? {
for (o in entries) {
if (o.value == syscallCode) {
return o.name.lowercase(Locale.getDefault())
return o.name.lowercase()
}
}
return null
}
}
}
}

View File

@@ -0,0 +1,58 @@
package mtmc.os.exec
import mtmc.emulator.DebugInfo
import mtmc.os.fs.Path
data class Executable(
val format: Format,
val code: ByteArray,
val data: ByteArray,
val graphics: Array<ByteArray>,
val sourceName: String,
val debugInfo: DebugInfo?
) {
enum class Format(val formatName: String) {
Orc1("orc1");
}
fun dump(): String? {
return null //Gson().toJson(this)
}
fun dump(path: Path) {
/*
FileWriter(path.toFile()).use { fw ->
dump(fw)
}
*/
}
/*
fun dump(writer: Writer?) {
val gson = Gson()
gson.toJson(this, writer)
}
*/
companion object {
fun load(exe: String?): Executable? {
return null //Gson().fromJson<Executable?>(exe, Executable::class.java)
}
fun load(path: Path): Executable? = null
/*
{
FileReader(path.toFile()).use { fw ->
return load(fw)
}
}
*/
/*
fun load(reader: Reader): Executable? {
val gson = Gson()
return gson.fromJson<Executable?>(reader, Executable::class.java)
}
*/
}
}

View File

@@ -0,0 +1,11 @@
package mtmc.os.fs
object System {
val console: Console = Console()
}
class Console {
fun readLine(prompt: String? = ""): String {
TODO("Not yet implemented")
}
}

View File

@@ -0,0 +1,47 @@
package mtmc.os.fs
object Files {
fun readString(toPath: Path): String {
TODO("Not yet implemented")
}
fun readAllBytes(toPath: Path): ByteArray {
TODO("Not yet implemented")
}
}
class File(
val parent: File? = null,
val name: String
) {
var directory: Boolean = TODO("initialize me")
constructor(name: String) : this(null, name)
fun getParent(): File = parent ?: error("No parent")
fun getPath(): String = if (parent == null) {
name
} else {
"${parent.getPath()}/$name"
}
fun exists(): Boolean {
TODO("Not yet implemented")
}
fun getAbsolutePath(): String {
TODO("Not yet implemented")
}
fun toPath(): Path {
TODO("Not yet implemented")
}
fun listFiles(): Array<File> {
TODO("Not yet implemented")
}
}

View File

@@ -0,0 +1,213 @@
package mtmc.os.fs
import mtmc.emulator.MonTanaMiniComputer
import kotlin.jvm.JvmField
class FileSystem(
val computer: MonTanaMiniComputer
) {
private var cwd = "/home"
init {
initFileSystem()
}
private fun initFileSystem() {
/*
if (DISK_PATH.toFile().exists()) return
// Make the disk/ directory
DISK_PATH.toFile().mkdirs()
try {
ZipInputStream(getClass().getResourceAsStream("/disk.zip")).use { `in` ->
var entry: ZipEntry?
var file: File?
val data = ByteArray(4096)
var count: Int
while ((`in`.getNextEntry().also { entry = it }) != null) {
file = File(entry.getName())
if (entry.isDirectory()) {
file.mkdirs()
} else {
file.getParentFile().mkdirs()
FileOutputStream(file).use { out ->
while ((`in`.read(data).also { count = it }) > 0) {
out.write(data, 0, count)
}
}
}
}
}
} catch (e: IOException) {
e.printStackTrace()
}
*/
}
private fun notifyOfFileSystemUpdate() {
if (this.computer != null) {
computer.notifyOfFileSystemUpdate()
}
}
var cWD: String
get() = this.cwd
set(cwd) {
this.cwd = resolve(cwd)
this.notifyOfFileSystemUpdate()
}
fun exists(path: String): Boolean {
return false //File(DISK_PATH.toFile(), resolve(path)).exists()
}
fun mkdir(path: String): Boolean {
/*
val success: Boolean = File(DISK_PATH.toFile(), resolve(path)).mkdir()
if (success) {
computer.notifyOfFileSystemUpdate()
}
return success
*/
return true
}
fun delete(path: String): Boolean {
/*
val success: Boolean = File(DISK_PATH.toFile(), resolve(path)).delete()
if (success) {
computer.notifyOfFileSystemUpdate()
}
return success
*/
return true
}
fun resolve(filename: String): String = filename
/*
{
val root: File = DISK_PATH.toFile()
var directory = if (filename.startsWith("/")) root else File(root, cwd)
val path: Array<String> = filename.split("/")
for (name in path) {
if (name.equals(".")) {
continue
} else if (name.equals("..") && directory.equals(root)) {
continue
} else if (name.equals("..")) {
directory = directory.getParentFile()
} else {
directory = File(directory, name)
}
}
if (directory.equals(root)) {
return "/"
}
return directory.getAbsolutePath().substring(root.getAbsolutePath().length())
}
*/
fun getRealPath(path: String): Path { // Resolves given path and returns /disk/ + path
val resolvedPath = resolve(path)
val slashGone: String = if (resolvedPath.length > 0) resolvedPath.substring(1) else ""
val file: File = DISK_PATH.resolve(slashGone).toFile()
if (file.getAbsolutePath().length < DISK_PATH.toFile().getAbsolutePath().length) {
return DISK_PATH
}
return file.toPath()
}
fun getFileList(path: String): Array<File> {
val resolvedPath: File = getRealPath(path).toFile()
if (!resolvedPath.directory) return arrayOf<File>(resolvedPath)
return resolvedPath.listFiles()
}
fun listFiles(path: String): Listing {
val resolvedPath: File = getRealPath(path).toFile()
return Listing(this, resolvedPath)
}
fun listCWD(): Listing {
return listFiles(cwd)
}
fun listRoot(): Listing {
return listFiles("/")
}
fun writeFile(path: String, contents: String?) {
val filePath: Path = getRealPath(path)
//Files.writeString(filePath, contents)
}
fun readFile(path: String): String? {
val filePath: Path = getRealPath(path)
val contents = "" //Files.readString(filePath)
return contents
}
fun getMimeType(path: String): String {
val file: Path = getRealPath(path)
val name = file.toFile().name.lowercase()
val probed = file.probeContentType()
if (name.endsWith(".asm")) return "text/x-asm"
if (name.endsWith(".c")) return "text/x-csrc"
if (name.endsWith(".sea")) return "text/x-csrc"
if (name.endsWith(".h")) return "text/x-csrc"
if (probed != null) return probed
return "application/octet-stream"
}
/*
fun openFile(path: String): InputStream? {
val file: Unit */
/* TODO: class org.jetbrains.kotlin.nj2k.types.JKJavaNullPrimitiveType *//*
? =
getRealPath(path).toFile()
return FileInputStream(file)
}
*/
/*
fun saveFile(path: String, contents: InputStream) {
val file: Unit */
/* TODO: class org.jetbrains.kotlin.nj2k.types.JKJavaNullPrimitiveType *//*
? =
getRealPath(path).toFile()
val data = ByteArray(4096)
var count: Int
FileOutputStream(file).use { out ->
while ((contents.read(data).also { count = it }) > 0) {
out.write(data, 0, count)
}
}
}
*/
companion object {
@JvmField
val DISK_PATH: Path = Path() //.of(System.getProperty("user.dir"), "disk").toAbsolutePath()
}
}

View File

@@ -0,0 +1,45 @@
package mtmc.os.fs
class Listing(
val fs: FileSystem,
val file: File
) {
val path: String
val name: String
val directory: Boolean
val root: Boolean
init {
val root = FileSystem.DISK_PATH.toFile().getAbsolutePath().replace('\\', '/')
val path = file.getAbsolutePath().substring(root.length).replace('\\', '/')
this.path = if (path.length > 0) path else "/"
this.name = if (path.length > 0) file.name else "/"
this.directory = file.directory
this.root = this.path.equals("/")
}
val parent: Listing?
get() = if (file.parent != null) {
Listing(fs, file.parent)
} else {
null
}
fun list(): List<Listing> {
if (!directory) {
return ArrayList()
}
val list = ArrayList<Listing>()
val children = file.listFiles()
list.sortBy { it.name }
for (child in children) {
list.add(Listing(fs, child))
}
return list
}
}

View File

@@ -0,0 +1,15 @@
package mtmc.os.fs
class Path {
fun toFile(): File {
TODO("Not yet implemented")
}
fun probeContentType(): String? {
TODO("Not yet implemented")
}
fun resolve(filename: String): Path {
TODO("Not yet implemented")
}
}

View File

@@ -1,160 +1,163 @@
package mtmc.os.shell;
package mtmc.os.shell
import mtmc.asm.Assembler;
import mtmc.asm.AssemblyResult;
import mtmc.asm.instructions.Instruction;
import mtmc.emulator.DebugInfo;
import mtmc.emulator.MonTanaMiniComputer;
import mtmc.emulator.Register;
import mtmc.os.exec.Executable;
import mtmc.os.fs.FileSystem;
import mtmc.os.shell.builtins.AssembleCommand;
import mtmc.os.shell.builtins.DisplayCommand;
import mtmc.os.shell.builtins.ExitCommand;
import mtmc.os.shell.builtins.GetCommand;
import mtmc.os.shell.builtins.HelpCommand;
import mtmc.os.shell.builtins.LoadCommand;
import mtmc.os.shell.builtins.PauseCommand;
import mtmc.os.shell.builtins.RunCommand;
import mtmc.os.shell.builtins.SeacCommand;
import mtmc.os.shell.builtins.SetCommand;
import mtmc.os.shell.builtins.SpeedCommand;
import mtmc.os.shell.builtins.StepCommand;
import mtmc.os.shell.builtins.WebCommand;
import mtmc.tokenizer.MTMCToken;
import mtmc.tokenizer.MTMCTokenizer;
import mtmc.asm.Assembler
import mtmc.asm.instructions.Instruction.Companion.isInstruction
import mtmc.asm.peekFirst
import mtmc.emulator.MonTanaMiniComputer
import mtmc.emulator.Register
import mtmc.os.fs.FileSystem
import mtmc.os.shell.builtins.AssembleCommand
import mtmc.os.shell.builtins.DisplayCommand
import mtmc.os.shell.builtins.ExitCommand
import mtmc.os.shell.builtins.GetCommand
import mtmc.os.shell.builtins.HelpCommand
import mtmc.os.shell.builtins.LoadCommand
import mtmc.os.shell.builtins.PauseCommand
import mtmc.os.shell.builtins.RunCommand
import mtmc.os.shell.builtins.SeacCommand
import mtmc.os.shell.builtins.SetCommand
import mtmc.os.shell.builtins.SpeedCommand
import mtmc.os.shell.builtins.StepCommand
import mtmc.os.shell.builtins.WebCommand
import mtmc.tokenizer.MTMCToken
import mtmc.tokenizer.MTMCTokenizer
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
object Shell {
private val COMMANDS: MutableMap<String?, ShellCommand> = LinkedHashMap<String?, ShellCommand>()
import static mtmc.tokenizer.MTMCToken.TokenType.IDENTIFIER;
import static mtmc.tokenizer.MTMCToken.TokenType.QUESTION_MARK;
init {
COMMANDS.put("help", HelpCommand())
COMMANDS.put("exit", ExitCommand())
COMMANDS.put("set", SetCommand())
COMMANDS.put("get", GetCommand())
COMMANDS.put("web", WebCommand())
COMMANDS.put("disp", DisplayCommand())
COMMANDS.put("asm", AssembleCommand())
COMMANDS.put("load", LoadCommand())
COMMANDS.put("step", StepCommand())
COMMANDS.put("run", RunCommand())
COMMANDS.put("pause", PauseCommand())
COMMANDS.put("speed", SpeedCommand())
COMMANDS.put("sc", SeacCommand())
}
public class Shell {
private static final Map<String, ShellCommand> COMMANDS = new LinkedHashMap<>();
fun isCommand(cmd: String): Boolean {
return COMMANDS.containsKey(cmd.lowercase())
}
static {
COMMANDS.put("help", new HelpCommand());
COMMANDS.put("exit", new ExitCommand());
COMMANDS.put("set", new SetCommand());
COMMANDS.put("get", new GetCommand());
COMMANDS.put("web", new WebCommand());
COMMANDS.put("disp", new DisplayCommand());
COMMANDS.put("asm", new AssembleCommand());
COMMANDS.put("load", new LoadCommand());
COMMANDS.put("step", new StepCommand());
COMMANDS.put("run", new RunCommand());
COMMANDS.put("pause", new PauseCommand());
COMMANDS.put("speed", new SpeedCommand());
COMMANDS.put("sc", new SeacCommand());
}
private fun findExecutable(path: String?, fs: FileSystem): Boolean {
if (path == null || path == "") return false
//if (fs.exists(path) && !fs.listFiles(path).directory) return true
//if (fs.exists("/bin/" + path) && !fs.listFiles("/bin/" + path).directory) return true
public static boolean isCommand(String cmd) {
return COMMANDS.containsKey(cmd.toLowerCase());
}
private static boolean findExecutable(String path, FileSystem fs) {
if (path == null || path.equals("")) return false;
if (fs.exists(path) && !fs.listFiles(path).directory) return true;
if (fs.exists("/bin/" + path) && !fs.listFiles("/bin/" + path).directory) return true;
return false;
}
private static void runExecutable(String file, String command, MTMCTokenizer tokens, MonTanaMiniComputer computer) throws Exception {
FileSystem fs = computer.getFileSystem();
Path srcPath = Path.of("disk/" + fs.resolve(file));
if(!srcPath.toFile().exists()) {
srcPath = Path.of("disk" + fs.resolve("/bin/" + file));
return false
}
@Throws(Exception::class)
private fun runExecutable(
file: String,
command: String,
tokens: MTMCTokenizer,
computer: MonTanaMiniComputer
) {
val fs = computer.fileSystem
/*
var srcPath = Path.of("disk/" + fs.resolve(file))
if (!srcPath.toFile().exists()) {
srcPath = Path.of("disk" + fs.resolve("/bin/" + file))
}
Executable exec = Executable.load(srcPath);
computer.load(exec.code, exec.data, exec.graphics, exec.debugInfo);
tokens.consume();
String arg = command.substring(file.length()).strip();
computer.getOS().applyBreakpoints();
computer.setArg(arg);
computer.run();
}
public static void execCommand(String command, MonTanaMiniComputer computer) {
MTMCTokenizer tokens = new MTMCTokenizer(command, "#");
try {
MTMCToken identifier = tokens.matchAndConsume(IDENTIFIER);
String cmd;
if (identifier == null) {
MTMCToken question = tokens.matchAndConsume(QUESTION_MARK);
String executable = tokens.collapseTokensAsString();
// alias ? to help
if (question != null) {
cmd = "help";
} else if (findExecutable(executable, computer.getFileSystem())) {
cmd = executable;
} else {
printShellHelp(computer);
return;
}
} else {
cmd = identifier.stringValue();
val exec = load(srcPath)
computer.load(exec!!.code, exec.data, exec.graphics, exec.debugInfo)
tokens.consume()
val arg = command.substring(file.length).trim()
computer.oS.applyBreakpoints()
computer.setArg(arg)
computer.run()
*/
}
fun execCommand(command: String, computer: MonTanaMiniComputer) {
val tokens = MTMCTokenizer(command, "#")
try {
val identifier = tokens.matchAndConsume(MTMCToken.TokenType.IDENTIFIER)
val cmd: String?
if (identifier == null) {
val question = tokens.matchAndConsume(MTMCToken.TokenType.QUESTION_MARK)
val executable = tokens.collapseTokensAsString()
// alias ? to help
if (question != null) {
cmd = "help"
} else if (findExecutable(executable, computer.fileSystem)) {
cmd = executable
} else {
printShellHelp(computer)
return
}
} else {
cmd = identifier.stringValue()
}
if (isCommand(cmd)) {
COMMANDS[cmd.lowercase()]!!.exec(tokens, computer)
} else {
tokens.reset()
val asm = mutableListOf<MTMCToken>()
asm.addAll(tokens.tokens)
val updatedAsm = Assembler.transformSyntheticInstructions(asm)
val firstToken = updatedAsm.peekFirst()
val firstTokenStr = firstToken?.stringValue() ?: error("Unexpected null token")
if (!updatedAsm.isEmpty() && isInstruction(firstTokenStr)) {
val assembler = Assembler()
val result = assembler.assemble(command)
if (result.errors.isEmpty()) {
val code = result.code
if (code.size == 4) {
val data = (code[2].toInt() shl 8) or code[3].toInt()
computer.setRegisterValue(Register.DR, data)
}
if (isCommand(cmd)) {
COMMANDS.get(cmd.toLowerCase()).exec(tokens, computer);
} else {
tokens.reset();
LinkedList<MTMCToken> asm = new LinkedList<>(tokens.stream().toList());
LinkedList<MTMCToken> updatedAsm = Assembler.Companion.transformSyntheticInstructions(asm);
MTMCToken firstToken = updatedAsm.peekFirst();
String firstTokenStr = firstToken.stringValue();
if (!updatedAsm.isEmpty() && Instruction.isInstruction(firstTokenStr)) {
Assembler assembler = new Assembler();
AssemblyResult result = assembler.assemble(command);
if (result.errors.isEmpty()) {
byte[] code = result.code;
if (code.length == 4) {
int data = (code[2] << 8) | code[3];
computer.setRegisterValue(Register.DR, data);
}
int lower = code[1] & 0xFF;
int higher = code[0] & 0xFF;
int inst = (higher << 8) | lower;
DebugInfo originalDebugInfo = computer.getDebugInfo();
computer.setDebugInfo(result.debugInfo);
computer.execInstruction((short) inst);
computer.setDebugInfo(originalDebugInfo);
} else {
computer.getConsole().println(result.printErrors());
}
} else {
if (findExecutable(cmd, computer.getFileSystem())) {
runExecutable(cmd, command, tokens, computer);
} else {
printShellHelp(computer);
}
}
}
} catch (NoSuchFileException e) {
computer.getConsole().println("No such file: " + e.getFile());
} catch (Exception e) {
computer.getConsole().println(e.getMessage());
e.printStackTrace();
val lower = code[1].toInt() and 0xFF
val higher = code[0].toInt() and 0xFF
val inst = (higher shl 8) or lower
val originalDebugInfo = computer.debugInfo
computer.debugInfo = result.debugInfo
computer.execInstruction(inst.toShort())
computer.debugInfo = originalDebugInfo
} else {
computer.console.println(result.printErrors())
}
} else {
if (findExecutable(cmd, computer.fileSystem)) {
runExecutable(cmd, command, tokens, computer)
} else {
printShellHelp(computer)
}
}
}
/*
} catch (e: NoSuchFileException) {
computer.console.println("No such file: " + e.getFile())
*/
} catch (e: Exception) {
computer.console.println(e.message!!)
e.printStackTrace()
}
}
public static void printShellHelp(MonTanaMiniComputer computer) {
computer.getConsole().println("Shell BuiltIns: \n");
for (ShellCommand value : COMMANDS.values()) {
computer.getConsole().println(value.help);
}
computer.getConsole().println("Also: ");
computer.getConsole().println(" <asm instruction>");
computer.getConsole().println("or \n" +
" <executable>\n\n");
fun printShellHelp(computer: MonTanaMiniComputer) {
computer.console.println("Shell BuiltIns: \n")
for (value in COMMANDS.values) {
computer.console.println(value.help!!)
}
computer.console.println("Also: ")
computer.console.println(" <asm instruction>")
computer.console.println(
"or \n" +
" <executable>\n\n"
)
}
public static void printShellWelcome(MonTanaMiniComputer computer) {
computer.getConsole().println("Welcome to MtOS! Type ? for help");
}
fun printShellWelcome(computer: MonTanaMiniComputer) {
computer.console.println("Welcome to MtOS! Type ? for help")
}
}

View File

@@ -1,12 +1,7 @@
package mtmc.os.shell;
package mtmc.os.shell
public class UsageException extends RuntimeException {
private final ShellCommand cmd;
public UsageException(ShellCommand shellCommand) {
super("Usage:\n\n" + shellCommand.help);
this.cmd = shellCommand;
}
public ShellCommand getCmd() {
return cmd;
}
}
class UsageException(
cmd: ShellCommand
) : RuntimeException(
"Usage:\n\n" + cmd.help
)

View File

@@ -0,0 +1,45 @@
package mtmc.os.shell.builtins
import mtmc.emulator.MonTanaMiniComputer
import mtmc.os.shell.ShellCommand
import mtmc.tokenizer.MTMCTokenizer
class AssembleCommand : ShellCommand() {
@Throws(Exception::class)
public override fun exec(tokens: MTMCTokenizer, computer: MonTanaMiniComputer) {
val fs = computer.fileSystem
val src = tokens.collapseTokensAsString()
require(!(src == null || src.isBlank())) { "missing or required argument 'src'" }
/*
val srcPath: Path = getDiskPath(src, fs)
val dst = tokens.collapseTokensAsString()
require(!(dst == null || dst.isBlank())) { "missing required argument 'dst'" }
val dstPath: Path = getDiskPath(dst, fs)
val contents = Files.readString(srcPath)
val assembler = Assembler()
val file_name =
fs.resolve(src) // srcPath.toString().substring(DISK_PATH.toString().length()).replaceAll("\\\\", "/");
val executable = assembler.assembleExecutable(file_name, contents)
executable.dump(dstPath)
computer.notifyOfFileSystemUpdate()
*/
}
override val help: String
get() = """
asm <src> <dst>
- src : path to a .asm file
- dst : path to a target output binary
""".trimIndent()
companion object {
/*
fun getDiskPath(pathString: String, fs: FileSystem): Path {
val path = Path.of("disk" + fs.resolve(pathString))
return path.toAbsolutePath()
}
*/
}
}

View File

@@ -2,15 +2,14 @@ package mtmc.os.shell.builtins
import mtmc.emulator.MTMCDisplay
import mtmc.emulator.MonTanaMiniComputer
import mtmc.os.fs.File
import mtmc.os.shell.ShellCommand
import mtmc.tokenizer.MTMCToken
import mtmc.tokenizer.MTMCTokenizer
import java.io.File
import java.util.*
import javax.imageio.ImageIO
import kotlin.random.Random
class DisplayCommand : ShellCommand() {
var random: Random = Random()
var random: Random = Random.Default
@Throws(Exception::class)
public override fun exec(tokens: MTMCTokenizer, computer: MonTanaMiniComputer) {
@@ -42,8 +41,8 @@ class DisplayCommand : ShellCommand() {
if (tokens.more()) {
val imagePath = tokens.collapseTokensAsString()
val file = computer.oS.loadFile(imagePath)
val img = ImageIO.read(file)
computer.display.loadScaledImage(img)
//val img = ImageIO.read(file)
//computer.display.loadScaledImage(img)
} else {
usageException()
}
@@ -54,11 +53,15 @@ class DisplayCommand : ShellCommand() {
} else if (tokens.match(MTMCToken.TokenType.INTEGER)) {
val row = tokens.consumeAsInteger()
val col = tokens.require(
mtmc.tokenizer.MTMCToken.TokenType.INTEGER,
java.lang.Runnable { this.usageException() })!!.intValue()
mtmc.tokenizer.MTMCToken.TokenType.INTEGER
) {
this.usageException()
}!!.intValue()
val color = tokens.require(
mtmc.tokenizer.MTMCToken.TokenType.INTEGER,
java.lang.Runnable { this.usageException() })!!.intValue()
mtmc.tokenizer.MTMCToken.TokenType.INTEGER
) {
this.usageException()
}!!.intValue()
computer.display.setPixel(row, col, color)
} else {
usageException()

View File

@@ -7,7 +7,7 @@ import mtmc.tokenizer.MTMCTokenizer
class ExitCommand : ShellCommand() {
public override fun exec(tokens: MTMCTokenizer, computer: MonTanaMiniComputer) {
computer.console.println("Goodbye!")
System.exit(1)
//System.exit(1)
}
override val help: String

View File

@@ -3,13 +3,13 @@ package mtmc.os.shell.builtins
import mtmc.emulator.MonTanaMiniComputer
import mtmc.os.exec.Executable.Companion.load
import mtmc.os.fs.FileSystem
import mtmc.os.fs.Path
import mtmc.os.shell.ShellCommand
import mtmc.tokenizer.MTMCTokenizer
import java.nio.file.Path
class LoadCommand : ShellCommand() {
@Throws(Exception::class)
public override fun exec(tokens: MTMCTokenizer, computer: MonTanaMiniComputer) {
override fun exec(tokens: MTMCTokenizer, computer: MonTanaMiniComputer) {
val fs = computer.fileSystem
val program = tokens.collapseTokensAsString()
require(!(program == null || program.isBlank())) { "missing or required argument 'src'" }

View File

@@ -1,10 +1,8 @@
package mtmc.os.shell.builtins
import mtmc.emulator.MonTanaMiniComputer
import mtmc.lang.sea.SeaLanguage
import mtmc.os.shell.ShellCommand
import mtmc.tokenizer.MTMCTokenizer
import mtmc.util.StringEscapeUtils.escapeString
class SeacCommand : ShellCommand() {
@Throws(Exception::class)
@@ -24,15 +22,18 @@ class SeacCommand : ShellCommand() {
requireNotNull(filename) { "expected source file" }
require(fs.exists(filename)) { "file " + escapeString(filename) + " does not exist" }
require(fs.exists(filename)) { "file " + filename + " does not exist" }
println(fs.resolve(filename))
val lang = SeaLanguage()
val content = fs.readFile(filename)
val exec = lang.compileExecutable(fs.resolve(filename), content)
val bin = exec.dump()
computer.fileSystem.writeFile(output, bin)
computer.notifyOfFileSystemUpdate()
/*
val lang = SeaLanguage()
val content = fs.readFile(filename)
val exec = lang.compileExecutable(fs.resolve(filename), content)
val bin = exec.dump()
computer.fileSystem.writeFile(output, bin)
computer.notifyOfFileSystemUpdate()
*/
}
override val help: String

View File

@@ -40,7 +40,10 @@ class SetCommand : ShellCommand() {
if (value?.type === MTMCToken.TokenType.INTEGER || value?.type === MTMCToken.TokenType.BINARY || value?.type === MTMCToken.TokenType.HEX) {
computer.writeWordToMemory(memLocation.intValue(), value.intValue())
} else {
computer.writeStringToMemory(memLocation.intValue(), value!!.stringValue().toByteArray())
computer.writeStringToMemory(
memLocation.intValue(),
value!!.stringValue().encodeToByteArray()
)
}
}
}

View File

@@ -4,13 +4,10 @@ import mtmc.emulator.MonTanaMiniComputer
import mtmc.os.shell.ShellCommand
import mtmc.tokenizer.MTMCToken
import mtmc.tokenizer.MTMCTokenizer
import java.util.*
class SpeedCommand : ShellCommand() {
private val speeds: MutableList<Int?> = Arrays.asList<Int?>(
*arrayOf<Int>(
private val speeds = listOf(
1, 10, 100, 1000, 10000, 100000, 1000000
)
)
@Throws(Exception::class)

View File

@@ -1,14 +1,16 @@
package mtmc.tokenizer
import java.util.*
fun Char.isHexDigit(): Boolean {
return this in '0'..'9' || this in 'a'..'f' || this in 'A'..'F'
}
class MTMCScanner(private val src: String, private val lineCommentStart: String) {
var position: Int = 0
var line: Int = 1
var lineOffset: Int = 0
var tokens: LinkedList<MTMCToken> = LinkedList<MTMCToken>()
var tokens = mutableListOf<MTMCToken>()
fun tokenize(): LinkedList<MTMCToken> {
fun tokenize(): MutableList<MTMCToken> {
consumeWhitespace()
while (!scanEnd()) {
scanToken()
@@ -38,7 +40,7 @@ class MTMCScanner(private val src: String, private val lineCommentStart: String)
}
private fun scanLineComment(): Boolean {
val bytes = lineCommentStart.toByteArray()
val bytes = lineCommentStart.encodeToByteArray()
for (i in bytes.indices) {
val aChar = bytes[i]
if (peek(i).code.toByte() != aChar) {
@@ -188,7 +190,7 @@ class MTMCScanner(private val src: String, private val lineCommentStart: String)
private fun scanHex(start: Int): Boolean {
takeChar() // take leading zero
takeChar() // take 'x'
while (HexFormat.isHexDigit(peek().code)) {
while (peek().isHexDigit()) {
takeChar()
}
tokens.add(makeToken(MTMCToken.TokenType.HEX, strValueFrom(start), start))

View File

@@ -1,13 +1,12 @@
package mtmc.tokenizer
@JvmRecord
data class MTMCToken(
@JvmField val start: Int,
@JvmField val end: Int,
val start: Int,
val end: Int,
val line: Int,
val lineOffset: Int,
@JvmField val stringValue: String,
@JvmField val type: TokenType?
val stringValue: String,
val type: TokenType?
) {
override fun toString(): String {
return stringValue

View File

@@ -1,11 +1,10 @@
package mtmc.tokenizer
import java.util.*
import java.util.stream.Stream
import mtmc.util.Runnable
import kotlin.math.max
class MTMCTokenizer(var source: String, lineCommentStart: String) {
var tokens: LinkedList<MTMCToken> = MTMCScanner(source, lineCommentStart).tokenize()
var tokens: MutableList<MTMCToken> = MTMCScanner(source, lineCommentStart).tokenize()
var currentToken: Int = 0
fun currentToken(): MTMCToken {
@@ -59,10 +58,6 @@ class MTMCTokenizer(var source: String, lineCommentStart: String) {
return tokens.get(max(0, currentToken - 1))
}
fun stream(): Stream<MTMCToken?> {
return tokens.stream()
}
override fun toString(): String {
val sb = StringBuilder()
for (i in tokens.indices) {
@@ -104,7 +99,7 @@ class MTMCTokenizer(var source: String, lineCommentStart: String) {
if (match(tokenType)) {
return consume()
} else {
notFound.run()
notFound.invoke()
return null
}
}

View File

@@ -22,27 +22,21 @@ object BinaryUtils {
}
fun toBinary(aByte: Byte): String {
val binaryString = Integer.toBinaryString(aByte.toInt())
val formatted = String.format("%8s", binaryString)
val zeroed = formatted.replace(" ".toRegex(), "0")
val zeroed = aByte.toInt().toString(2).padStart(8, '0')
val underScored = zeroed.replace("....".toRegex(), "$0_")
val noTrailingUnderscore = underScored.substring(0, underScored.length - 1)
return "0b" + noTrailingUnderscore
}
fun toBinary(aShort: Short): String {
val binaryString = Integer.toBinaryString(aShort.toInt())
val formatted = String.format("%16s", binaryString)
val zeroed = formatted.replace(" ".toRegex(), "0")
val zeroed = aShort.toInt().toString(2).padStart(16, '0')
val underScored = zeroed.replace("....".toRegex(), "$0_")
val noTrailingUnderscore = underScored.substring(0, underScored.length - 1)
return "0b" + noTrailingUnderscore
}
fun toBinary(anInt: Int): String {
val binaryString = Integer.toBinaryString(anInt)
val formatted = String.format("%32s", binaryString)
val zeroed = formatted.replace(" ".toRegex(), "0")
val zeroed = anInt.toString(2).padStart(32, '0')
val underScored = zeroed.replace("....".toRegex(), "$0_")
val noTrailingUnderscore = underScored.substring(0, underScored.length - 1)
return "0b" + noTrailingUnderscore

View File

@@ -0,0 +1,3 @@
package mtmc.util
expect fun currentTimeMillis(): Long

View File

@@ -0,0 +1,3 @@
package mtmc.util
typealias Runnable = () -> Unit

View File

@@ -0,0 +1,5 @@
package mtmc.util
import kotlin.js.Date
actual fun currentTimeMillis(): Long = Date().getTime().toLong()

Some files were not shown because too many files have changed in this diff Show More