generated from rnentjes/kotlin-server-web-undertow
Remove legacy JVM-specific file system, shell, and related implementations; migrate to platform-agnostic and common main modules.
This commit is contained in:
459
src/commonMain/kotlin/mtmc/os/MTOS.kt
Normal file
459
src/commonMain/kotlin/mtmc/os/MTOS.kt
Normal file
@@ -0,0 +1,459 @@
|
||||
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 mtmc.util.currentTimeMillis
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.random.Random
|
||||
|
||||
class MTOS(private val computer: MonTanaMiniComputer) {
|
||||
private var timer: Long = 0
|
||||
var random: Random = Random.Default
|
||||
|
||||
// Editor support
|
||||
var currentFile: String? = null
|
||||
var currentFileMime: String? = null
|
||||
var breakpoints: IntArray? = null
|
||||
|
||||
fun applyBreakpoints() {
|
||||
if (computer.debugInfo == null || breakpoints == null) {
|
||||
return
|
||||
}
|
||||
|
||||
var debug = computer.debugInfo!!.originalLineNumbers
|
||||
var name: String? = computer.debugInfo!!.originalFile
|
||||
|
||||
if (this.currentFileMime == "text/x-asm") {
|
||||
debug = computer.debugInfo!!.assemblyLineNumbers
|
||||
name = computer.debugInfo!!.assemblyFile
|
||||
}
|
||||
|
||||
if (debug == null || name != currentFile) {
|
||||
return
|
||||
}
|
||||
|
||||
for (index in breakpoints!!.indices) {
|
||||
val line = breakpoints!![index]
|
||||
for (i in debug.indices) {
|
||||
if (debug[i] == line) {
|
||||
computer.setBreakpoint(i, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun handleSysCall(syscallNumber: Short) {
|
||||
if (syscallNumber == getValue("exit").toShort()) {
|
||||
computer.setStatus(MonTanaMiniComputer.ComputerStatus.FINISHED)
|
||||
} else if (syscallNumber == getValue("rint").toShort()) {
|
||||
// rint
|
||||
if (!computer.console.hasShortValue()) {
|
||||
computer.notifyOfRequestInteger()
|
||||
}
|
||||
while (!computer.console.hasShortValue() && computer.getStatus() == MonTanaMiniComputer.ComputerStatus.EXECUTING) {
|
||||
try {
|
||||
Thread.sleep(10)
|
||||
} catch (e: InterruptedException) {
|
||||
}
|
||||
}
|
||||
val `val` = computer.console.readInt()
|
||||
computer.setRegisterValue(Register.RV, `val`.toInt())
|
||||
} else if (syscallNumber == getValue("wint").toShort()) {
|
||||
// wint
|
||||
val value = computer.getRegisterValue(Register.A0)
|
||||
computer.console.writeInt(value)
|
||||
} else if (syscallNumber == getValue("rchr").toShort()) {
|
||||
if (!computer.console.hasShortValue()) {
|
||||
computer.notifyOfRequestCharacter()
|
||||
}
|
||||
while (!computer.console.hasShortValue() && computer.getStatus() == MonTanaMiniComputer.ComputerStatus.EXECUTING) {
|
||||
try {
|
||||
Thread.sleep(10)
|
||||
} catch (e: InterruptedException) {
|
||||
}
|
||||
}
|
||||
val `val` = computer.console.readChar()
|
||||
computer.setRegisterValue(Register.RV, `val`.code)
|
||||
} else if (syscallNumber == getValue("wchr").toShort()) {
|
||||
val value = computer.getRegisterValue(Register.A0)
|
||||
computer.console.print("" + Char(value.toUShort()))
|
||||
} else if (syscallNumber == getValue("rstr").toShort()) {
|
||||
// rstr
|
||||
val pointer = computer.getRegisterValue(Register.A0)
|
||||
val maxLen = computer.getRegisterValue(Register.A1)
|
||||
if (!computer.console.hasReadString()) {
|
||||
computer.notifyOfRequestString()
|
||||
}
|
||||
while (!computer.console.hasReadString() && computer.getStatus() == MonTanaMiniComputer.ComputerStatus.EXECUTING) {
|
||||
try {
|
||||
Thread.sleep(10)
|
||||
} catch (e: InterruptedException) {
|
||||
}
|
||||
}
|
||||
val string = computer.console.readString()
|
||||
val bytes = string!!.encodeToByteArray()
|
||||
val bytesToRead = min(bytes.size, maxLen.toInt())
|
||||
for (i in 0..<bytesToRead) {
|
||||
val aByte = bytes[i]
|
||||
computer.writeByteToMemory(pointer + i, aByte)
|
||||
}
|
||||
computer.setRegisterValue(Register.RV, bytesToRead)
|
||||
} else if (syscallNumber == getValue("wstr").toShort()) {
|
||||
// wstr
|
||||
val pointer = computer.getRegisterValue(Register.A0)
|
||||
val outputString = readStringFromMemory(pointer)
|
||||
computer.console.print(outputString)
|
||||
} else if (syscallNumber == getValue("printf").toShort()) {
|
||||
val pointer = computer.getRegisterValue(Register.A0)
|
||||
val initSP = computer.getRegisterValue(Register.A1)
|
||||
val fmtString = readStringFromMemory(pointer)
|
||||
val sb = StringBuilder()
|
||||
var stackOff = 0
|
||||
var i = 0
|
||||
while (i < fmtString.length) {
|
||||
var c = fmtString.get(i++)
|
||||
if (c != '%') {
|
||||
sb.append(c)
|
||||
continue
|
||||
}
|
||||
|
||||
if (i >= fmtString.length) break
|
||||
c = fmtString.get(i++)
|
||||
|
||||
if (c == 'd') {
|
||||
stackOff += 2
|
||||
val v = computer.fetchWordFromMemory(initSP - stackOff).toInt()
|
||||
sb.append(v)
|
||||
} else if (c == 'c') {
|
||||
stackOff += 2
|
||||
val v = Char(computer.fetchWordFromMemory(initSP - stackOff).toUShort())
|
||||
sb.append(v)
|
||||
} else if (c == 's') {
|
||||
stackOff += 2
|
||||
val valuePointer = computer.fetchWordFromMemory(initSP - stackOff)
|
||||
val s = readStringFromMemory(valuePointer)
|
||||
sb.append(s)
|
||||
} else {
|
||||
sb.append('%').append(c)
|
||||
}
|
||||
}
|
||||
|
||||
computer.console.print(sb.toString())
|
||||
computer.setRegisterValue(Register.RV, sb.length)
|
||||
} else if (syscallNumber == getValue("atoi").toShort()) {
|
||||
val pointer = computer.getRegisterValue(Register.A0)
|
||||
val string = readStringFromMemory(pointer)
|
||||
val split = string.trim { it <= ' ' }.split("\\s+".toRegex()).dropLastWhile { it.isEmpty() }
|
||||
.toTypedArray()
|
||||
val firstNum = split[0]
|
||||
try {
|
||||
val value = firstNum.toShort()
|
||||
computer.setRegisterValue(Register.RV, value.toInt())
|
||||
} catch (e: NumberFormatException) {
|
||||
computer.setRegisterValue(Register.RV, 0)
|
||||
}
|
||||
} else if (syscallNumber == getValue("rnd").toShort()) {
|
||||
// rnd
|
||||
var low = computer.getRegisterValue(Register.A0)
|
||||
var high = computer.getRegisterValue(Register.A1)
|
||||
val temp: Short
|
||||
|
||||
if (low > high) {
|
||||
temp = low
|
||||
low = high
|
||||
high = temp
|
||||
}
|
||||
|
||||
computer.setRegisterValue(Register.RV, random.nextInt(low.toInt(), high + 1))
|
||||
} else if (syscallNumber == getValue("sleep").toShort()) {
|
||||
// sleep
|
||||
val millis = computer.getRegisterValue(Register.A0)
|
||||
try {
|
||||
if (millis > 0) Thread.sleep(millis.toLong())
|
||||
} catch (e: InterruptedException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
} else if (syscallNumber == getValue("fbreset").toShort()) {
|
||||
// fbreset
|
||||
computer.display.reset()
|
||||
} else if (syscallNumber == getValue("fbstat").toShort()) {
|
||||
// fbstat
|
||||
val x = computer.getRegisterValue(Register.A0)
|
||||
val y = computer.getRegisterValue(Register.A1)
|
||||
val `val` = computer.display.getPixel(x.toInt(), y.toInt())
|
||||
computer.setRegisterValue(Register.RV, `val`.toInt())
|
||||
} else if (syscallNumber == getValue("fbset").toShort()) {
|
||||
// fbset
|
||||
val x = computer.getRegisterValue(Register.A0)
|
||||
val y = computer.getRegisterValue(Register.A1)
|
||||
val color = computer.getRegisterValue(Register.A2)
|
||||
computer.display.setPixel(x.toInt(), y.toInt(), color.toInt())
|
||||
} else if (syscallNumber == getValue("fbline").toShort()) {
|
||||
val startX = computer.getRegisterValue(Register.A0)
|
||||
val startY = computer.getRegisterValue(Register.A1)
|
||||
val endX = computer.getRegisterValue(Register.A2)
|
||||
val endY = computer.getRegisterValue(Register.A3)
|
||||
computer.display.drawLine(startX, startY, endX, endY)
|
||||
} else if (syscallNumber == getValue("fbrect").toShort()) {
|
||||
val startX = computer.getRegisterValue(Register.A0)
|
||||
val startY = computer.getRegisterValue(Register.A1)
|
||||
val width = computer.getRegisterValue(Register.A2)
|
||||
val height = computer.getRegisterValue(Register.A3)
|
||||
computer.display.drawRectangle(startX, startY, width, height)
|
||||
} else if (syscallNumber == getValue("fbflush").toShort()) {
|
||||
computer.display.sync()
|
||||
} else if (syscallNumber == getValue("joystick").toShort()) {
|
||||
computer.setRegisterValue(Register.RV, computer.iOState)
|
||||
} else if (syscallNumber == getValue("scolor").toShort()) {
|
||||
computer.display.setColor(computer.getRegisterValue(Register.A0))
|
||||
} else if (syscallNumber == getValue("memcpy").toShort()) {
|
||||
val fromPointer = computer.getRegisterValue(Register.A0)
|
||||
val toPointer = computer.getRegisterValue(Register.A1)
|
||||
val bytes = computer.getRegisterValue(Register.A2)
|
||||
for (i in 0..<bytes) {
|
||||
val b = computer.fetchByteFromMemory(fromPointer + i)
|
||||
computer.writeByteToMemory(toPointer + i, b)
|
||||
}
|
||||
} else if (syscallNumber == getValue("rfile").toShort()) {
|
||||
val fileNamePtr = computer.getRegisterValue(Register.A0)
|
||||
val fileName = readStringFromMemory(fileNamePtr)
|
||||
val file = File("disk" + computer.fileSystem.resolve(fileName))
|
||||
|
||||
if (!file.exists()) {
|
||||
computer.setRegisterValue(Register.RV, 1)
|
||||
return
|
||||
}
|
||||
|
||||
val destination = computer.getRegisterValue(Register.A1)
|
||||
|
||||
val maxSize1 = computer.getRegisterValue(Register.A2)
|
||||
val maxSize2 = computer.getRegisterValue(Register.A3)
|
||||
|
||||
val fileType = fileName.substring(fileName.lastIndexOf('.') + 1)
|
||||
|
||||
try {
|
||||
// special handling for game-of-life cell files
|
||||
if ("cells" == fileType) {
|
||||
val str = Files.readString(file.toPath())
|
||||
val lines: List<String> =
|
||||
str.split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
.filter { s: String? -> !s!!.startsWith("!") }
|
||||
.toList()
|
||||
|
||||
val linesTotal = lines.size
|
||||
val cappedLines = min(linesTotal, maxSize2.toInt())
|
||||
|
||||
for (lineNum in 0..<cappedLines) {
|
||||
val line: String = lines.get(lineNum)
|
||||
for (colNum in 0..<maxSize1) {
|
||||
val offset = lineNum * maxSize1 + colNum
|
||||
val byteOffset = offset / 8
|
||||
val bitOffset = offset % 8
|
||||
val currentVal = computer.fetchByteFromMemory(destination + byteOffset)
|
||||
val mask = 1 shl bitOffset
|
||||
val newVal: Byte
|
||||
if (colNum < line.length && line.get(colNum) == 'O') {
|
||||
newVal = (currentVal.toInt() or mask).toByte()
|
||||
} else {
|
||||
newVal = (currentVal.toInt() and mask.inv()).toByte()
|
||||
}
|
||||
computer.writeByteToMemory(destination + byteOffset, newVal)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val bytes = Files.readAllBytes(file.toPath())
|
||||
for (i in 0..<maxSize1) {
|
||||
val aByte = bytes[i]
|
||||
computer.writeByteToMemory(destination + i, aByte)
|
||||
}
|
||||
}
|
||||
computer.setRegisterValue(Register.RV, 0)
|
||||
} catch (e: Exception) {
|
||||
computer.setRegisterValue(Register.RV, -1)
|
||||
e.printStackTrace() // debugging
|
||||
}
|
||||
} else if (syscallNumber == getValue("cwd").toShort()) {
|
||||
val cwd = computer.fileSystem.listCWD().path
|
||||
|
||||
val destination = computer.getRegisterValue(Register.A0)
|
||||
val maxSize = min(computer.getRegisterValue(Register.A1).toInt(), cwd.length + 1)
|
||||
|
||||
for (i in 0..<maxSize - 1) {
|
||||
val aByte = cwd.get(i).code.toByte()
|
||||
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)
|
||||
} else if (syscallNumber == getValue("chdir").toShort()) {
|
||||
val pointer = computer.getRegisterValue(Register.A0)
|
||||
val dir = readStringFromMemory(pointer)
|
||||
|
||||
if (computer.fileSystem.exists(dir)) {
|
||||
computer.setRegisterValue(Register.RV, 0)
|
||||
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 = currentTimeMillis() + value
|
||||
|
||||
computer.setRegisterValue(
|
||||
Register.RV,
|
||||
max(0, this.timer - currentTimeMillis()).toInt()
|
||||
)
|
||||
} else if (syscallNumber == getValue("drawimg").toShort()) {
|
||||
val image = computer.getRegisterValue(Register.A0)
|
||||
val x = computer.getRegisterValue(Register.A1)
|
||||
val y = computer.getRegisterValue(Register.A2)
|
||||
|
||||
if (!computer.display.hasGraphic(image.toInt())) {
|
||||
computer.setRegisterValue(Register.RV, 1)
|
||||
return
|
||||
}
|
||||
|
||||
computer.display.drawImage(image.toInt(), x.toInt(), y.toInt())
|
||||
computer.setRegisterValue(Register.RV, 0)
|
||||
} else if (syscallNumber == getValue("drawimgsz").toShort()) {
|
||||
val image = computer.getRegisterValue(Register.A0)
|
||||
val pointer = computer.getRegisterValue(Register.A1)
|
||||
val x = computer.fetchWordFromMemory(pointer.toInt())
|
||||
val y = computer.fetchWordFromMemory(pointer + 2)
|
||||
val width = computer.fetchWordFromMemory(pointer + 4)
|
||||
val height = computer.fetchWordFromMemory(pointer + 6)
|
||||
|
||||
if (!computer.display.hasGraphic(image.toInt())) {
|
||||
computer.setRegisterValue(Register.RV, 1)
|
||||
return
|
||||
}
|
||||
|
||||
computer.display.drawImage(image.toInt(), x.toInt(), y.toInt(), width.toInt(), height.toInt())
|
||||
computer.setRegisterValue(Register.RV, 0)
|
||||
} else if (syscallNumber == getValue("drawimgclip").toShort()) {
|
||||
val image = computer.getRegisterValue(Register.A0)
|
||||
val source = computer.getRegisterValue(Register.A1)
|
||||
val destination = computer.getRegisterValue(Register.A2)
|
||||
|
||||
val sx = computer.fetchWordFromMemory(source.toInt())
|
||||
val sy = computer.fetchWordFromMemory(source + 2)
|
||||
val sw = computer.fetchWordFromMemory(source + 4)
|
||||
val sh = computer.fetchWordFromMemory(source + 6)
|
||||
|
||||
val dx = computer.fetchWordFromMemory(destination.toInt())
|
||||
val dy = computer.fetchWordFromMemory(destination + 2)
|
||||
val dw = computer.fetchWordFromMemory(destination + 4)
|
||||
val dh = computer.fetchWordFromMemory(destination + 6)
|
||||
|
||||
if (!computer.display.hasGraphic(image.toInt())) {
|
||||
computer.setRegisterValue(Register.RV, 1)
|
||||
return
|
||||
}
|
||||
|
||||
computer.display.drawImage(
|
||||
image.toInt(),
|
||||
sx.toInt(),
|
||||
sy.toInt(),
|
||||
sw.toInt(),
|
||||
sh.toInt(),
|
||||
dx.toInt(),
|
||||
dy.toInt(),
|
||||
dw.toInt(),
|
||||
dh.toInt()
|
||||
)
|
||||
computer.setRegisterValue(Register.RV, 0)
|
||||
} else if (syscallNumber == getValue("dirent").toShort()) {
|
||||
val dirent = computer.getRegisterValue(Register.A0)
|
||||
val command = computer.getRegisterValue(Register.A1)
|
||||
val offset = computer.getRegisterValue(Register.A2)
|
||||
val destination = computer.getRegisterValue(Register.A3)
|
||||
|
||||
val maxSize = computer.fetchWordFromMemory(dirent + 2)
|
||||
val maxSizeOut = computer.fetchWordFromMemory(destination + 2)
|
||||
|
||||
val dir = readStringFromMemory(dirent)
|
||||
val list: Array<File>
|
||||
|
||||
if (!computer.fileSystem.exists(dir)) {
|
||||
computer.setRegisterValue(Register.RV, -1)
|
||||
return
|
||||
}
|
||||
|
||||
list = computer.fileSystem.getFileList(dir)
|
||||
|
||||
if (command.toInt() == 0) { // Count of files in the directory
|
||||
computer.setRegisterValue(Register.RV, list.size)
|
||||
}
|
||||
if (command.toInt() == 1) {
|
||||
if (offset < 0 || offset >= list.size) {
|
||||
computer.setRegisterValue(Register.RV, -2)
|
||||
return
|
||||
}
|
||||
|
||||
val file = list[offset.toInt()]
|
||||
val name = file.name
|
||||
val size = min(maxSizeOut - 1, name.length)
|
||||
|
||||
computer.writeWordToMemory(
|
||||
destination.toInt(),
|
||||
if (file.directory) 1 else 0
|
||||
)
|
||||
|
||||
for (i in 0..<size) {
|
||||
val aByte = name.get(i).code.toByte()
|
||||
computer.writeByteToMemory(destination + 4 + i, aByte)
|
||||
}
|
||||
|
||||
//TODO: Should this return the length with or without the null terminator?
|
||||
computer.writeByteToMemory(destination + 4 + size, 0.toByte())
|
||||
computer.setRegisterValue(Register.RV, min(maxSizeOut.toInt(), name.length) - 1)
|
||||
}
|
||||
} else if (syscallNumber == getValue("dfile").toShort()) {
|
||||
val pointer = computer.getRegisterValue(Register.A0)
|
||||
val path = readStringFromMemory(pointer)
|
||||
|
||||
if (computer.fileSystem.delete(path)) {
|
||||
computer.setRegisterValue(Register.RV, 0)
|
||||
} else {
|
||||
computer.setRegisterValue(Register.RV, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun readStringFromMemory(pointer: Short): String {
|
||||
var length: Short = 0
|
||||
while (computer.fetchByteFromMemory(pointer + length).toInt() != 0) {
|
||||
length++
|
||||
}
|
||||
try {
|
||||
val outputString = computer.memory.decodeToString(pointer.toInt(), pointer.toInt() + length)
|
||||
return outputString
|
||||
} catch (ignored: IndexOutOfBoundsException) {
|
||||
computer.setStatus(MonTanaMiniComputer.ComputerStatus.PERMANENT_ERROR)
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
fun processCommand(command: String) {
|
||||
if (!command.trim { it <= ' ' }.isEmpty()) {
|
||||
Shell.execCommand(command, computer)
|
||||
}
|
||||
}
|
||||
|
||||
fun loadFile(path: String?): File = File(path ?: "<empty>")
|
||||
/*
|
||||
{
|
||||
val fs = computer.fileSystem
|
||||
val file = fs.getRealPath(path).toFile()
|
||||
return file
|
||||
}
|
||||
*/
|
||||
}
|
||||
71
src/commonMain/kotlin/mtmc/os/SysCall.kt
Normal file
71
src/commonMain/kotlin/mtmc/os/SysCall.kt
Normal file
@@ -0,0 +1,71 @@
|
||||
package mtmc.os
|
||||
|
||||
enum class SysCall(value: Int) {
|
||||
EXIT(0x00),
|
||||
RINT(0x01),
|
||||
WINT(0x02),
|
||||
RSTR(0x03),
|
||||
WCHR(0x04),
|
||||
RCHR(0x05),
|
||||
WSTR(0x06),
|
||||
PRINTF(0x07),
|
||||
ATOI(0x08),
|
||||
|
||||
RFILE(0x10),
|
||||
WFILE(0x11),
|
||||
CWD(0x12),
|
||||
CHDIR(0x13),
|
||||
DIRENT(0x14),
|
||||
DFILE(0x15),
|
||||
|
||||
RND(0x20),
|
||||
SLEEP(0x21),
|
||||
TIMER(0x22),
|
||||
|
||||
FBRESET(0x30),
|
||||
FBSTAT(0x31),
|
||||
FBSET(0x32),
|
||||
FBLINE(0x33),
|
||||
FBRECT(0x34),
|
||||
FBFLUSH(0x35),
|
||||
JOYSTICK(0x3A),
|
||||
SCOLOR(0x3B),
|
||||
|
||||
MEMCPY(0x40),
|
||||
|
||||
DRAWIMG(0x50),
|
||||
DRAWIMGSZ(0x51),
|
||||
DRAWIMGCLIP(0x52),
|
||||
|
||||
ERROR(0xFF);
|
||||
|
||||
val value: Byte
|
||||
|
||||
init {
|
||||
this.value = value.toByte()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun isSysCall(call: String): Boolean {
|
||||
try {
|
||||
valueOf(call.uppercase())
|
||||
return true
|
||||
} catch (e: IllegalArgumentException) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
fun getValue(call: String): Byte {
|
||||
return valueOf(call.uppercase()).value
|
||||
}
|
||||
|
||||
fun getString(syscallCode: Byte): String? {
|
||||
for (o in entries) {
|
||||
if (o.value == syscallCode) {
|
||||
return o.name.lowercase()
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
58
src/commonMain/kotlin/mtmc/os/exec/Executable.kt
Normal file
58
src/commonMain/kotlin/mtmc/os/exec/Executable.kt
Normal 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)
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
11
src/commonMain/kotlin/mtmc/os/fs/Console.kt
Normal file
11
src/commonMain/kotlin/mtmc/os/fs/Console.kt
Normal 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")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package mtmc.os.fs;
|
||||
|
||||
import mtmc.os.exec.Executable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.spi.FileTypeDetector;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jbanes
|
||||
*/
|
||||
public class ExecutableFileTypeDetector extends FileTypeDetector {
|
||||
|
||||
@Override
|
||||
public String probeContentType(Path path) throws IOException {
|
||||
try(var in = Files.newInputStream(path)) {
|
||||
if(in.read() != '{') return null;
|
||||
}
|
||||
|
||||
try {
|
||||
Executable.load(path);
|
||||
|
||||
return "text/mtmc16-bin";
|
||||
} catch(IOException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
47
src/commonMain/kotlin/mtmc/os/fs/File.kt
Normal file
47
src/commonMain/kotlin/mtmc/os/fs/File.kt
Normal 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")
|
||||
}
|
||||
|
||||
}
|
||||
213
src/commonMain/kotlin/mtmc/os/fs/FileSystem.kt
Normal file
213
src/commonMain/kotlin/mtmc/os/fs/FileSystem.kt
Normal 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()
|
||||
}
|
||||
}
|
||||
45
src/commonMain/kotlin/mtmc/os/fs/Listing.kt
Normal file
45
src/commonMain/kotlin/mtmc/os/fs/Listing.kt
Normal 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
|
||||
}
|
||||
}
|
||||
15
src/commonMain/kotlin/mtmc/os/fs/Path.kt
Normal file
15
src/commonMain/kotlin/mtmc/os/fs/Path.kt
Normal 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")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package mtmc.os.fs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.spi.FileTypeDetector;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jbanes
|
||||
*/
|
||||
public class PlainTextFileTypeDetector extends FileTypeDetector {
|
||||
|
||||
@Override
|
||||
public String probeContentType(Path path) throws IOException {
|
||||
int c;
|
||||
|
||||
try(var in = Files.newInputStream(path)) {
|
||||
// Detect non-ASCII characters
|
||||
while ((c = in.read()) >= 0) {
|
||||
if (c < 9 || c > 126) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "text/plain";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
13
src/commonMain/kotlin/mtmc/os/shell/ShellCommand.kt
Normal file
13
src/commonMain/kotlin/mtmc/os/shell/ShellCommand.kt
Normal file
@@ -0,0 +1,13 @@
|
||||
package mtmc.os.shell
|
||||
|
||||
import mtmc.emulator.MonTanaMiniComputer
|
||||
import mtmc.tokenizer.MTMCTokenizer
|
||||
|
||||
abstract class ShellCommand {
|
||||
@Throws(Exception::class)
|
||||
abstract fun exec(tokens: MTMCTokenizer, computer: MonTanaMiniComputer)
|
||||
abstract val help: String?
|
||||
fun usageException() {
|
||||
throw UsageException(this)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
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 kotlin.random.Random
|
||||
|
||||
class DisplayCommand : ShellCommand() {
|
||||
var random: Random = Random.Default
|
||||
|
||||
@Throws(Exception::class)
|
||||
public override fun exec(tokens: MTMCTokenizer, computer: MonTanaMiniComputer) {
|
||||
if (tokens.match(MTMCToken.TokenType.IDENTIFIER)) {
|
||||
val option = tokens.consumeAsString()
|
||||
when (option) {
|
||||
"fuzz" -> {
|
||||
for (row in 0..<MTMCDisplay.ROWS) {
|
||||
for (col in 0..<MTMCDisplay.COLS) {
|
||||
computer.display.setPixel(col, row, random.nextInt(0, 4).toShort().toInt())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"reset" -> {
|
||||
computer.display.reset()
|
||||
}
|
||||
|
||||
"invert" -> {
|
||||
for (row in 0..<MTMCDisplay.ROWS) {
|
||||
for (col in 0..<MTMCDisplay.COLS) {
|
||||
val color = computer.display.getPixel(col, row)
|
||||
computer.display.setPixel(col, row, 3.toShort() - color)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"image" -> {
|
||||
if (tokens.more()) {
|
||||
val imagePath = tokens.collapseTokensAsString()
|
||||
val file = computer.oS.loadFile(imagePath)
|
||||
//val img = ImageIO.read(file)
|
||||
//computer.display.loadScaledImage(img)
|
||||
} else {
|
||||
usageException()
|
||||
}
|
||||
}
|
||||
|
||||
else -> usageException()
|
||||
}
|
||||
} else if (tokens.match(MTMCToken.TokenType.INTEGER)) {
|
||||
val row = tokens.consumeAsInteger()
|
||||
val col = tokens.require(
|
||||
mtmc.tokenizer.MTMCToken.TokenType.INTEGER
|
||||
) {
|
||||
this.usageException()
|
||||
}!!.intValue()
|
||||
val color = tokens.require(
|
||||
mtmc.tokenizer.MTMCToken.TokenType.INTEGER
|
||||
) {
|
||||
this.usageException()
|
||||
}!!.intValue()
|
||||
computer.display.setPixel(row, col, color)
|
||||
} else {
|
||||
usageException()
|
||||
}
|
||||
computer.display.sync()
|
||||
}
|
||||
|
||||
override val help: String
|
||||
get() = """
|
||||
disp [options] - updates the display
|
||||
fuzz - displays random colors
|
||||
reset - resets the display
|
||||
invert - inverts the display
|
||||
image <file> - loads the given image into the display
|
||||
<x> <y> <color> - sets the given pixel to the given color [0-3]
|
||||
""".trimIndent()
|
||||
|
||||
companion object {
|
||||
private fun loadFile(imagePath: String?): File {
|
||||
val file = File("disk/" + imagePath)
|
||||
return file
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/commonMain/kotlin/mtmc/os/shell/builtins/ExitCommand.kt
Normal file
15
src/commonMain/kotlin/mtmc/os/shell/builtins/ExitCommand.kt
Normal file
@@ -0,0 +1,15 @@
|
||||
package mtmc.os.shell.builtins
|
||||
|
||||
import mtmc.emulator.MonTanaMiniComputer
|
||||
import mtmc.os.shell.ShellCommand
|
||||
import mtmc.tokenizer.MTMCTokenizer
|
||||
|
||||
class ExitCommand : ShellCommand() {
|
||||
public override fun exec(tokens: MTMCTokenizer, computer: MonTanaMiniComputer) {
|
||||
computer.console.println("Goodbye!")
|
||||
//System.exit(1)
|
||||
}
|
||||
|
||||
override val help: String
|
||||
get() = "exit - exits the system"
|
||||
}
|
||||
43
src/commonMain/kotlin/mtmc/os/shell/builtins/GetCommand.kt
Normal file
43
src/commonMain/kotlin/mtmc/os/shell/builtins/GetCommand.kt
Normal file
@@ -0,0 +1,43 @@
|
||||
package mtmc.os.shell.builtins
|
||||
|
||||
import mtmc.emulator.MonTanaMiniComputer
|
||||
import mtmc.emulator.Register
|
||||
import mtmc.os.shell.ShellCommand
|
||||
import mtmc.tokenizer.MTMCToken
|
||||
import mtmc.tokenizer.MTMCTokenizer
|
||||
|
||||
class GetCommand : ShellCommand() {
|
||||
@Throws(Exception::class)
|
||||
public override fun exec(tokens: MTMCTokenizer, computer: MonTanaMiniComputer) {
|
||||
val memLocation = tokens.matchAndConsume(
|
||||
MTMCToken.TokenType.INTEGER,
|
||||
MTMCToken.TokenType.HEX,
|
||||
MTMCToken.TokenType.BINARY
|
||||
)
|
||||
if (memLocation == null) {
|
||||
val register = tokens.matchAndConsume(MTMCToken.TokenType.IDENTIFIER)
|
||||
if (register == null) usageException()
|
||||
val reg = Register.toInteger(register!!.stringValue())
|
||||
if (reg >= 0) {
|
||||
computer.console.println(register.stringValue() + ": " + computer.getRegisterValue(reg))
|
||||
} else {
|
||||
throw IllegalArgumentException("Bad register: " + register.stringValue())
|
||||
}
|
||||
} else {
|
||||
if (memLocation.type === MTMCToken.TokenType.INTEGER || memLocation.type === MTMCToken.TokenType.BINARY || memLocation.type === MTMCToken.TokenType.HEX) {
|
||||
computer.console.println(
|
||||
"MEMORY " + memLocation.intValue() + ": " + computer.fetchWordFromMemory(
|
||||
memLocation.intValue()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val help: String
|
||||
get() = """
|
||||
get <loc> - gets a memory location value
|
||||
loc: a register name or memory location
|
||||
|
||||
""".trimIndent()
|
||||
}
|
||||
16
src/commonMain/kotlin/mtmc/os/shell/builtins/HelpCommand.kt
Normal file
16
src/commonMain/kotlin/mtmc/os/shell/builtins/HelpCommand.kt
Normal file
@@ -0,0 +1,16 @@
|
||||
package mtmc.os.shell.builtins
|
||||
|
||||
import mtmc.emulator.MonTanaMiniComputer
|
||||
import mtmc.os.shell.Shell
|
||||
import mtmc.os.shell.ShellCommand
|
||||
import mtmc.tokenizer.MTMCTokenizer
|
||||
|
||||
class HelpCommand : ShellCommand() {
|
||||
@Throws(Exception::class)
|
||||
public override fun exec(tokens: MTMCTokenizer, computer: MonTanaMiniComputer) {
|
||||
Shell.printShellHelp(computer)
|
||||
}
|
||||
|
||||
override val help: String
|
||||
get() = "? or help - print help"
|
||||
}
|
||||
46
src/commonMain/kotlin/mtmc/os/shell/builtins/LoadCommand.kt
Normal file
46
src/commonMain/kotlin/mtmc/os/shell/builtins/LoadCommand.kt
Normal file
@@ -0,0 +1,46 @@
|
||||
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
|
||||
|
||||
class LoadCommand : ShellCommand() {
|
||||
@Throws(Exception::class)
|
||||
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'" }
|
||||
val srcPath: Path = getDiskPath(program, fs)
|
||||
|
||||
val exec = load(srcPath)
|
||||
computer.load(exec!!.code, exec.data, exec.graphics, exec.debugInfo)
|
||||
val source = tokens.source
|
||||
|
||||
// set up an argument if given
|
||||
if (tokens.more()) {
|
||||
val firstArgToken = tokens.consume()
|
||||
val startChar: Int = firstArgToken.start
|
||||
val arg = source.substring(startChar)
|
||||
val strippedArg = arg.trim()
|
||||
if (!strippedArg.isEmpty()) {
|
||||
computer.setArg(strippedArg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val help: String
|
||||
get() = """
|
||||
load <exec>
|
||||
- exec : path to an executable file
|
||||
""".trimIndent()
|
||||
|
||||
companion object {
|
||||
fun getDiskPath(pathString: String, fs: FileSystem): Path {
|
||||
val path = Path.of("disk" + fs.resolve(pathString))
|
||||
return path.toAbsolutePath()
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/commonMain/kotlin/mtmc/os/shell/builtins/PauseCommand.kt
Normal file
15
src/commonMain/kotlin/mtmc/os/shell/builtins/PauseCommand.kt
Normal file
@@ -0,0 +1,15 @@
|
||||
package mtmc.os.shell.builtins
|
||||
|
||||
import mtmc.emulator.MonTanaMiniComputer
|
||||
import mtmc.os.shell.ShellCommand
|
||||
import mtmc.tokenizer.MTMCTokenizer
|
||||
|
||||
class PauseCommand : ShellCommand() {
|
||||
override fun exec(tokens: MTMCTokenizer, computer: MonTanaMiniComputer) {
|
||||
computer.pause()
|
||||
}
|
||||
override val help: String
|
||||
|
||||
get() = "pause - pauses the computer"
|
||||
}
|
||||
|
||||
18
src/commonMain/kotlin/mtmc/os/shell/builtins/RunCommand.kt
Normal file
18
src/commonMain/kotlin/mtmc/os/shell/builtins/RunCommand.kt
Normal file
@@ -0,0 +1,18 @@
|
||||
package mtmc.os.shell.builtins
|
||||
|
||||
import mtmc.emulator.MonTanaMiniComputer
|
||||
import mtmc.emulator.MonTanaMiniComputer.ComputerStatus
|
||||
import mtmc.os.shell.ShellCommand
|
||||
import mtmc.tokenizer.MTMCTokenizer
|
||||
|
||||
class RunCommand : ShellCommand() {
|
||||
override fun exec(tokens: MTMCTokenizer, computer: MonTanaMiniComputer) {
|
||||
when (computer.getStatus()) {
|
||||
ComputerStatus.READY, ComputerStatus.EXECUTING -> computer.run()
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override val help: String
|
||||
get() = "run - runs the program until it halts"
|
||||
}
|
||||
43
src/commonMain/kotlin/mtmc/os/shell/builtins/SeacCommand.kt
Normal file
43
src/commonMain/kotlin/mtmc/os/shell/builtins/SeacCommand.kt
Normal file
@@ -0,0 +1,43 @@
|
||||
package mtmc.os.shell.builtins
|
||||
|
||||
import mtmc.emulator.MonTanaMiniComputer
|
||||
import mtmc.os.shell.ShellCommand
|
||||
import mtmc.tokenizer.MTMCTokenizer
|
||||
|
||||
class SeacCommand : ShellCommand() {
|
||||
@Throws(Exception::class)
|
||||
override fun exec(tokens: MTMCTokenizer, computer: MonTanaMiniComputer) {
|
||||
var output = "a.out"
|
||||
var filename: String? = null
|
||||
val fs = computer.fileSystem
|
||||
while (tokens.more()) {
|
||||
val token = tokens.collapseTokensAsString()
|
||||
if (token == "-o") {
|
||||
require(tokens.more()) { "expected filename after '-o'" }
|
||||
output = tokens.collapseTokensAsString()
|
||||
} else {
|
||||
filename = token
|
||||
}
|
||||
}
|
||||
|
||||
requireNotNull(filename) { "expected source file" }
|
||||
|
||||
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()
|
||||
*/
|
||||
}
|
||||
|
||||
override val help: String
|
||||
get() = """
|
||||
|
||||
""".trimIndent()
|
||||
}
|
||||
57
src/commonMain/kotlin/mtmc/os/shell/builtins/SetCommand.kt
Normal file
57
src/commonMain/kotlin/mtmc/os/shell/builtins/SetCommand.kt
Normal file
@@ -0,0 +1,57 @@
|
||||
package mtmc.os.shell.builtins
|
||||
|
||||
import mtmc.emulator.MonTanaMiniComputer
|
||||
import mtmc.emulator.Register
|
||||
import mtmc.os.shell.ShellCommand
|
||||
import mtmc.tokenizer.MTMCToken
|
||||
import mtmc.tokenizer.MTMCTokenizer
|
||||
|
||||
class SetCommand : ShellCommand() {
|
||||
@Throws(Exception::class)
|
||||
public override fun exec(tokens: MTMCTokenizer, computer: MonTanaMiniComputer) {
|
||||
val memLocation = tokens.matchAndConsume(
|
||||
MTMCToken.TokenType.INTEGER,
|
||||
MTMCToken.TokenType.HEX,
|
||||
MTMCToken.TokenType.BINARY
|
||||
)
|
||||
if (memLocation == null) {
|
||||
val register = tokens.matchAndConsume(MTMCToken.TokenType.IDENTIFIER)
|
||||
if (register == null) usageException()
|
||||
val value = tokens.matchAndConsume(
|
||||
MTMCToken.TokenType.INTEGER,
|
||||
MTMCToken.TokenType.HEX,
|
||||
MTMCToken.TokenType.BINARY
|
||||
)
|
||||
if (value == null) usageException()
|
||||
val reg = Register.toInteger(register!!.stringValue())
|
||||
if (reg >= 0) {
|
||||
computer.setRegisterValue(reg, value!!.intValue())
|
||||
} else {
|
||||
throw IllegalArgumentException("Bad register: " + register.stringValue())
|
||||
}
|
||||
} else {
|
||||
val value = tokens.matchAndConsume(
|
||||
MTMCToken.TokenType.INTEGER,
|
||||
MTMCToken.TokenType.HEX,
|
||||
MTMCToken.TokenType.BINARY,
|
||||
MTMCToken.TokenType.STRING
|
||||
)
|
||||
if (value == null) usageException()
|
||||
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().encodeToByteArray()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val help: String
|
||||
get() = """
|
||||
set <loc> <value>- sets a memory location value
|
||||
loc: a register name or memory location
|
||||
value: an integer, hex or binary value, or, for memory locations, a quoted string
|
||||
""".trimIndent()
|
||||
}
|
||||
38
src/commonMain/kotlin/mtmc/os/shell/builtins/SpeedCommand.kt
Normal file
38
src/commonMain/kotlin/mtmc/os/shell/builtins/SpeedCommand.kt
Normal file
@@ -0,0 +1,38 @@
|
||||
package mtmc.os.shell.builtins
|
||||
|
||||
import mtmc.emulator.MonTanaMiniComputer
|
||||
import mtmc.os.shell.ShellCommand
|
||||
import mtmc.tokenizer.MTMCToken
|
||||
import mtmc.tokenizer.MTMCTokenizer
|
||||
|
||||
class SpeedCommand : ShellCommand() {
|
||||
private val speeds = listOf(
|
||||
1, 10, 100, 1000, 10000, 100000, 1000000
|
||||
)
|
||||
|
||||
@Throws(Exception::class)
|
||||
override fun exec(tokens: MTMCTokenizer, computer: MonTanaMiniComputer) {
|
||||
if (tokens.match(MTMCToken.TokenType.IDENTIFIER)) {
|
||||
computer.speed = 0
|
||||
} else if (tokens.match(MTMCToken.TokenType.INTEGER)) {
|
||||
val speed = tokens.consumeAsInteger()
|
||||
if (!speeds.contains(speed)) {
|
||||
usageException()
|
||||
}
|
||||
computer.speed = speed
|
||||
} else {
|
||||
usageException()
|
||||
}
|
||||
}
|
||||
|
||||
override val help: String
|
||||
get() = """
|
||||
speed <val> - set the speed of the computer
|
||||
where <val> is one of:
|
||||
raw - run with no simulated speed delay
|
||||
1 - run the computer at 1hz
|
||||
10 - run the computer at 10hz
|
||||
100 - run the computer at 100hz
|
||||
1000 - run the computer at 1khz
|
||||
""".trimIndent()
|
||||
}
|
||||
24
src/commonMain/kotlin/mtmc/os/shell/builtins/StepCommand.kt
Normal file
24
src/commonMain/kotlin/mtmc/os/shell/builtins/StepCommand.kt
Normal file
@@ -0,0 +1,24 @@
|
||||
package mtmc.os.shell.builtins
|
||||
|
||||
import mtmc.emulator.MonTanaMiniComputer
|
||||
import mtmc.emulator.MonTanaMiniComputer.ComputerStatus
|
||||
import mtmc.os.shell.ShellCommand
|
||||
import mtmc.tokenizer.MTMCTokenizer
|
||||
|
||||
class StepCommand : ShellCommand() {
|
||||
override fun exec(tokens: MTMCTokenizer, computer: MonTanaMiniComputer) {
|
||||
when (computer.getStatus()) {
|
||||
ComputerStatus.READY -> {
|
||||
computer.setStatus(ComputerStatus.EXECUTING)
|
||||
computer.fetchAndExecute()
|
||||
}
|
||||
|
||||
ComputerStatus.EXECUTING -> computer.fetchAndExecute()
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
override val help: String
|
||||
get() = "step - runs the next instruction"
|
||||
}
|
||||
|
||||
16
src/commonMain/kotlin/mtmc/os/shell/builtins/WebCommand.kt
Normal file
16
src/commonMain/kotlin/mtmc/os/shell/builtins/WebCommand.kt
Normal file
@@ -0,0 +1,16 @@
|
||||
package mtmc.os.shell.builtins
|
||||
|
||||
import mtmc.emulator.MonTanaMiniComputer
|
||||
import mtmc.os.shell.ShellCommand
|
||||
import mtmc.tokenizer.MTMCTokenizer
|
||||
|
||||
class WebCommand : ShellCommand() {
|
||||
@Throws(Exception::class)
|
||||
public override fun exec(tokens: MTMCTokenizer, computer: MonTanaMiniComputer) {
|
||||
//val server: WebServer = WebServer.getInstance(computer)
|
||||
//Desktop.getDesktop().browse(server.getURL())
|
||||
}
|
||||
|
||||
override val help: String
|
||||
get() = "web - starts the web UI"
|
||||
}
|
||||
Reference in New Issue
Block a user