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 {
|
||||
dependencies {
|
||||
implementation("nl.astraeus:kotlin-komponent:1.2.5")
|
||||
implementation("nl.astraeus:kotlin-komponent:1.2.8")
|
||||
}
|
||||
}
|
||||
val jsTest by getting
|
||||
|
||||
@@ -57,7 +57,7 @@ abstract class Instruction(
|
||||
if (ls != null) {
|
||||
return ls
|
||||
}
|
||||
val jumpReg = JumpInstruction.disassemble(instruction)
|
||||
val jumpReg = JumpRegisterInstruction.disassemble(instruction)
|
||||
if (jumpReg != null) {
|
||||
return jumpReg
|
||||
}
|
||||
@@ -65,7 +65,7 @@ abstract class Instruction(
|
||||
if (jump != null) {
|
||||
return jump
|
||||
}
|
||||
return "<unknown>"
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,10 +26,16 @@ class JumpRegisterInstruction(
|
||||
|
||||
companion object {
|
||||
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 sb = StringBuilder("jr")
|
||||
sb.append(fromInteger(reg.toInt()))
|
||||
if (reg.toInt() == 11) {
|
||||
return "ret"
|
||||
} else {
|
||||
val sb = StringBuilder("jr")
|
||||
sb.append(" ")
|
||||
sb.append(fromInteger(reg.toInt()))
|
||||
return sb.toString()
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package mtmc.emulator
|
||||
|
||||
expect fun createBufferedImage(width: Int, height: Int): BufferedImage
|
||||
|
||||
expect fun createCanvasImage(width: Int, height: Int): BufferedImage
|
||||
|
||||
interface BufferedImage {
|
||||
val width: Int
|
||||
val height: Int
|
||||
|
||||
@@ -3,7 +3,7 @@ package mtmc.emulator
|
||||
import kotlin.math.min
|
||||
|
||||
class MTMCDisplay(private val computer: MonTanaMiniComputer) {
|
||||
private val buffer: BufferedImage? = null
|
||||
val buffer: BufferedImage = createCanvasImage(COLS, ROWS)
|
||||
private var currentColor: DisplayColor? = null
|
||||
private var graphics: Array<BufferedImage> = arrayOf()
|
||||
private var byteArray: ByteArray = ByteArray(0)
|
||||
@@ -107,11 +107,11 @@ class MTMCDisplay(private val computer: MonTanaMiniComputer) {
|
||||
}
|
||||
|
||||
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 {
|
||||
val rgb = buffer?.getRGB(col, row) ?: 0
|
||||
val rgb = buffer.getRGB(col, row)
|
||||
return DisplayColor.indexFromInt(rgb)
|
||||
}
|
||||
|
||||
@@ -123,10 +123,11 @@ class MTMCDisplay(private val computer: MonTanaMiniComputer) {
|
||||
}
|
||||
|
||||
fun drawRectangle(startCol: Short, startRow: Short, width: Short, height: Short) {
|
||||
// val graphics = buffer.getGraphics()
|
||||
// graphics.setColor(currentColor!!.javaColor)
|
||||
// graphics.fillRect(startCol.toInt(), startRow.toInt(), width.toInt(), height.toInt())
|
||||
// graphics.dispose()
|
||||
for (x in startCol..<startCol + width) {
|
||||
for (y in startRow..<startRow + height) {
|
||||
setPixel(x, y, currentColor!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun drawImage(image: Int, x: Int, y: Int) {
|
||||
|
||||
@@ -31,7 +31,9 @@ class MonTanaMiniComputer {
|
||||
var display: MTMCDisplay = MTMCDisplay(this)
|
||||
var clock: MTMCClock = MTMCClock(this)
|
||||
var fileSystem: FileSystem = FileSystem(this)
|
||||
var rewindSteps = mutableListOf<RewindStep>()
|
||||
|
||||
var rewindSteps = Array<RewindStep?>(MAX_REWIND_STEPS) { null }
|
||||
var rewindIndex = -1
|
||||
|
||||
// listeners
|
||||
private val observers = mutableListOf<MTMCObserver>()
|
||||
@@ -46,12 +48,12 @@ class MonTanaMiniComputer {
|
||||
registerFile = ShortArray(Register.entries.size)
|
||||
memory = ByteArray(MEMORY_SIZE)
|
||||
breakpoints = ByteArray(MEMORY_SIZE)
|
||||
rewindSteps.clear()
|
||||
rewindIndex = -1
|
||||
setRegisterValue(
|
||||
Register.SP,
|
||||
MEMORY_SIZE.toShort().toInt()
|
||||
) // default the stack pointer to the top of memory
|
||||
rewindSteps.clear()
|
||||
rewindIndex = -1
|
||||
observers.forEach { obj ->
|
||||
obj.computerReset()
|
||||
}
|
||||
@@ -134,7 +136,8 @@ class MonTanaMiniComputer {
|
||||
fun fetchAndExecute() {
|
||||
currentRewindStep = RewindStep()
|
||||
currentRewindStep?.let {
|
||||
rewindSteps.add(0, it)
|
||||
rewindIndex = (rewindIndex + 1) % rewindSteps.size
|
||||
rewindSteps.set(rewindIndex, it)
|
||||
}
|
||||
fetchCurrentInstruction()
|
||||
val instruction = getRegisterValue(Register.IR)
|
||||
@@ -795,11 +798,8 @@ class MonTanaMiniComputer {
|
||||
}
|
||||
|
||||
private fun addRewindStep(runnable: Runnable?) {
|
||||
if (currentRewindStep != null && rewindSteps != null) {
|
||||
if (currentRewindStep != null) {
|
||||
currentRewindStep!!.addSubStep(runnable)
|
||||
if (rewindSteps.size > MAX_REWIND_STEPS) {
|
||||
rewindSteps.removeLast()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -954,12 +954,15 @@ class MonTanaMiniComputer {
|
||||
}
|
||||
|
||||
fun rewind() {
|
||||
val latestRewindStep = rewindSteps.removeFirst()
|
||||
latestRewindStep.rewind()
|
||||
if (rewindIndex >= 0) {
|
||||
val latestRewindStep = rewindSteps[rewindIndex]
|
||||
rewindIndex--
|
||||
latestRewindStep?.rewind()
|
||||
}
|
||||
}
|
||||
|
||||
val isBackAvailable: Boolean
|
||||
get() = !rewindSteps!!.isEmpty()
|
||||
get() = rewindIndex >= 0
|
||||
|
||||
enum class ComputerStatus {
|
||||
READY,
|
||||
|
||||
@@ -3,7 +3,7 @@ package mtmc.emulator
|
||||
import mtmc.util.Runnable
|
||||
|
||||
class RewindStep {
|
||||
var subSteps: MutableList<Runnable?> = ArrayList<Runnable?>()
|
||||
var subSteps: MutableList<Runnable?> = mutableListOf()
|
||||
|
||||
fun rewind() {
|
||||
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
|
||||
|
||||
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(
|
||||
width: Int,
|
||||
height: Int
|
||||
@@ -25,10 +32,44 @@ class BufferedImageImpl(
|
||||
check(x in 0 until width && y in 0 until height)
|
||||
|
||||
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 + 2] = (intVal shr 16).toByte()
|
||||
display[offset + 2] = intVal.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
|
||||
|
||||
import kotlinx.browser.window
|
||||
import mtmc.display
|
||||
import mtmc.mainView
|
||||
import kotlin.js.Date
|
||||
|
||||
@@ -11,8 +12,9 @@ actual fun requestAnimationFrame(action: (Double) -> Unit) {
|
||||
window.requestAnimationFrame {
|
||||
action(it)
|
||||
|
||||
mainView.registerView.requestUpdate()
|
||||
display.requestUpdate()
|
||||
if (currentTimeMillis() - lastMemoryUpdate > 100) {
|
||||
mainView.registerView.requestUpdate()
|
||||
mainView.memoryView.requestUpdate()
|
||||
lastMemoryUpdate = currentTimeMillis()
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import kotlinx.html.input
|
||||
import kotlinx.html.js.onClickFunction
|
||||
import kotlinx.html.js.onKeyUpFunction
|
||||
import kotlinx.html.span
|
||||
import mtmc.display
|
||||
import mtmc.emulator.MonTanaMiniComputer
|
||||
import mtmc.mainView
|
||||
import mtmc.os.shell.Shell
|
||||
@@ -65,7 +66,10 @@ class ConsoleView(
|
||||
Shell.execCommand(input, computer)
|
||||
|
||||
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.div
|
||||
import mtmc.display
|
||||
import mtmc.emulator.BufferedImageData
|
||||
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 DiplayControlView(
|
||||
val computer: MonTanaMiniComputer
|
||||
@@ -26,7 +26,6 @@ class DisplayView(
|
||||
val computer: MonTanaMiniComputer
|
||||
) : Komponent() {
|
||||
var ctx: CanvasRenderingContext2D? = null
|
||||
var imageData: ImageData? = null
|
||||
|
||||
override fun HtmlBuilder.render() {
|
||||
canvas("display-canvas") {
|
||||
@@ -37,16 +36,16 @@ class DisplayView(
|
||||
|
||||
ctx = cv?.getContext("2d")?.unsafeCast<CanvasRenderingContext2D>()
|
||||
|
||||
ctx?.fillStyle = "#404040"
|
||||
ctx?.fillStyle = "#400040"
|
||||
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)
|
||||
val buffer = computer.display.buffer
|
||||
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 {
|
||||
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")
|
||||
}
|
||||
|
||||
actual fun immediateTimeout(action: (Double) -> Unit): Int {}
|
||||
actual fun immediateTimeout(action: (Double) -> Unit): Int = 0
|
||||
Reference in New Issue
Block a user