diff --git a/.idea/runConfigurations/MTMC_debug.xml b/.idea/runConfigurations/MTMC_debug.xml new file mode 100644 index 0000000..bc803aa --- /dev/null +++ b/.idea/runConfigurations/MTMC_debug.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/MainKt.xml b/.idea/runConfigurations/MainKt.xml new file mode 100644 index 0000000..16b522e --- /dev/null +++ b/.idea/runConfigurations/MainKt.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/src/commonMain/kotlin/mtmc/emulator/BufferedImage.kt b/src/commonMain/kotlin/mtmc/emulator/BufferedImage.kt index d17a833..05d2496 100644 --- a/src/commonMain/kotlin/mtmc/emulator/BufferedImage.kt +++ b/src/commonMain/kotlin/mtmc/emulator/BufferedImage.kt @@ -8,15 +8,22 @@ class BufferedImage( val display = ByteArray(width * height * 4) fun getRGB(x: Int, y: Int): Int { - return display[x * 4 + y * width * 4 + 0].toInt() + - display[x * 4 + y * width * 4 + 1].toInt() shl 8 + - display[x * 4 + y * width * 4 + 2].toInt() shl 16 + check(x in 0 until width && y in 0 until height) + + val offset = (x + y * width) * 4 + return display[offset + 0].toInt() + + display[offset + 1].toInt() shl 8 + + display[offset + 2].toInt() shl 16 } fun setRGB(x: Int, y: Int, intVal: Int) { - display[x * 4 + y * width * 4 + 0] = intVal.toByte() - display[x * 4 + y * width * 4 + 1] = (intVal shr 8).toByte() - display[x * 4 + y * width * 4 + 2] = (intVal shr 16).toByte() + check(x in 0 until width && y in 0 until height) + + val offset = (x + y * width) * 4 + display[offset + 0] = intVal.toByte() + display[offset + 1] = (intVal shr 8).toByte() + display[offset + 2] = (intVal shr 16).toByte() + display[offset + 3] = 255.toByte() } } diff --git a/src/jsMain/kotlin/mtmc/Main.kt b/src/jsMain/kotlin/mtmc/Main.kt index e9250ed..ef9fb82 100644 --- a/src/jsMain/kotlin/mtmc/Main.kt +++ b/src/jsMain/kotlin/mtmc/Main.kt @@ -4,6 +4,7 @@ import kotlinx.browser.document import kotlinx.html.div import kotlinx.html.style import mtmc.emulator.MonTanaMiniComputer +import mtmc.view.DisplayView import mtmc.view.MTMCView import nl.astraeus.komp.HtmlBuilder import nl.astraeus.komp.Komponent @@ -20,6 +21,7 @@ class HelloKomponent : Komponent() { val computer = MonTanaMiniComputer() val mainView = MTMCView(computer) +val display = DisplayView(computer) fun main() { computer.speed = 1 // default to 1hz diff --git a/src/jsMain/kotlin/mtmc/view/ConsoleView.kt b/src/jsMain/kotlin/mtmc/view/ConsoleView.kt index e2964ae..f1d83a8 100644 --- a/src/jsMain/kotlin/mtmc/view/ConsoleView.kt +++ b/src/jsMain/kotlin/mtmc/view/ConsoleView.kt @@ -3,6 +3,7 @@ package mtmc.view import kotlinx.browser.window import kotlinx.html.div import kotlinx.html.input +import kotlinx.html.js.onClickFunction import kotlinx.html.js.onKeyUpFunction import kotlinx.html.span import mtmc.emulator.MonTanaMiniComputer @@ -19,11 +20,15 @@ class ConsoleView( ) : Komponent() { val history: MutableList = mutableListOf() var input: String = "" + private var inputElement: HTMLInputElement? = null override fun HtmlBuilder.render() { div("console") { +"Console view" + onClickFunction = { + inputElement?.focus() + } div("console-history") { for (line in history) { div { @@ -38,7 +43,7 @@ class ConsoleView( input(classes = "console-input") { value = input autoFocus = true - val inputElement = currentElement() as? HTMLInputElement + inputElement = currentElement() as? HTMLInputElement window.setTimeout({ inputElement?.focus() }, 0) diff --git a/src/jsMain/kotlin/mtmc/view/DisplayView.kt b/src/jsMain/kotlin/mtmc/view/DisplayView.kt index df7a513..a9e7076 100644 --- a/src/jsMain/kotlin/mtmc/view/DisplayView.kt +++ b/src/jsMain/kotlin/mtmc/view/DisplayView.kt @@ -1,18 +1,52 @@ package mtmc.view +import kotlinx.html.canvas import kotlinx.html.div +import mtmc.display import mtmc.emulator.MonTanaMiniComputer import nl.astraeus.komp.HtmlBuilder import nl.astraeus.komp.Komponent +import nl.astraeus.komp.currentElement +import org.w3c.dom.CanvasRenderingContext2D +import org.w3c.dom.HTMLCanvasElement +import org.w3c.dom.ImageData -class DisplayView( +class DiplayControlView( val computer: MonTanaMiniComputer ) : Komponent() { override fun HtmlBuilder.render() { div("display") { - +"Display" + include(display) + } + } +} + +class DisplayView( + val computer: MonTanaMiniComputer +) : Komponent() { + var ctx: CanvasRenderingContext2D? = null + var imageData: ImageData? = null + + override fun HtmlBuilder.render() { + canvas("display-canvas") { + width = "160px" + height = "144px" + + val cv = currentElement() as? HTMLCanvasElement + + ctx = cv?.getContext("2d")?.unsafeCast() + + ctx?.fillStyle = "#404040" + ctx?.fillRect(0.0, 0.0, 160.0, 144.0) + imageData = ctx?.getImageData(0.0, 0.0, 160.0, 144.0) } } + override fun renderUpdate() { + // move data to canvas + imageData?.let { id -> + ctx?.putImageData(id, 0.0, 0.0) + } + } } diff --git a/src/jsMain/kotlin/mtmc/view/MTMCView.kt b/src/jsMain/kotlin/mtmc/view/MTMCView.kt index bc154fe..f0eef51 100644 --- a/src/jsMain/kotlin/mtmc/view/MTMCView.kt +++ b/src/jsMain/kotlin/mtmc/view/MTMCView.kt @@ -11,7 +11,7 @@ class MTMCView( val controlView = ControlView(computer) val registerView = RegisterView(computer) val memoryView = MemoryView(computer) - val displayView = DisplayView(computer) + val displayView = DiplayControlView(computer) val consoleView = ConsoleView(computer) override fun HtmlBuilder.render() { diff --git a/src/jsMain/resources/mtmc.css b/src/jsMain/resources/mtmc.css index 43605ea..feb6039 100644 --- a/src/jsMain/resources/mtmc.css +++ b/src/jsMain/resources/mtmc.css @@ -116,6 +116,13 @@ table.register-table tr td.register-lights { min-height: 480px; } +.display-canvas { + width: 320px; + height: 288px; + image-rendering: pixelated; /* Keeps sharp pixels, no smoothing */ + image-rendering: -moz-crisp-edges; /* Firefox */ + image-rendering: crisp-edges; /* Standard */ +} /* console */ .console {