mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 19:51:51 +09:00
impl of screen ghosting
This commit is contained in:
30
assets/tvdos/command.js
Normal file
30
assets/tvdos/command.js
Normal 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 + "@");
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user