From 57c16bcb4443c0072cd06cbe836cda1e0e0d6e1a Mon Sep 17 00:00:00 2001 From: minjaesong Date: Sat, 16 May 2020 09:06:17 +0900 Subject: [PATCH] impl of screen ghosting --- assets/tvdos/command.js | 30 +++ src/net/torvald/tsvm/VM.kt | 6 + src/net/torvald/tsvm/VMGUI.kt | 11 +- src/net/torvald/tsvm/VMJSR223Delegate.kt | 2 +- src/net/torvald/tsvm/VMRunnerFactory.kt | 9 +- .../tsvm/peripheral/GraphicsAdapter.kt | 246 +++++++++++------- src/net/torvald/tsvm/peripheral/IOSpace.kt | 1 + terranmon.txt | 25 +- 8 files changed, 217 insertions(+), 113 deletions(-) create mode 100644 assets/tvdos/command.js diff --git a/assets/tvdos/command.js b/assets/tvdos/command.js new file mode 100644 index 0000000..7918963 --- /dev/null +++ b/assets/tvdos/command.js @@ -0,0 +1,30 @@ +var DOS_VERSION = "1.0"; +var PROMPT_TEXT = ">"; +var CURRENT_DRIVE = "A"; + +var shell_pwd = [""]; + +var welcome_text = "TSVM Disk Operating System, version " + DOS_VERSION; + +function get_prompt_text() { + return CURRENT_DRIVE + ":\\\\" + shell_pwd.join("\\\\") + PROMPT_TEXT; +} + +function greet() { + println(welcome_text); + println(); +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +println("Starting TVDOS..."); + +greet(); + +while (true) { + print(get_prompt_text()); + var s = read(); + println(); + println("String read: " + s + "@"); +} \ No newline at end of file diff --git a/src/net/torvald/tsvm/VM.kt b/src/net/torvald/tsvm/VM.kt index 20a6e69..2250df6 100644 --- a/src/net/torvald/tsvm/VM.kt +++ b/src/net/torvald/tsvm/VM.kt @@ -78,6 +78,8 @@ class VM( var getErrorStream: () -> OutputStream = { TODO() } var getInputStream: () -> InputStream = { TODO() } + val startTime: Long + init { peripheralTable[0] = PeripheralEntry( "io", @@ -88,6 +90,8 @@ class VM( ) println("[VM] Creating new VM with ID of $id, memesize $memsize") + + startTime = System.nanoTime() } @@ -107,6 +111,8 @@ class VM( peripheralTable.forEach { it.peripheral?.dispose() } } + open fun getUptime() = System.nanoTime() - startTime + /* NOTE: re-fill peripheralTable whenever the VM cold-boots! you are absolutely not supposed to hot-swap peripheral cards when the computer is on diff --git a/src/net/torvald/tsvm/VMGUI.kt b/src/net/torvald/tsvm/VMGUI.kt index c543d0c..9a3f7ff 100644 --- a/src/net/torvald/tsvm/VMGUI.kt +++ b/src/net/torvald/tsvm/VMGUI.kt @@ -9,8 +9,10 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch import kotlinx.coroutines.* import net.torvald.tsvm.peripheral.GraphicsAdapter import net.torvald.tsvm.peripheral.IOSpace +import java.io.FileReader import java.io.InputStream import java.io.OutputStream +import java.io.StringReader class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter() { @@ -27,7 +29,7 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter() override fun create() { super.create() - gpu = GraphicsAdapter(vm, lcdMode = false) + gpu = GraphicsAdapter(vm, lcdMode = true) vm.peripheralTable[1] = PeripheralEntry( VM.PERITYPE_TERM, @@ -49,9 +51,14 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter() vm.getInputStream = { gpu.getInputStream() } // TEST PRG + val fr = FileReader("./assets/tvdos/command.js") + val prg = fr.readText() + fr.close() + vmRunner = VMRunnerFactory(vm, "js") coroutineJob = GlobalScope.launch { - vmRunner.executeCommand(sanitiseJS(shitcode)) + //vmRunner.executeCommand(sanitiseJS(gpuTestPaletteJs)) + vmRunner.executeCommand(sanitiseJS(prg)) } diff --git a/src/net/torvald/tsvm/VMJSR223Delegate.kt b/src/net/torvald/tsvm/VMJSR223Delegate.kt index f51308e..2eac43b 100644 --- a/src/net/torvald/tsvm/VMJSR223Delegate.kt +++ b/src/net/torvald/tsvm/VMJSR223Delegate.kt @@ -52,5 +52,5 @@ class VMJSR223Delegate(val vm: VM) { } class VMSerialDebugger(val vm: VM) { - fun print(s: String) = System.out.println(s) + fun println(s: String) = System.out.println(s) } \ No newline at end of file diff --git a/src/net/torvald/tsvm/VMRunnerFactory.kt b/src/net/torvald/tsvm/VMRunnerFactory.kt index 2f1e237..7d3d92e 100644 --- a/src/net/torvald/tsvm/VMRunnerFactory.kt +++ b/src/net/torvald/tsvm/VMRunnerFactory.kt @@ -72,13 +72,16 @@ object VMRunnerFactory { private val JS_INIT = """ function print(s) { - return vm.print(s) + vm.print(s); } function println(s) { - return vm.println(s) + if (typeof s == "undefined") + vm.print("\n"); + else + vm.println(s); } function read() { - return vm.read() + return vm.read(); } """ } diff --git a/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt b/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt index 1aa3d9f..23b9520 100644 --- a/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt +++ b/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt @@ -1,18 +1,18 @@ package net.torvald.tsvm.peripheral +import com.badlogic.gdx.Gdx import com.badlogic.gdx.graphics.Color +import com.badlogic.gdx.graphics.GL20 import com.badlogic.gdx.graphics.Pixmap import com.badlogic.gdx.graphics.Texture import com.badlogic.gdx.graphics.g2d.SpriteBatch +import com.badlogic.gdx.graphics.glutils.FrameBuffer import net.torvald.UnsafeHelper import net.torvald.tsvm.AppLoader import net.torvald.tsvm.VM import net.torvald.tsvm.kB -import net.torvald.util.CircularArray -import sun.nio.ch.DirectBuffer import java.io.InputStream import java.io.OutputStream -import java.io.PrintStream import kotlin.experimental.and class GraphicsAdapter(val vm: VM, val lcdMode: Boolean = false) : GlassTty(Companion.TEXT_ROWS, Companion.TEXT_COLS), PeriBase { @@ -56,6 +56,8 @@ class GraphicsAdapter(val vm: VM, val lcdMode: Boolean = false) : GlassTty(Compa private var textBackTex = Texture(textBackPixmap) private var textTex = Texture(textPixmap) + private val outFBOs = Array(2) { FrameBuffer(Pixmap.Format.RGBA8888, WIDTH, HEIGHT, false) } + private val memTextCursorPosOffset = 2978L private val memTextForeOffset = 2980L private val memTextBackOffset = 2980L + 2560 @@ -273,7 +275,8 @@ class GraphicsAdapter(val vm: VM, val lcdMode: Boolean = false) : GlassTty(Compa override fun eraseInLine(arg: Int) { when (arg) { else -> TODO() - } } + } + } /** New lines are added at the bottom */ override fun scrollUp(arg: Int) { @@ -451,6 +454,7 @@ class GraphicsAdapter(val vm: VM, val lcdMode: Boolean = false) : GlassTty(Compa paletteShader.dispose() textShader.dispose() faketex.dispose() + outFBOs.forEach { it.dispose() } try { textForeTex.dispose() } catch (_: Throwable) {} try { textBackTex.dispose() } catch (_: Throwable) {} @@ -461,121 +465,159 @@ class GraphicsAdapter(val vm: VM, val lcdMode: Boolean = false) : GlassTty(Compa private var textCursorBlinkTimer = 0f private val textCursorBlinkInterval = 0.5f private var textCursorIsOn = true + private var glowDecay = if (lcdMode) 0.69f else 0.25f + private var decayColor = Color(1f, 1f, 1f, 1f - glowDecay) fun render(delta: Float, batch: SpriteBatch, x: Float, y: Float) { rendertex.dispose() rendertex = Texture(framebuffer, Pixmap.Format.RGBA8888, false) - - batch.shader = null - batch.begin() - - // clear screen - batch.color = if (lcdMode) LCD_BASE_COL else Color.BLACK - batch.draw(faketex, 0f, 0f, WIDTH.toFloat(), HEIGHT.toFloat()) - - - // initiialise draw - batch.color = Color.WHITE - batch.shader = paletteShader - - // feed palette data - // must be done every time the shader is "actually loaded" - // try this: if above line precedes 'batch.shader = paletteShader', it won't work - batch.shader.setUniform4fv("pal", paletteOfFloats, 0, paletteOfFloats.size) - if (lcdMode) batch.shader.setUniformf("lcdBaseCol", LCD_BASE_COL) - - // draw framebuffer - batch.draw(rendertex, x, y) - - // draw texts or sprites - - batch.color = Color.WHITE - - if (!graphicsUseSprites) { - // draw texts - val (cx, cy) = getCursorPos() - - // prepare char buffer texture - for (y in 0 until TEXT_ROWS) { - for (x in 0 until TEXT_COLS) { - val drawCursor = textCursorIsOn && cx == x && cy == y - val addr = y.toLong() * TEXT_COLS + x - val char = if (drawCursor) 0xDB else spriteAndTextArea[memTextOffset + addr].toInt().and(255) - val back = if (drawCursor) ttyBack else spriteAndTextArea[memTextBackOffset + addr].toInt().and(255) - val fore = if (drawCursor) ttyFore else spriteAndTextArea[memTextForeOffset + addr].toInt().and(255) - - textPixmap.setColor(Color(0f, 0f, char / 255f, 1f)) - textPixmap.drawPixel(x, y) - textBackPixmap.setColor(Color(paletteOfFloats[4 * back], paletteOfFloats[4 * back + 1], paletteOfFloats[4 * back + 2], paletteOfFloats[4 * back + 3])) - textBackPixmap.drawPixel(x, y) - textForePixmap.setColor(Color(paletteOfFloats[4 * fore], paletteOfFloats[4 * fore + 1], paletteOfFloats[4 * fore + 2], paletteOfFloats[4 * fore + 3])) - textForePixmap.drawPixel(x, y) - } + outFBOs[1].inUse { + batch.shader = null + batch.inUse { + batch.color = decayColor + batch.draw(outFBOs[0].colorBufferTexture, 0f, HEIGHT.toFloat(), WIDTH.toFloat(), -HEIGHT.toFloat()) } + } - // bake char buffer texture - textForeTex.dispose() - textBackTex.dispose() - textTex.dispose() - textForeTex = Texture(textForePixmap) - textBackTex = Texture(textBackPixmap) - textTex = Texture(textPixmap) - textForeTex.bind(4) - textBackTex.bind(3) - textTex.bind(2) - chrrom0.bind(1) - faketex.bind(0) + outFBOs[0].inUse { + Gdx.gl.glClearColor(0f, 0f, 0f, 0f) + Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) - batch.shader = textShader - textShader.setUniformi("foreColours", 4) - textShader.setUniformi("backColours", 3) - textShader.setUniformi("tilemap", 2) - textShader.setUniformi("tilesAtlas", 1) - textShader.setUniformi("u_texture", 0) - textShader.setUniformf("tilesInAxes", TEXT_COLS.toFloat(), TEXT_ROWS.toFloat()) - textShader.setUniformf("screenDimension", WIDTH.toFloat(), HEIGHT.toFloat()) - textShader.setUniformf("tilesInAtlas", 16f, 16f) - textShader.setUniformf("atlasTexSize", chrrom0.width.toFloat(), chrrom0.height.toFloat()) - if (lcdMode) batch.shader.setUniformf("lcdBaseCol", LCD_BASE_COL) + batch.shader = null + batch.inUse { - batch.draw(faketex, 0f, 0f, WIDTH.toFloat(), HEIGHT.toFloat()) + // clear screen + batch.color = if (lcdMode) LCD_BASE_COL else Color.BLACK + batch.draw(faketex, 0f, 0f, WIDTH.toFloat(), HEIGHT.toFloat()) + + + // initiialise draw + batch.color = Color.WHITE + batch.shader = paletteShader + + // feed palette data + // must be done every time the shader is "actually loaded" + // try this: if above line precedes 'batch.shader = paletteShader', it won't work + batch.shader.setUniform4fv("pal", paletteOfFloats, 0, paletteOfFloats.size) + if (lcdMode) batch.shader.setUniformf("lcdBaseCol", LCD_BASE_COL) + + // draw framebuffer + batch.draw(rendertex, x, y) + + // draw texts or sprites + + batch.color = Color.WHITE + + if (!graphicsUseSprites) { + // draw texts + val (cx, cy) = getCursorPos() + + // prepare char buffer texture + for (y in 0 until TEXT_ROWS) { + for (x in 0 until TEXT_COLS) { + val drawCursor = textCursorIsOn && cx == x && cy == y + val addr = y.toLong() * TEXT_COLS + x + val char = + if (drawCursor) 0xDB else spriteAndTextArea[memTextOffset + addr].toInt().and(255) + val back = + if (drawCursor) ttyBack else spriteAndTextArea[memTextBackOffset + addr].toInt() + .and(255) + val fore = + if (drawCursor) ttyFore else spriteAndTextArea[memTextForeOffset + addr].toInt() + .and(255) + + textPixmap.setColor(Color(0f, 0f, char / 255f, 1f)) + textPixmap.drawPixel(x, y) + textBackPixmap.setColor( + Color( + paletteOfFloats[4 * back], + paletteOfFloats[4 * back + 1], + paletteOfFloats[4 * back + 2], + paletteOfFloats[4 * back + 3] + ) + ) + textBackPixmap.drawPixel(x, y) + textForePixmap.setColor( + Color( + paletteOfFloats[4 * fore], + paletteOfFloats[4 * fore + 1], + paletteOfFloats[4 * fore + 2], + paletteOfFloats[4 * fore + 3] + ) + ) + textForePixmap.drawPixel(x, y) + } + } + + // bake char buffer texture + textForeTex.dispose() + textBackTex.dispose() + textTex.dispose() + textForeTex = Texture(textForePixmap) + textBackTex = Texture(textBackPixmap) + textTex = Texture(textPixmap) + + textForeTex.bind(4) + textBackTex.bind(3) + textTex.bind(2) + chrrom0.bind(1) + faketex.bind(0) + + batch.shader = textShader + textShader.setUniformi("foreColours", 4) + textShader.setUniformi("backColours", 3) + textShader.setUniformi("tilemap", 2) + textShader.setUniformi("tilesAtlas", 1) + textShader.setUniformi("u_texture", 0) + textShader.setUniformf("tilesInAxes", TEXT_COLS.toFloat(), TEXT_ROWS.toFloat()) + textShader.setUniformf("screenDimension", WIDTH.toFloat(), HEIGHT.toFloat()) + textShader.setUniformf("tilesInAtlas", 16f, 16f) + textShader.setUniformf("atlasTexSize", chrrom0.width.toFloat(), chrrom0.height.toFloat()) + if (lcdMode) batch.shader.setUniformf("lcdBaseCol", LCD_BASE_COL) + + batch.draw(faketex, 0f, 0f, WIDTH.toFloat(), HEIGHT.toFloat()) + + batch.shader = null + } else { + // draw sprites + batch.shader = paletteShader + + // feed palette data + // must be done every time the shader is "actually loaded" + // try this: if above line precedes 'batch.shader = paletteShader', it won't work + paletteShader.setUniform4fv("pal", paletteOfFloats, 0, paletteOfFloats.size) + TODO("sprite draw") + } + + } batch.shader = null - /*if (textCursorIsOn) { - batch.color = Color( - paletteOfFloats[4 * ttyFore], - paletteOfFloats[4 * ttyFore + 1], - paletteOfFloats[4 * ttyFore + 2], - paletteOfFloats[4 * ttyFore + 3] - ) - val (cursorx, cursory) = getCursorPos() - batch.draw(faketex, cursorx * chrWidth, (TEXT_ROWS - cursory - 1) * chrHeight, chrWidth, chrHeight) - }*/ - } - else { - // draw sprites - batch.shader = paletteShader - - // feed palette data - // must be done every time the shader is "actually loaded" - // try this: if above line precedes 'batch.shader = paletteShader', it won't work - paletteShader.setUniform4fv("pal", paletteOfFloats, 0, paletteOfFloats.size) - TODO("sprite draw") } - - batch.end() + outFBOs[1].inUse { + batch.shader = null + batch.inUse { + batch.color = decayColor + batch.draw(outFBOs[0].colorBufferTexture, 0f, HEIGHT.toFloat(), WIDTH.toFloat(), -HEIGHT.toFloat()) + } + } batch.shader = null + batch.inUse { + batch.color = Color.WHITE + batch.draw(outFBOs[1].colorBufferTexture, 0f, HEIGHT.toFloat(), WIDTH.toFloat(), -HEIGHT.toFloat()) + } + textCursorBlinkTimer += delta if (textCursorBlinkTimer > textCursorBlinkInterval) { textCursorBlinkTimer -= 0.5f textCursorIsOn = !textCursorIsOn } + } private fun peekPalette(offset: Int): Byte { @@ -1128,4 +1170,16 @@ void main() { 0 ) } + + private fun FrameBuffer.inUse(action: () -> Unit) { + this.begin() + action() + this.end() + } + + private fun SpriteBatch.inUse(action: () -> Unit) { + this.begin() + action() + this.end() + } } \ No newline at end of file diff --git a/src/net/torvald/tsvm/peripheral/IOSpace.kt b/src/net/torvald/tsvm/peripheral/IOSpace.kt index d493168..c088d8d 100644 --- a/src/net/torvald/tsvm/peripheral/IOSpace.kt +++ b/src/net/torvald/tsvm/peripheral/IOSpace.kt @@ -41,6 +41,7 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor { 36L -> if (mouseDown) 1 else 0 37L -> keyboardBuffer.removeTail() ?: -1 38L -> if (keyboardInputRequested) 1 else 0 + in 64..67 -> vm.memsize.shr((adi - 64) * 8).toByte() else -> -1 } } diff --git a/terranmon.txt b/terranmon.txt index c780046..b63f882 100644 --- a/terranmon.txt +++ b/terranmon.txt @@ -21,14 +21,14 @@ User area: 8 MB, hardware area: 8 MB 1024 kB Peripheral #7 ... -1024 kB +1024 kB (where Peripheral #0 would be) MMIO and Interrupt Vectors 128 kB MMIO for Peri #8 128 kB MMIO for Peri #7 ... - 128 kB + 128 kB (where Peripheral #0 would be) MMIO for the computer 130816 bytes MMIO for Ports, etc. @@ -40,6 +40,7 @@ User area: 8 MB, hardware area: 8 MB IO Device Endianness: little +Note: Always takes up the peripheral slot of zero MMIO @@ -54,6 +55,8 @@ MMIO close it. Keyboard buffer will be cleared whenever request is received, so MAKE SURE YOU REQUEST THE KEY INPUT ONLY ONCE! +64..67 RO: User area memory size in bytes + -------------------------------------------------------------------------------- @@ -102,25 +105,25 @@ FI MMIO -2 bytes RO +0..1 RO Framebuffer width in pixels -2 bytes RO +2..3 RO Framebuffer height in pixels -1 bytes RO +4 RO Text mode columns -1 bytes RO +5 RO Text mode rows -1 bytes RW +6 RW Text-mode attributes 0b kkkk 00rc (k: currently using character rom, r: TTY Raw mode, c: Cursor blink) -1 bytes RW +7 RW Graphics-mode attributes 0b 0000 000g (g: Use sprites(wipes out text buffer)) -1 bytes RO +8 RO Last used colour (set by poking at the framebuffer) -1 bytes RW +9 RW current TTY foreground colour (useful for print() function) -1 bytes RW +10 RW current TTY background colour (useful for print() function) Text-mode-font-ROM is immutable and does not belong to VRAM