diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..bfd4d31 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,13 @@ +Zero-Clause BSD +============= + +Permission to use, copy, modify, and/or distribute this software for +any purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE +FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN +AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 7eff4b1..f0a06e1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,6 @@ @file:OptIn(ExperimentalDistributionDsl::class) +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalDistributionDsl plugins { @@ -21,7 +22,15 @@ repositories { kotlin { jvmToolchain(21) - jvm() + jvm { + @OptIn(ExperimentalKotlinGradlePluginApi::class) + binaries { + // Configures a JavaExec task named "runJvm" and a Gradle distribution for the "main" compilation in this target + executable { + mainClass.set("mtmc.MainKt") + } + } + } js { binaries.executable() browser { @@ -40,9 +49,6 @@ kotlin { val commonMain by getting { dependencies { api("nl.astraeus:kotlin-simple-logging:1.1.1") - api("nl.astraeus:kotlin-css-generator:1.0.10") - - implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.0") } } val commonTest by getting @@ -72,4 +78,4 @@ kotlin { } val jsTest by getting } -} +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 689d139..3e54fe6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Sun Apr 28 09:54:33 CEST 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/commonMain/kotlin/mtmc/emulator/MTMCConsole.kt b/src/commonMain/kotlin/mtmc/emulator/MTMCConsole.kt index 24374eb..c9a6fd8 100644 --- a/src/commonMain/kotlin/mtmc/emulator/MTMCConsole.kt +++ b/src/commonMain/kotlin/mtmc/emulator/MTMCConsole.kt @@ -10,7 +10,7 @@ class MTMCConsole(private val computer: MonTanaMiniComputer) { var sysConsole: Console? = null // non-interactive data - private val output = StringBuilder() + private var output = StringBuilder() private var shortValueSet = false private var shortValue: Short = 0 private var stringValue: String? = null @@ -83,7 +83,9 @@ class MTMCConsole(private val computer: MonTanaMiniComputer) { val text = if (index >= 0) output.substring(0, index + 1) else "" if (index >= 0) { - output.removeRange(0, index + 1) + val updated = StringBuilder() + updated.append(output.removeRange(0, index + 1)) + output = updated } return text @@ -116,7 +118,7 @@ class MTMCConsole(private val computer: MonTanaMiniComputer) { } fun resetOutput() { - output.removeRange(0, output.length) + output.clear() } enum class Mode { diff --git a/src/jsMain/kotlin/mtmc/view/ConsoleView.kt b/src/jsMain/kotlin/mtmc/view/ConsoleView.kt index 26c7cc4..5b3c6c3 100644 --- a/src/jsMain/kotlin/mtmc/view/ConsoleView.kt +++ b/src/jsMain/kotlin/mtmc/view/ConsoleView.kt @@ -19,10 +19,14 @@ import org.w3c.dom.events.KeyboardEvent class ConsoleView( val computer: MonTanaMiniComputer ) : Komponent() { - val history: MutableList = mutableListOf() var input: String = "" + var output = StringBuilder() private var inputElement: HTMLInputElement? = null + init { + output.append(computer.console.consumeLines()) + } + override fun HtmlBuilder.render() { div("console") { onClickFunction = { @@ -30,7 +34,7 @@ class ConsoleView( } div("console-history") { div { - +computer.console.getOutput() + +output.toString() } } div("console-input") { @@ -41,6 +45,7 @@ class ConsoleView( value = input autoFocus = true inputElement = currentElement() as? HTMLInputElement + currentElement().scrollIntoView() window.setTimeout({ inputElement?.focus() }, 0) @@ -61,11 +66,17 @@ class ConsoleView( } private fun handleCommand() { - //history.add(input) + computer.console.print("mtmc$ ") + computer.console.println(input) Shell.execCommand(input, computer) input = "" + output.append(computer.console.consumeLines()) + if (output.length > 1000 && output.contains("\n")) { + output = output.deleteRange(0, output.indexOf("\n")) + } + mainView.registerView.requestUpdate() mainView.memoryView.requestUpdate() display.requestUpdate() diff --git a/src/jsMain/kotlin/mtmc/view/ControlView.kt b/src/jsMain/kotlin/mtmc/view/ControlView.kt index 52f8d0b..b725ac7 100644 --- a/src/jsMain/kotlin/mtmc/view/ControlView.kt +++ b/src/jsMain/kotlin/mtmc/view/ControlView.kt @@ -123,11 +123,12 @@ class ControlView( } } button { - disabled = true +"reset" onClickFunction = { - //computer.reset() + computer.initMemory() + mainView.requestUpdate() + display.requestUpdate() } } } diff --git a/src/jsMain/kotlin/mtmc/view/RegisterView.kt b/src/jsMain/kotlin/mtmc/view/RegisterView.kt index 4477d14..008301a 100644 --- a/src/jsMain/kotlin/mtmc/view/RegisterView.kt +++ b/src/jsMain/kotlin/mtmc/view/RegisterView.kt @@ -8,6 +8,7 @@ import kotlinx.html.classes import kotlinx.html.div import kotlinx.html.hr import kotlinx.html.id +import kotlinx.html.span import kotlinx.html.table import kotlinx.html.td import kotlinx.html.tr @@ -36,9 +37,34 @@ class RegisterView( showRegister(16) showRegister(17) tr { - td { + td("flags") { colSpan = "3" - +"Flags" + span { + +"flags t:" + + div("blinken") { + id = "flags-t" + if (!computer.isFlagTestBitSet) { + classes += "off" + } + } + } + span { + +"o:" + div("blinken") { + id = "flags-o" + classes += "off" + } + } + span { + +"e:" + div("blinken") { + id = "flags-e" + if (computer.getStatus() != MonTanaMiniComputer.ComputerStatus.PERMANENT_ERROR) { + classes += "off" + } + } + } } } } diff --git a/src/jsMain/resources/mtmc.css b/src/jsMain/resources/mtmc.css index 8773a21..bba27ce 100644 --- a/src/jsMain/resources/mtmc.css +++ b/src/jsMain/resources/mtmc.css @@ -128,6 +128,20 @@ table.register-table tr td.register-lights { padding-left: 20px; } +table.register-table tr td.flags { + text-align: center; +} + +table.register-table tr td.flags span { + margin-left: 8px; +} + +table.register-table tr td.flags span .blinken { + margin-left: 4px; + top: 2px; + position: relative; +} + .blinken { display: inline-block; margin-right: 2px; @@ -214,10 +228,11 @@ table.register-table tr td.register-lights { font-family: monospace; font-weight: bold; padding: 5px; + overflow: auto; } .console-prompt { - margin-right: 10px; + margin-right: 6px; } input.console-input { @@ -225,6 +240,8 @@ input.console-input { background-color: #35291c; border: none; outline: none; + font-weight: bold; + font-family: monospace; } .small-button { diff --git a/src/jvmMain/kotlin/mtmc/web/RequestHandler.kt b/src/jvmMain/kotlin/mtmc/web/RequestHandler.kt index 4ea2833..82dfeb6 100644 --- a/src/jvmMain/kotlin/mtmc/web/RequestHandler.kt +++ b/src/jvmMain/kotlin/mtmc/web/RequestHandler.kt @@ -5,12 +5,13 @@ import io.undertow.server.HttpServerExchange import io.undertow.server.handlers.PathHandler import io.undertow.server.handlers.resource.PathResourceManager import io.undertow.server.handlers.resource.ResourceHandler +import io.undertow.util.Headers import mtmc.itemUrl import java.nio.file.Paths -import kotlin.text.startsWith object IndexHandler : HttpHandler { override fun handleRequest(exchange: HttpServerExchange) { + exchange.responseHeaders.put(Headers.CONTENT_TYPE, "text/html") if (exchange.requestPath.startsWith("/$itemUrl/")) { exchange.responseSender.send(generateIndex(null)) } else {