partially implemented SGR seq; DECTCEM cursor show/hide

This commit is contained in:
minjaesong
2020-05-30 04:31:44 +09:00
parent 2bc44d905c
commit 790d5807e3
15 changed files with 1408 additions and 50 deletions

View File

@@ -29,7 +29,7 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter()
override fun create() {
super.create()
gpu = GraphicsAdapter(vm, lcdMode = true)
gpu = GraphicsAdapter(vm, lcdMode = false)
vm.peripheralTable[1] = PeripheralEntry(
VM.PERITYPE_TERM,
@@ -53,13 +53,20 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter()
// TEST PRG
//val fr = FileReader("./assets/tvdos/command.js")
//val fr = FileReader("./assets/jscon.js")
//val fr1 = FileReader("./assets/bios1.js")
val fr1 = FileReader("./assets/phototest.js")
val bios = fr1.readText()
fr1.close()
//val fr = FileReader("./assets/tvdos/command.js")
val fr = FileReader("./assets/tbas/basic.js")
val prg = fr.readText()
fr.close()
vmRunner = VMRunnerFactory(vm, "js")
coroutineJob = GlobalScope.launch {
//vmRunner.executeCommand(sanitiseJS(gpuTestPaletteJs))
vmRunner.executeCommand(sanitiseJS(bios))
vmRunner.executeCommand(sanitiseJS(prg))
}

View File

@@ -65,7 +65,6 @@ object VMRunnerFactory {
}
override suspend fun executeCommand(command: String) {
//(engine as Compilable).compile(command).eval(context) // compiling does not work with bindings in kts
engine.eval("\"use strict\";{$command}", context)
}
}

View File

@@ -18,7 +18,10 @@ abstract class BlockTransferInterface(val isMaster: Boolean, val isSlave: Boolea
open fun areYouReady(): Boolean = recipient?.ready ?: false
open fun areYouBusy(): Boolean = recipient?.busy ?: false
open fun startSend(sendfun: ((BlockTransferInterface) -> Unit)? = null) {
/** A method exposed to outside of the box */
abstract fun startSend()
/** The actual implementation */
protected fun startSend(sendfun: ((BlockTransferInterface) -> Unit)? = null) {
if (areYouReady()) {
busy = true
ready = false
@@ -36,8 +39,10 @@ abstract class BlockTransferInterface(val isMaster: Boolean, val isSlave: Boolea
recipient?.startSend(null)
}
/** must be called by a sender class */
open fun writeout(inputData: ByteArray, writeoutfun: (() -> Unit)? = null) {
/** A method exposed to outside of the box */
abstract fun writeout(inputData: ByteArray)
/** The actual implementation; must be called by a sender class */
protected fun writeout(inputData: ByteArray, writeoutfun: (() -> Unit)? = null) {
busy = true
ready = false
blockSize = minOf(inputData.size, BLOCK_SIZE)

View File

@@ -10,8 +10,8 @@ internal class BlockTransferPort(val vm: VM, val portno: Int) : BlockTransferInt
internal var hasNext = false
override fun startSend(sendfun: ((BlockTransferInterface) -> Unit)?) {
super.startSend { recipient ->
override fun startSend() {
startSend { recipient ->
val ba = ByteArray(BLOCK_SIZE) { vm.getIO().blockTransferRx[portno][it.toLong()] }
recipient.writeout(ba)
}
@@ -19,8 +19,8 @@ internal class BlockTransferPort(val vm: VM, val portno: Int) : BlockTransferInt
override fun hasNext(): Boolean = hasNext
override fun writeout(inputData: ByteArray, writeoutfun: (() -> Unit)?) {
super.writeout(inputData) {
override fun writeout(inputData: ByteArray) {
writeout(inputData) {
val copySize = minOf(BLOCK_SIZE, inputData.size).toLong()
val arrayOffset = UnsafeHelper.getArrayOffset(inputData).toLong()
UnsafeHelper.memcpyRaw(inputData, arrayOffset, null, vm.getIO().blockTransferRx[portno].ptr, copySize)

View File

@@ -116,6 +116,7 @@ abstract class GlassTty(val TEXT_ROWS: Int, val TEXT_COLS: Int) {
'S' -> return accept { scrollUp() }
'T' -> return accept { scrollDown() }
'm' -> return accept { sgrOneArg() }
'?' -> ttyEscState = TTY_ESC_STATE.PRIVATESEQ
';' -> {
ttyEscArguments.push(0)
ttyEscState = TTY_ESC_STATE.SEP1
@@ -124,6 +125,20 @@ abstract class GlassTty(val TEXT_ROWS: Int, val TEXT_COLS: Int) {
else -> return reject()
}
}
TTY_ESC_STATE.PRIVATESEQ -> {
when (char.toChar()) {
in '0'..'9' -> registerNewNumberArg(char, TTY_ESC_STATE.PRIVATENUM)
else -> return reject()
}
}
TTY_ESC_STATE.PRIVATENUM -> {
when (char.toChar()) {
'h' -> return accept { privateSeqH(ttyEscArguments.pop()) }
'l' -> return accept { privateSeqL(ttyEscArguments.pop()) }
in '0'..'9' -> appendToExistingNumber(char)
else -> return reject()
}
}
TTY_ESC_STATE.NUM1 -> {
when (char.toChar()) {
'A' -> return accept { cursorUp(ttyEscArguments.pop()) }
@@ -231,6 +246,8 @@ abstract class GlassTty(val TEXT_ROWS: Int, val TEXT_COLS: Int) {
abstract fun insertTab()
abstract fun crlf()
abstract fun backspace()
abstract fun privateSeqH(arg: Int)
abstract fun privateSeqL(arg: Int)
abstract fun getPrintStream(): OutputStream
abstract fun getErrorStream(): OutputStream
@@ -244,7 +261,7 @@ abstract class GlassTty(val TEXT_ROWS: Int, val TEXT_COLS: Int) {
private val ESC = 0x1B.toByte()
private enum class TTY_ESC_STATE {
INITIAL, ESC, CSI, NUM1, SEP1, NUM2, SEP2, NUM3
INITIAL, ESC, CSI, NUM1, SEP1, NUM2, SEP2, NUM3, PRIVATESEQ, PRIVATENUM
}
@@ -283,6 +300,13 @@ digraph G {
CSI -> SGR [label="m"]
CSI -> separator1 [label="; (zero)"]
CSI -> privateseq [label="?"]
privateseq -> privatenum [label="0..9"]
privatenum -> privateSeqH [label=h]
privatenum -> privateSeqL [label=l]
numeral -> numeral [label="0..9"]
numeral -> CursorUp [label="A"]
numeral -> CursorDown [label="B"]

View File

@@ -15,7 +15,7 @@ import java.io.InputStream
import java.io.OutputStream
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, lcdInvert: Boolean = true) : GlassTty(Companion.TEXT_ROWS, Companion.TEXT_COLS), PeriBase {
override fun getVM(): VM {
return vm
@@ -34,8 +34,8 @@ class GraphicsAdapter(val vm: VM, val lcdMode: Boolean = false) : GlassTty(Compa
internal val spriteAndTextArea = UnsafeHelper.allocate(10660L)
private val unusedArea = ByteArray(92)
private val paletteShader = AppLoader.loadShaderInline(DRAW_SHADER_VERT, if (lcdMode) DRAW_SHADER_FRAG_LCD else DRAW_SHADER_FRAG)
private val textShader = AppLoader.loadShaderInline(DRAW_SHADER_VERT, if (lcdMode) TEXT_TILING_SHADER_LCD else TEXT_TILING_SHADER)
private val paletteShader = AppLoader.loadShaderInline(DRAW_SHADER_VERT, if (lcdMode && !lcdInvert) DRAW_SHADER_FRAG_LCD_NOINV else if (lcdMode) DRAW_SHADER_FRAG_LCD else DRAW_SHADER_FRAG)
private val textShader = AppLoader.loadShaderInline(DRAW_SHADER_VERT, if (lcdMode && !lcdInvert) TEXT_TILING_SHADER_LCD_NOINV else if (lcdMode) TEXT_TILING_SHADER_LCD else TEXT_TILING_SHADER)
override var blinkCursor = true
override var ttyRawMode = false
@@ -111,6 +111,10 @@ class GraphicsAdapter(val vm: VM, val lcdMode: Boolean = false) : GlassTty(Compa
// -1 is preferred because it points to the colour CLEAR, and it's constant.
spriteAndTextArea.fillWith(-1)
unusedArea[0] = 0
unusedArea[1] = 2
unusedArea[2] = 14
setCursorPos(0, 0)
println(framebuffer.pixels.limit())
@@ -331,28 +335,57 @@ class GraphicsAdapter(val vm: VM, val lcdMode: Boolean = false) : GlassTty(Compa
}
}
/*
Color table for default palette
Black 240
Red 160
Green 30
Yellow 230
Blue 19
Magenta 199
Cyan 74
White 254
*/
private val sgrDefault8ColPal = intArrayOf(240,160,30,230,19,199,74,254)
override fun sgrOneArg(arg: Int) {
TODO("Not yet implemented")
if (arg in 30..37) {
ttyFore = sgrDefault8ColPal[arg - 30]
}
else if (arg in 40..47) {
ttyBack = sgrDefault8ColPal[arg - 40]
}
else if (arg == 0) {
ttyFore = TTY_FORE_DEFAULT
ttyBack = TTY_BACK_DEFAULT
}
}
override fun sgrTwoArg(arg1: Int, arg2: Int) {
/*
Color table for default palette
Black 240
Red 160
Green 30
Yellow 230
Blue 19
Magenta 199
Cyan 74
White 254
*/
TODO("Not yet implemented")
}
override fun sgrThreeArg(arg1: Int, arg2: Int, arg3: Int) {
TODO("Not yet implemented")
if (arg1 == 38 && arg2 == 5) {
ttyFore = arg3
}
else if (arg1 == 48 && arg2 == 5) {
ttyBack = arg3
}
}
override fun privateSeqH(arg: Int) {
when (arg) {
25 -> blinkCursor = true
}
}
override fun privateSeqL(arg: Int) {
when (arg) {
25 -> blinkCursor = false
}
}
/** The values are one-based
@@ -497,6 +530,9 @@ class GraphicsAdapter(val vm: VM, val lcdMode: Boolean = false) : GlassTty(Compa
outFBOs[0].inUse {
val clearCol = Color(unusedArea[0].toInt().and(15).toFloat() / 15f,
unusedArea[1].toInt().and(15).toFloat() / 15f,
unusedArea[2].toInt().and(15).toFloat() / 15f, 1f)
Gdx.gl.glClearColor(0f, 0f, 0f, 0f)
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
@@ -504,7 +540,7 @@ class GraphicsAdapter(val vm: VM, val lcdMode: Boolean = false) : GlassTty(Compa
batch.inUse {
// clear screen
batch.color = if (lcdMode) LCD_BASE_COL else Color.BLACK
batch.color = if (lcdMode) LCD_BASE_COL else clearCol
batch.draw(faketex, 0f, 0f, WIDTH.toFloat(), HEIGHT.toFloat())
@@ -532,7 +568,7 @@ class GraphicsAdapter(val vm: VM, val lcdMode: Boolean = false) : GlassTty(Compa
// 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 drawCursor = blinkCursor && textCursorIsOn && cx == x && cy == y
val addr = y.toLong() * TEXT_COLS + x
val char =
if (drawCursor) 0xDB else spriteAndTextArea[memTextOffset + addr].toInt().and(255)
@@ -709,15 +745,32 @@ uniform vec4 pal[256];
float intensitySteps = 4.0;
uniform vec4 lcdBaseCol;
float rand(vec2 co){
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
void main(void) {
vec4 palCol = pal[int(texture2D(u_texture, v_texCoords).a * 255.0)];
float lum = floor((3.0 * palCol.r + 4.0 * palCol.g + palCol.b) / 8.0 * intensitySteps) / intensitySteps;
vec4 outIntensity = vec4(vec3(1.0 - lum), palCol.a);
// LCD output will invert the luminosity. That is, normally white colour will be black on PM-LCD.
gl_FragColor = lcdBaseCol * outIntensity;
}
""".trimIndent()
val DRAW_SHADER_FRAG_LCD_NOINV = """
#version 120
varying vec4 v_color;
varying vec2 v_texCoords;
uniform sampler2D u_texture;
uniform vec4 pal[256];
float intensitySteps = 4.0;
uniform vec4 lcdBaseCol;
void main(void) {
vec4 palCol = pal[int(texture2D(u_texture, v_texCoords).a * 255.0)];
float lum = floor((3.0 * palCol.r + 4.0 * palCol.g + palCol.b) / 8.0 * intensitySteps) / intensitySteps;
vec4 outIntensity = vec4(vec3(lum), palCol.a);
// LCD output will invert the luminosity. That is, normally white colour will be black on PM-LCD.
gl_FragColor = lcdBaseCol * outIntensity;
}
@@ -924,6 +977,103 @@ void main() {
// LCD output will invert the luminosity. That is, normally white colour will be black on PM-LCD.
gl_FragColor = lcdBaseCol * outIntensity;
}
""".trimIndent()
val TEXT_TILING_SHADER_LCD_NOINV = """
#version 120
#ifdef GL_ES
precision mediump float;
#endif
#extension GL_EXT_gpu_shader4 : enable
//layout(origin_upper_left) in vec4 gl_FragCoord; // commented; requires #version 150 or later
// gl_FragCoord is origin to bottom-left
varying vec4 v_color;
varying vec2 v_texCoords;
uniform sampler2D u_texture;
uniform vec2 screenDimension;
uniform vec2 tilesInAxes; // size of the tilemap texture; vec2(tiles_in_horizontal, tiles_in_vertical)
uniform sampler2D tilesAtlas;
uniform sampler2D foreColours;
uniform sampler2D backColours;
uniform sampler2D tilemap;
uniform vec2 tilesInAtlas = ivec2(16.0, 16.0);
uniform vec2 atlasTexSize = ivec2(128.0, 224.0);
vec2 tileSizeInPx = atlasTexSize / tilesInAtlas; // should be like ivec2(16, 16)
ivec2 getTileXY(int tileNumber) {
return ivec2(tileNumber % int(tilesInAtlas.x), tileNumber / int(tilesInAtlas.x));
}
// return: int=0xaarrggbb
int _colToInt(vec4 color) {
return int(color.b * 255) | (int(color.g * 255) << 8) | (int(color.r * 255) << 16) | (int(color.a * 255) << 24);
}
// 0x0rggbb where int=0xaarrggbb
// return: [0..1048575]
int getTileFromColor(vec4 color) {
return _colToInt(color) & 0xFFFFF;
}
float intensitySteps = 4.0;
uniform vec4 lcdBaseCol;
void main() {
// READ THE FUCKING MANUAL, YOU DONKEY !! //
// This code purposedly uses flipped fragcoord. //
// Make sure you don't use gl_FragCoord unknowingly! //
// Remember, if there's a compile error, shader SILENTLY won't do anything //
// default gl_FragCoord takes half-integer (represeting centre of the pixel) -- could be useful for phys solver?
// This one, however, takes exact integer by rounding down. //
vec2 flippedFragCoord = vec2(gl_FragCoord.x, screenDimension.y - gl_FragCoord.y); // NO IVEC2!!; this flips Y
// get required tile numbers //
vec4 tileFromMap = texture2D(tilemap, flippedFragCoord / screenDimension); // raw tile number
vec4 foreColFromMap = texture2D(foreColours, flippedFragCoord / screenDimension);
vec4 backColFromMap = texture2D(backColours, flippedFragCoord / screenDimension);
int tile = getTileFromColor(tileFromMap);
ivec2 tileXY = getTileXY(tile);
// calculate the UV coord value for texture sampling //
// don't really need highp here; read the GLES spec
vec2 uvCoordForTile = (mod(flippedFragCoord, tileSizeInPx) / tileSizeInPx) / tilesInAtlas; // 0..0.00390625 regardless of tile position in atlas
vec2 uvCoordOffsetTile = tileXY / tilesInAtlas; // where the tile starts in the atlas, using uv coord (0..1)
// get final UV coord for the actual sampling //
vec2 finalUVCoordForTile = uvCoordForTile + uvCoordOffsetTile;// where we should be actually looking for in atlas, using UV coord (0..1)
// blending a breakage tex with main tex //
vec4 tileCol = texture2D(tilesAtlas, finalUVCoordForTile);
vec4 palCol = vec4(1.0);
// apply colour
if (tileCol.r > 0) {
palCol = foreColFromMap;
}
else {
palCol = backColFromMap;
}
float lum = floor((3.0 * palCol.r + 4.0 * palCol.g + palCol.b) / 8.0 * intensitySteps) / intensitySteps;
vec4 outIntensity = vec4(vec3(lum), palCol.a);
// LCD output will invert the luminosity. That is, normally white colour will be black on PM-LCD.
gl_FragColor = lcdBaseCol * outIntensity;
}
""".trimIndent()
val DEFAULT_PALETTE = intArrayOf( // 0b rrrrrrrr gggggggg bbbbbbbb aaaaaaaa

View File

@@ -8,6 +8,14 @@ class SerialDiskDrive : BlockTransferInterface(false, true) {
TODO("Not yet implemented")
}
override fun startSend() {
TODO("Not yet implemented")
}
override fun writeout(inputData: ByteArray) {
TODO("Not yet implemented")
}
val diskID: UUID = UUID(0, 0)

View File

@@ -2,7 +2,7 @@ package net.torvald.tsvm.peripheral
class TestFunctionGenerator : BlockTransferInterface(true, false) {
val msg = """Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi ipsum magna, ultrices eu leo eu, consequat eleifend arcu. Nam tempor nunc aliquam mi cursus mollis. Aenean dictum iaculis dolor eget porttitor. Fusce vulputate dui id mauris ultricies, non aliquet nulla pulvinar. Integer consectetur nulla at cursus cursus. Nullam enim nisl, elementum a fermentum sed, suscipit id sapien. Duis eget enim lacinia, aliquam sapien ac, commodo risus. Morbi at enim sem. Aenean sollicitudin purus et sem porttitor, convallis ultricies nulla posuere. Suspendisse euismod sagittis vestibulum. Mauris lorem nisl, placerat et finibus non, cursus non ex. Interdum et malesuada fames ac ante ipsum primis in faucibus. Suspendisse finibus non dui vel tempor. Nam rhoncus ligula et massa sagittis fringilla. Cras convallis pellentesque nulla in rutrum.
val filecontent_lorem = """Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi ipsum magna, ultrices eu leo eu, consequat eleifend arcu. Nam tempor nunc aliquam mi cursus mollis. Aenean dictum iaculis dolor eget porttitor. Fusce vulputate dui id mauris ultricies, non aliquet nulla pulvinar. Integer consectetur nulla at cursus cursus. Nullam enim nisl, elementum a fermentum sed, suscipit id sapien. Duis eget enim lacinia, aliquam sapien ac, commodo risus. Morbi at enim sem. Aenean sollicitudin purus et sem porttitor, convallis ultricies nulla posuere. Suspendisse euismod sagittis vestibulum. Mauris lorem nisl, placerat et finibus non, cursus non ex. Interdum et malesuada fames ac ante ipsum primis in faucibus. Suspendisse finibus non dui vel tempor. Nam rhoncus ligula et massa sagittis fringilla. Cras convallis pellentesque nulla in rutrum.
Quisque ac orci sodales, semper neque eu, consequat lacus. Nulla suscipit orci felis, id tempor quam ultrices quis. Integer eu vulputate risus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Maecenas posuere sem sed erat tristique laoreet. Sed in ante est. Fusce nec est ut nunc aliquam condimentum viverra non ex. Pellentesque nisi ante, efficitur id neque sit amet, convallis tincidunt sapien. Nunc condimentum rutrum nisi, eu lobortis libero tempor non. Morbi euismod venenatis tincidunt. Nulla facilisi. Ut interdum nec nisi pharetra pretium.
@@ -14,13 +14,50 @@ Nam in aliquet velit, vitae aliquam sapien. Phasellus imperdiet nulla augue, fer
Nunc mollis nibh vitae sapien consequat, ut vestibulum sem pharetra. Aliquam iaculis, felis ut auctor porta, ipsum diam laoreet ex, sed egestas lacus est at neque. Aenean venenatis blandit arcu at porta. Nunc sed est magna. Duis pulvinar, nulla eu tristique mattis, dui diam malesuada sem, ac condimentum turpis nunc iaculis urna. Nam et ligula aliquet, fermentum lectus nec, consectetur ipsum. Proin convallis, mi id consectetur lobortis, urna nulla pellentesque odio, a finibus tortor nisl nec tortor. Suspendisse blandit nisl in magna hendrerit tristique. Cras sit amet metus et lacus rutrum tempus. In sapien elit, facilisis quis tristique a, vestibulum a massa. Donec ligula diam, posuere ac velit eget, lobortis tincidunt ante. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam lectus massa, egestas eu urna id, tempor pulvinar odio. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aliquam in suscipit mauris, quis faucibus dui. Cras tincidunt turpe es.""".toByteArray(Charsets.US_ASCII)
override fun startSend(sendfun: ((BlockTransferInterface) -> Unit)?) {
super.startSend { it.writeout(msg) }
private var fileOpen = false
private var readModeLength = -1
fun composeSerialAns(vararg msg: String): ByteArray {
val sb = ArrayList<Byte>()
sb.addAll(msg[0].toByteArray().toTypedArray())
for (k in 1 until msg.lastIndex) {
sb.add(0x1F)
sb.addAll(msg[k].toByteArray().toTypedArray())
}
sb.add(0x17)
return sb.toByteArray()
}
override fun startSend() {
if (readModeLength > 0) {
readModeLength = 0
startSend { it.writeout(filecontent_lorem) }
}
}
override fun hasNext(): Boolean = false
override fun writeout(inputData: ByteArray, writeoutfun: (() -> Unit)?) {
override fun writeout(inputData: ByteArray) {
val inputString = inputData.toString()
if (inputString.startsWith("DEVRST\u0017")) {
readModeLength = -1
fileOpen = false
}
else if (inputString.startsWith("DEVTYP\u0017"))
startSend { it.writeout(composeSerialAns("STOR")) }
else if (inputString.startsWith("DEVNAM\u0017"))
startSend { it.writeout(composeSerialAns("Testtec Virtual Disk Drive")) }
else if (inputString.startsWith("OPENR\""))
fileOpen = true
else if (inputString.startsWith("CLOSE"))
fileOpen = false
else if (inputString.startsWith("READ"))
readModeLength = inputString.substring(4 until inputString.length).toInt()
else if (inputString.startsWith("LIST"))
startSend { it.writeout("\"LOREM.TXT\" TXT\nTotal 1 files on the disk".toByteArray()) }
}
override fun setMode(sendmode: Boolean) {