generated from rnentjes/kotlin-server-web-undertow
Refactor rewind functionality with circular buffer, update BufferedImageData handling in DisplayView, enhance ConsoleView rendering updates, and integrate SnakeCode data.
This commit is contained in:
@@ -67,7 +67,7 @@ kotlin {
|
|||||||
}
|
}
|
||||||
val jsMain by getting {
|
val jsMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("nl.astraeus:kotlin-komponent:1.2.5")
|
implementation("nl.astraeus:kotlin-komponent:1.2.8")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val jsTest by getting
|
val jsTest by getting
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ abstract class Instruction(
|
|||||||
if (ls != null) {
|
if (ls != null) {
|
||||||
return ls
|
return ls
|
||||||
}
|
}
|
||||||
val jumpReg = JumpInstruction.disassemble(instruction)
|
val jumpReg = JumpRegisterInstruction.disassemble(instruction)
|
||||||
if (jumpReg != null) {
|
if (jumpReg != null) {
|
||||||
return jumpReg
|
return jumpReg
|
||||||
}
|
}
|
||||||
@@ -65,7 +65,7 @@ abstract class Instruction(
|
|||||||
if (jump != null) {
|
if (jump != null) {
|
||||||
return jump
|
return jump
|
||||||
}
|
}
|
||||||
return "<unknown>"
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,10 +26,16 @@ class JumpRegisterInstruction(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun disassemble(instruction: Short): String? {
|
fun disassemble(instruction: Short): String? {
|
||||||
if (BinaryUtils.getBits(16, 5, instruction).toInt() == 9) {
|
if (BinaryUtils.getBits(16, 4, instruction).toInt() == 9) {
|
||||||
val reg = BinaryUtils.getBits(4, 4, instruction)
|
val reg = BinaryUtils.getBits(4, 4, instruction)
|
||||||
val sb = StringBuilder("jr")
|
if (reg.toInt() == 11) {
|
||||||
sb.append(fromInteger(reg.toInt()))
|
return "ret"
|
||||||
|
} else {
|
||||||
|
val sb = StringBuilder("jr")
|
||||||
|
sb.append(" ")
|
||||||
|
sb.append(fromInteger(reg.toInt()))
|
||||||
|
return sb.toString()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package mtmc.emulator
|
|||||||
|
|
||||||
expect fun createBufferedImage(width: Int, height: Int): BufferedImage
|
expect fun createBufferedImage(width: Int, height: Int): BufferedImage
|
||||||
|
|
||||||
|
expect fun createCanvasImage(width: Int, height: Int): BufferedImage
|
||||||
|
|
||||||
interface BufferedImage {
|
interface BufferedImage {
|
||||||
val width: Int
|
val width: Int
|
||||||
val height: Int
|
val height: Int
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package mtmc.emulator
|
|||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
class MTMCDisplay(private val computer: MonTanaMiniComputer) {
|
class MTMCDisplay(private val computer: MonTanaMiniComputer) {
|
||||||
private val buffer: BufferedImage? = null
|
val buffer: BufferedImage = createCanvasImage(COLS, ROWS)
|
||||||
private var currentColor: DisplayColor? = null
|
private var currentColor: DisplayColor? = null
|
||||||
private var graphics: Array<BufferedImage> = arrayOf()
|
private var graphics: Array<BufferedImage> = arrayOf()
|
||||||
private var byteArray: ByteArray = ByteArray(0)
|
private var byteArray: ByteArray = ByteArray(0)
|
||||||
@@ -107,11 +107,11 @@ class MTMCDisplay(private val computer: MonTanaMiniComputer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setPixel(col: Int, row: Int, color: DisplayColor) {
|
fun setPixel(col: Int, row: Int, color: DisplayColor) {
|
||||||
buffer?.setRGB(col, row, color.intVal)
|
buffer.setRGB(col, row, color.intVal)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPixel(col: Int, row: Int): Short {
|
fun getPixel(col: Int, row: Int): Short {
|
||||||
val rgb = buffer?.getRGB(col, row) ?: 0
|
val rgb = buffer.getRGB(col, row)
|
||||||
return DisplayColor.indexFromInt(rgb)
|
return DisplayColor.indexFromInt(rgb)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,10 +123,11 @@ class MTMCDisplay(private val computer: MonTanaMiniComputer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun drawRectangle(startCol: Short, startRow: Short, width: Short, height: Short) {
|
fun drawRectangle(startCol: Short, startRow: Short, width: Short, height: Short) {
|
||||||
// val graphics = buffer.getGraphics()
|
for (x in startCol..<startCol + width) {
|
||||||
// graphics.setColor(currentColor!!.javaColor)
|
for (y in startRow..<startRow + height) {
|
||||||
// graphics.fillRect(startCol.toInt(), startRow.toInt(), width.toInt(), height.toInt())
|
setPixel(x, y, currentColor!!)
|
||||||
// graphics.dispose()
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun drawImage(image: Int, x: Int, y: Int) {
|
fun drawImage(image: Int, x: Int, y: Int) {
|
||||||
|
|||||||
@@ -31,7 +31,9 @@ class MonTanaMiniComputer {
|
|||||||
var display: MTMCDisplay = MTMCDisplay(this)
|
var display: MTMCDisplay = MTMCDisplay(this)
|
||||||
var clock: MTMCClock = MTMCClock(this)
|
var clock: MTMCClock = MTMCClock(this)
|
||||||
var fileSystem: FileSystem = FileSystem(this)
|
var fileSystem: FileSystem = FileSystem(this)
|
||||||
var rewindSteps = mutableListOf<RewindStep>()
|
|
||||||
|
var rewindSteps = Array<RewindStep?>(MAX_REWIND_STEPS) { null }
|
||||||
|
var rewindIndex = -1
|
||||||
|
|
||||||
// listeners
|
// listeners
|
||||||
private val observers = mutableListOf<MTMCObserver>()
|
private val observers = mutableListOf<MTMCObserver>()
|
||||||
@@ -46,12 +48,12 @@ class MonTanaMiniComputer {
|
|||||||
registerFile = ShortArray(Register.entries.size)
|
registerFile = ShortArray(Register.entries.size)
|
||||||
memory = ByteArray(MEMORY_SIZE)
|
memory = ByteArray(MEMORY_SIZE)
|
||||||
breakpoints = ByteArray(MEMORY_SIZE)
|
breakpoints = ByteArray(MEMORY_SIZE)
|
||||||
rewindSteps.clear()
|
rewindIndex = -1
|
||||||
setRegisterValue(
|
setRegisterValue(
|
||||||
Register.SP,
|
Register.SP,
|
||||||
MEMORY_SIZE.toShort().toInt()
|
MEMORY_SIZE.toShort().toInt()
|
||||||
) // default the stack pointer to the top of memory
|
) // default the stack pointer to the top of memory
|
||||||
rewindSteps.clear()
|
rewindIndex = -1
|
||||||
observers.forEach { obj ->
|
observers.forEach { obj ->
|
||||||
obj.computerReset()
|
obj.computerReset()
|
||||||
}
|
}
|
||||||
@@ -134,7 +136,8 @@ class MonTanaMiniComputer {
|
|||||||
fun fetchAndExecute() {
|
fun fetchAndExecute() {
|
||||||
currentRewindStep = RewindStep()
|
currentRewindStep = RewindStep()
|
||||||
currentRewindStep?.let {
|
currentRewindStep?.let {
|
||||||
rewindSteps.add(0, it)
|
rewindIndex = (rewindIndex + 1) % rewindSteps.size
|
||||||
|
rewindSteps.set(rewindIndex, it)
|
||||||
}
|
}
|
||||||
fetchCurrentInstruction()
|
fetchCurrentInstruction()
|
||||||
val instruction = getRegisterValue(Register.IR)
|
val instruction = getRegisterValue(Register.IR)
|
||||||
@@ -795,11 +798,8 @@ class MonTanaMiniComputer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun addRewindStep(runnable: Runnable?) {
|
private fun addRewindStep(runnable: Runnable?) {
|
||||||
if (currentRewindStep != null && rewindSteps != null) {
|
if (currentRewindStep != null) {
|
||||||
currentRewindStep!!.addSubStep(runnable)
|
currentRewindStep!!.addSubStep(runnable)
|
||||||
if (rewindSteps.size > MAX_REWIND_STEPS) {
|
|
||||||
rewindSteps.removeLast()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -954,12 +954,15 @@ class MonTanaMiniComputer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun rewind() {
|
fun rewind() {
|
||||||
val latestRewindStep = rewindSteps.removeFirst()
|
if (rewindIndex >= 0) {
|
||||||
latestRewindStep.rewind()
|
val latestRewindStep = rewindSteps[rewindIndex]
|
||||||
|
rewindIndex--
|
||||||
|
latestRewindStep?.rewind()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val isBackAvailable: Boolean
|
val isBackAvailable: Boolean
|
||||||
get() = !rewindSteps!!.isEmpty()
|
get() = rewindIndex >= 0
|
||||||
|
|
||||||
enum class ComputerStatus {
|
enum class ComputerStatus {
|
||||||
READY,
|
READY,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package mtmc.emulator
|
|||||||
import mtmc.util.Runnable
|
import mtmc.util.Runnable
|
||||||
|
|
||||||
class RewindStep {
|
class RewindStep {
|
||||||
var subSteps: MutableList<Runnable?> = ArrayList<Runnable?>()
|
var subSteps: MutableList<Runnable?> = mutableListOf()
|
||||||
|
|
||||||
fun rewind() {
|
fun rewind() {
|
||||||
subSteps.reversed().forEach({ obj: Runnable? -> obj!!.invoke() })
|
subSteps.reversed().forEach({ obj: Runnable? -> obj!!.invoke() })
|
||||||
|
|||||||
4817
src/jsMain/kotlin/mtmc/Code.kt
Normal file
4817
src/jsMain/kotlin/mtmc/Code.kt
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,12 @@
|
|||||||
package mtmc.emulator
|
package mtmc.emulator
|
||||||
|
|
||||||
|
import kotlinx.browser.document
|
||||||
|
import org.khronos.webgl.get
|
||||||
|
import org.khronos.webgl.set
|
||||||
|
import org.w3c.dom.CanvasRenderingContext2D
|
||||||
|
import org.w3c.dom.HTMLCanvasElement
|
||||||
|
import org.w3c.dom.ImageData
|
||||||
|
|
||||||
actual fun createBufferedImage(
|
actual fun createBufferedImage(
|
||||||
width: Int,
|
width: Int,
|
||||||
height: Int
|
height: Int
|
||||||
@@ -25,10 +32,44 @@ class BufferedImageImpl(
|
|||||||
check(x in 0 until width && y in 0 until height)
|
check(x in 0 until width && y in 0 until height)
|
||||||
|
|
||||||
val offset = (x + y * width) * 4
|
val offset = (x + y * width) * 4
|
||||||
display[offset + 0] = intVal.toByte()
|
display[offset + 0] = (intVal shr 16).toByte()
|
||||||
display[offset + 1] = (intVal shr 8).toByte()
|
display[offset + 1] = (intVal shr 8).toByte()
|
||||||
display[offset + 2] = (intVal shr 16).toByte()
|
display[offset + 2] = intVal.toByte()
|
||||||
display[offset + 3] = 255.toByte()
|
display[offset + 3] = 255.toByte()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class BufferedImageData(
|
||||||
|
val imageData: ImageData,
|
||||||
|
) : BufferedImage {
|
||||||
|
override val width: Int = imageData.width
|
||||||
|
override val height: Int = imageData.height
|
||||||
|
|
||||||
|
override fun getRGB(x: Int, y: Int): Int {
|
||||||
|
check(x in 0 until width && y in 0 until height)
|
||||||
|
|
||||||
|
val offset = (x * 4 + y * width * 4)
|
||||||
|
val display = imageData.data
|
||||||
|
return (display[offset + 0].toInt() and 0xff) shl 16 +
|
||||||
|
(display[offset + 1].toInt() and 0xff) shl 8 +
|
||||||
|
(display[offset + 2].toInt() and 0xff)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setRGB(x: Int, y: Int, intVal: Int) {
|
||||||
|
check(x in 0 until width && y in 0 until height)
|
||||||
|
|
||||||
|
val offset = (x * 4 + y * width * 4)
|
||||||
|
val display = imageData.data
|
||||||
|
display[offset + 0] = ((intVal shr 16) and 0xff).toShort().asDynamic()
|
||||||
|
display[offset + 1] = ((intVal shr 8) and 0xff).toShort().asDynamic()
|
||||||
|
display[offset + 2] = (intVal and 0xff).toShort().asDynamic()
|
||||||
|
display[offset + 3] = 255.toShort().asDynamic()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val canvas = document.createElement("canvas") as HTMLCanvasElement
|
||||||
|
val ctx = canvas.getContext("2d") as CanvasRenderingContext2D
|
||||||
|
|
||||||
|
actual fun createCanvasImage(width: Int, height: Int): BufferedImage {
|
||||||
|
return BufferedImageData(ctx.createImageData(width.toDouble(), height.toDouble()));
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package mtmc.util
|
package mtmc.util
|
||||||
|
|
||||||
import kotlinx.browser.window
|
import kotlinx.browser.window
|
||||||
|
import mtmc.display
|
||||||
import mtmc.mainView
|
import mtmc.mainView
|
||||||
import kotlin.js.Date
|
import kotlin.js.Date
|
||||||
|
|
||||||
@@ -11,8 +12,9 @@ actual fun requestAnimationFrame(action: (Double) -> Unit) {
|
|||||||
window.requestAnimationFrame {
|
window.requestAnimationFrame {
|
||||||
action(it)
|
action(it)
|
||||||
|
|
||||||
mainView.registerView.requestUpdate()
|
display.requestUpdate()
|
||||||
if (currentTimeMillis() - lastMemoryUpdate > 100) {
|
if (currentTimeMillis() - lastMemoryUpdate > 100) {
|
||||||
|
mainView.registerView.requestUpdate()
|
||||||
mainView.memoryView.requestUpdate()
|
mainView.memoryView.requestUpdate()
|
||||||
lastMemoryUpdate = currentTimeMillis()
|
lastMemoryUpdate = currentTimeMillis()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import kotlinx.html.input
|
|||||||
import kotlinx.html.js.onClickFunction
|
import kotlinx.html.js.onClickFunction
|
||||||
import kotlinx.html.js.onKeyUpFunction
|
import kotlinx.html.js.onKeyUpFunction
|
||||||
import kotlinx.html.span
|
import kotlinx.html.span
|
||||||
|
import mtmc.display
|
||||||
import mtmc.emulator.MonTanaMiniComputer
|
import mtmc.emulator.MonTanaMiniComputer
|
||||||
import mtmc.mainView
|
import mtmc.mainView
|
||||||
import mtmc.os.shell.Shell
|
import mtmc.os.shell.Shell
|
||||||
@@ -65,7 +66,10 @@ class ConsoleView(
|
|||||||
Shell.execCommand(input, computer)
|
Shell.execCommand(input, computer)
|
||||||
|
|
||||||
input = ""
|
input = ""
|
||||||
mainView.requestUpdate()
|
mainView.registerView.requestUpdate()
|
||||||
|
mainView.memoryView.requestUpdate()
|
||||||
|
display.requestUpdate()
|
||||||
|
requestUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ package mtmc.view
|
|||||||
import kotlinx.html.canvas
|
import kotlinx.html.canvas
|
||||||
import kotlinx.html.div
|
import kotlinx.html.div
|
||||||
import mtmc.display
|
import mtmc.display
|
||||||
|
import mtmc.emulator.BufferedImageData
|
||||||
import mtmc.emulator.MonTanaMiniComputer
|
import mtmc.emulator.MonTanaMiniComputer
|
||||||
import nl.astraeus.komp.HtmlBuilder
|
import nl.astraeus.komp.HtmlBuilder
|
||||||
import nl.astraeus.komp.Komponent
|
import nl.astraeus.komp.Komponent
|
||||||
import nl.astraeus.komp.currentElement
|
import nl.astraeus.komp.currentElement
|
||||||
import org.w3c.dom.CanvasRenderingContext2D
|
import org.w3c.dom.CanvasRenderingContext2D
|
||||||
import org.w3c.dom.HTMLCanvasElement
|
import org.w3c.dom.HTMLCanvasElement
|
||||||
import org.w3c.dom.ImageData
|
|
||||||
|
|
||||||
class DiplayControlView(
|
class DiplayControlView(
|
||||||
val computer: MonTanaMiniComputer
|
val computer: MonTanaMiniComputer
|
||||||
@@ -26,7 +26,6 @@ class DisplayView(
|
|||||||
val computer: MonTanaMiniComputer
|
val computer: MonTanaMiniComputer
|
||||||
) : Komponent() {
|
) : Komponent() {
|
||||||
var ctx: CanvasRenderingContext2D? = null
|
var ctx: CanvasRenderingContext2D? = null
|
||||||
var imageData: ImageData? = null
|
|
||||||
|
|
||||||
override fun HtmlBuilder.render() {
|
override fun HtmlBuilder.render() {
|
||||||
canvas("display-canvas") {
|
canvas("display-canvas") {
|
||||||
@@ -37,16 +36,16 @@ class DisplayView(
|
|||||||
|
|
||||||
ctx = cv?.getContext("2d")?.unsafeCast<CanvasRenderingContext2D>()
|
ctx = cv?.getContext("2d")?.unsafeCast<CanvasRenderingContext2D>()
|
||||||
|
|
||||||
ctx?.fillStyle = "#404040"
|
ctx?.fillStyle = "#400040"
|
||||||
ctx?.fillRect(0.0, 0.0, 160.0, 144.0)
|
ctx?.fillRect(0.0, 0.0, 160.0, 144.0)
|
||||||
imageData = ctx?.getImageData(0.0, 0.0, 160.0, 144.0)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun renderUpdate() {
|
override fun renderUpdate() {
|
||||||
// move data to canvas
|
// move data to canvas
|
||||||
imageData?.let { id ->
|
val buffer = computer.display.buffer
|
||||||
ctx?.putImageData(id, 0.0, 0.0)
|
if (buffer is BufferedImageData) {
|
||||||
|
ctx?.putImageData(buffer.imageData, 0.0, 0.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,4 +2,8 @@ package mtmc.emulator
|
|||||||
|
|
||||||
actual fun createBufferedImage(width: Int, height: Int): BufferedImage {
|
actual fun createBufferedImage(width: Int, height: Int): BufferedImage {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun createCanvasImage(width: Int, height: Int): BufferedImage {
|
||||||
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
@@ -5,4 +5,4 @@ actual fun requestAnimationFrame(action: (Double) -> Unit) {
|
|||||||
error("requestAnimationFrame is not supported on JVM")
|
error("requestAnimationFrame is not supported on JVM")
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun immediateTimeout(action: (Double) -> Unit): Int {}
|
actual fun immediateTimeout(action: (Double) -> Unit): Int = 0
|
||||||
Reference in New Issue
Block a user