impl of screen ghosting

This commit is contained in:
minjaesong
2020-05-16 09:06:17 +09:00
parent 772354d2d1
commit 57c16bcb44
8 changed files with 217 additions and 113 deletions

30
assets/tvdos/command.js Normal file
View File

@@ -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 + "@");
}

View File

@@ -78,6 +78,8 @@ class VM(
var getErrorStream: () -> OutputStream = { TODO() } var getErrorStream: () -> OutputStream = { TODO() }
var getInputStream: () -> InputStream = { TODO() } var getInputStream: () -> InputStream = { TODO() }
val startTime: Long
init { init {
peripheralTable[0] = PeripheralEntry( peripheralTable[0] = PeripheralEntry(
"io", "io",
@@ -88,6 +90,8 @@ class VM(
) )
println("[VM] Creating new VM with ID of $id, memesize $memsize") 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() } peripheralTable.forEach { it.peripheral?.dispose() }
} }
open fun getUptime() = System.nanoTime() - startTime
/* /*
NOTE: re-fill peripheralTable whenever the VM cold-boots! NOTE: re-fill peripheralTable whenever the VM cold-boots!
you are absolutely not supposed to hot-swap peripheral cards when the computer is on you are absolutely not supposed to hot-swap peripheral cards when the computer is on

View File

@@ -9,8 +9,10 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
import kotlinx.coroutines.* import kotlinx.coroutines.*
import net.torvald.tsvm.peripheral.GraphicsAdapter import net.torvald.tsvm.peripheral.GraphicsAdapter
import net.torvald.tsvm.peripheral.IOSpace import net.torvald.tsvm.peripheral.IOSpace
import java.io.FileReader
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
import java.io.StringReader
class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter() { class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter() {
@@ -27,7 +29,7 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter()
override fun create() { override fun create() {
super.create() super.create()
gpu = GraphicsAdapter(vm, lcdMode = false) gpu = GraphicsAdapter(vm, lcdMode = true)
vm.peripheralTable[1] = PeripheralEntry( vm.peripheralTable[1] = PeripheralEntry(
VM.PERITYPE_TERM, VM.PERITYPE_TERM,
@@ -49,9 +51,14 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter()
vm.getInputStream = { gpu.getInputStream() } vm.getInputStream = { gpu.getInputStream() }
// TEST PRG // TEST PRG
val fr = FileReader("./assets/tvdos/command.js")
val prg = fr.readText()
fr.close()
vmRunner = VMRunnerFactory(vm, "js") vmRunner = VMRunnerFactory(vm, "js")
coroutineJob = GlobalScope.launch { coroutineJob = GlobalScope.launch {
vmRunner.executeCommand(sanitiseJS(shitcode)) //vmRunner.executeCommand(sanitiseJS(gpuTestPaletteJs))
vmRunner.executeCommand(sanitiseJS(prg))
} }

View File

@@ -52,5 +52,5 @@ class VMJSR223Delegate(val vm: VM) {
} }
class VMSerialDebugger(val vm: VM) { class VMSerialDebugger(val vm: VM) {
fun print(s: String) = System.out.println(s) fun println(s: String) = System.out.println(s)
} }

View File

@@ -72,13 +72,16 @@ object VMRunnerFactory {
private val JS_INIT = """ private val JS_INIT = """
function print(s) { function print(s) {
return vm.print(s) vm.print(s);
} }
function println(s) { function println(s) {
return vm.println(s) if (typeof s == "undefined")
vm.print("\n");
else
vm.println(s);
} }
function read() { function read() {
return vm.read() return vm.read();
} }
""" """
} }

View File

@@ -1,18 +1,18 @@
package net.torvald.tsvm.peripheral package net.torvald.tsvm.peripheral
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.GL20
import com.badlogic.gdx.graphics.Pixmap import com.badlogic.gdx.graphics.Pixmap
import com.badlogic.gdx.graphics.Texture import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.glutils.FrameBuffer
import net.torvald.UnsafeHelper import net.torvald.UnsafeHelper
import net.torvald.tsvm.AppLoader import net.torvald.tsvm.AppLoader
import net.torvald.tsvm.VM import net.torvald.tsvm.VM
import net.torvald.tsvm.kB import net.torvald.tsvm.kB
import net.torvald.util.CircularArray
import sun.nio.ch.DirectBuffer
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
import java.io.PrintStream
import kotlin.experimental.and import kotlin.experimental.and
class GraphicsAdapter(val vm: VM, val lcdMode: Boolean = false) : GlassTty(Companion.TEXT_ROWS, Companion.TEXT_COLS), PeriBase { 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 textBackTex = Texture(textBackPixmap)
private var textTex = Texture(textPixmap) private var textTex = Texture(textPixmap)
private val outFBOs = Array(2) { FrameBuffer(Pixmap.Format.RGBA8888, WIDTH, HEIGHT, false) }
private val memTextCursorPosOffset = 2978L private val memTextCursorPosOffset = 2978L
private val memTextForeOffset = 2980L private val memTextForeOffset = 2980L
private val memTextBackOffset = 2980L + 2560 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) { override fun eraseInLine(arg: Int) {
when (arg) { when (arg) {
else -> TODO() else -> TODO()
} } }
}
/** New lines are added at the bottom */ /** New lines are added at the bottom */
override fun scrollUp(arg: Int) { override fun scrollUp(arg: Int) {
@@ -451,6 +454,7 @@ class GraphicsAdapter(val vm: VM, val lcdMode: Boolean = false) : GlassTty(Compa
paletteShader.dispose() paletteShader.dispose()
textShader.dispose() textShader.dispose()
faketex.dispose() faketex.dispose()
outFBOs.forEach { it.dispose() }
try { textForeTex.dispose() } catch (_: Throwable) {} try { textForeTex.dispose() } catch (_: Throwable) {}
try { textBackTex.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 var textCursorBlinkTimer = 0f
private val textCursorBlinkInterval = 0.5f private val textCursorBlinkInterval = 0.5f
private var textCursorIsOn = true 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) { fun render(delta: Float, batch: SpriteBatch, x: Float, y: Float) {
rendertex.dispose() rendertex.dispose()
rendertex = Texture(framebuffer, Pixmap.Format.RGBA8888, false) rendertex = Texture(framebuffer, Pixmap.Format.RGBA8888, false)
outFBOs[1].inUse {
batch.shader = null batch.shader = null
batch.begin() batch.inUse {
batch.color = decayColor
// clear screen batch.draw(outFBOs[0].colorBufferTexture, 0f, HEIGHT.toFloat(), WIDTH.toFloat(), -HEIGHT.toFloat())
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) outFBOs[0].inUse {
textBackTex.bind(3) Gdx.gl.glClearColor(0f, 0f, 0f, 0f)
textTex.bind(2) Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
chrrom0.bind(1)
faketex.bind(0)
batch.shader = textShader batch.shader = null
textShader.setUniformi("foreColours", 4) batch.inUse {
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()) // 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 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")
} }
outFBOs[1].inUse {
batch.end() batch.shader = null
batch.inUse {
batch.color = decayColor
batch.draw(outFBOs[0].colorBufferTexture, 0f, HEIGHT.toFloat(), WIDTH.toFloat(), -HEIGHT.toFloat())
}
}
batch.shader = null batch.shader = null
batch.inUse {
batch.color = Color.WHITE
batch.draw(outFBOs[1].colorBufferTexture, 0f, HEIGHT.toFloat(), WIDTH.toFloat(), -HEIGHT.toFloat())
}
textCursorBlinkTimer += delta textCursorBlinkTimer += delta
if (textCursorBlinkTimer > textCursorBlinkInterval) { if (textCursorBlinkTimer > textCursorBlinkInterval) {
textCursorBlinkTimer -= 0.5f textCursorBlinkTimer -= 0.5f
textCursorIsOn = !textCursorIsOn textCursorIsOn = !textCursorIsOn
} }
} }
private fun peekPalette(offset: Int): Byte { private fun peekPalette(offset: Int): Byte {
@@ -1128,4 +1170,16 @@ void main() {
0 0
) )
} }
private fun FrameBuffer.inUse(action: () -> Unit) {
this.begin()
action()
this.end()
}
private fun SpriteBatch.inUse(action: () -> Unit) {
this.begin()
action()
this.end()
}
} }

View File

@@ -41,6 +41,7 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor {
36L -> if (mouseDown) 1 else 0 36L -> if (mouseDown) 1 else 0
37L -> keyboardBuffer.removeTail() ?: -1 37L -> keyboardBuffer.removeTail() ?: -1
38L -> if (keyboardInputRequested) 1 else 0 38L -> if (keyboardInputRequested) 1 else 0
in 64..67 -> vm.memsize.shr((adi - 64) * 8).toByte()
else -> -1 else -> -1
} }
} }

