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

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

View File

@@ -0,0 +1,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
}
*/
}

View 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
}
}
}

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,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;
}
}
}

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

@@ -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";
}
}

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

@@ -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)
}
}

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

@@ -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
}
}
}

View 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"
}

View 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()
}

View 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"
}

View 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()
}
}
}

View 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"
}

View 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"
}

View 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()
}

View 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()
}

View 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()
}

View 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"
}

View 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"
}