View File

@@ -21,14 +21,14 @@ User area: 8 MB, hardware area: 8 MB
1024 kB 1024 kB
Peripheral #7 Peripheral #7
... ...
1024 kB 1024 kB (where Peripheral #0 would be)
MMIO and Interrupt Vectors MMIO and Interrupt Vectors
128 kB 128 kB
MMIO for Peri #8 MMIO for Peri #8
128 kB 128 kB
MMIO for Peri #7 MMIO for Peri #7
... ...
128 kB 128 kB (where Peripheral #0 would be)
MMIO for the computer MMIO for the computer
130816 bytes 130816 bytes
MMIO for Ports, etc. MMIO for Ports, etc.
@@ -40,6 +40,7 @@ User area: 8 MB, hardware area: 8 MB
IO Device IO Device
Endianness: little Endianness: little
Note: Always takes up the peripheral slot of zero
MMIO MMIO
@@ -54,6 +55,8 @@ MMIO
close it. Keyboard buffer will be cleared whenever request is received, so close it. Keyboard buffer will be cleared whenever request is received, so
MAKE SURE YOU REQUEST THE KEY INPUT ONLY ONCE! MAKE SURE YOU REQUEST THE KEY INPUT ONLY ONCE!
64..67 RO: User area memory size in bytes
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@@ -102,25 +105,25 @@ FI
MMIO MMIO
2 bytes RO 0..1 RO
Framebuffer width in pixels Framebuffer width in pixels
2 bytes RO 2..3 RO
Framebuffer height in pixels Framebuffer height in pixels
1 bytes RO 4 RO
Text mode columns Text mode columns
1 bytes RO 5 RO
Text mode rows Text mode rows
1 bytes RW 6 RW
Text-mode attributes Text-mode attributes
0b kkkk 00rc (k: currently using character rom, r: TTY Raw mode, c: Cursor blink) 0b kkkk 00rc (k: currently using character rom, r: TTY Raw mode, c: Cursor blink)
1 bytes RW 7 RW
Graphics-mode attributes Graphics-mode attributes
0b 0000 000g (g: Use sprites(wipes out text buffer)) 0b 0000 000g (g: Use sprites(wipes out text buffer))
1 bytes RO 8 RO
Last used colour (set by poking at the framebuffer) Last used colour (set by poking at the framebuffer)
1 bytes RW 9 RW
current TTY foreground colour (useful for print() function) current TTY foreground colour (useful for print() function)
1 bytes RW 10 RW
current TTY background colour (useful for print() function) current TTY background colour (useful for print() function)
Text-mode-font-ROM is immutable and does not belong to VRAM Text-mode-font-ROM is immutable and does not belong to VRAM