working print() sans screen scroll

This commit is contained in:
minjaesong
2020-05-12 21:47:27 +09:00
parent 80c1c25b4d
commit 516eb84b65
7 changed files with 262 additions and 16 deletions

View File

@@ -7,7 +7,7 @@ import sun.nio.ch.DirectBuffer
class GraphicsJSR223Delegate(val vm: VM) {
private fun getFirstGPU(): GraphicsAdapter? {
return vm.peripheralTable[vm.findPeribyType("gpu") ?: return null].peripheral as? GraphicsAdapter
return vm.findPeribyType(VM.PERITYPE_TERM)?.peripheral as? GraphicsAdapter
}
fun resetPalette() {

View File

@@ -7,6 +7,8 @@ import net.torvald.tsvm.firmware.Firmware.Companion.toLuaValue
import net.torvald.tsvm.peripheral.IOSpace
import net.torvald.tsvm.peripheral.PeriBase
import org.luaj.vm2.LuaValue
import java.io.InputStream
import java.io.OutputStream
import java.util.*
import kotlin.math.ceil
import kotlin.random.Random
@@ -64,6 +66,10 @@ class VM(
val peripheralTable = Array(8) { PeripheralEntry() }
lateinit var printStream: OutputStream
lateinit var errorStream: OutputStream
lateinit var inputStream: InputStream
init {
peripheralTable[0] = PeripheralEntry(
"io",
@@ -77,9 +83,9 @@ class VM(
}
fun findPeribyType(searchTerm: String): Int? {
fun findPeribyType(searchTerm: String): PeripheralEntry? {
for (i in 0..7) {
if (peripheralTable[i].type == searchTerm) return i
if (peripheralTable[i].type == searchTerm) return peripheralTable[i]
}
return null
}
@@ -100,7 +106,7 @@ class VM(
val HW_RESERVE_SIZE = 1024.kB()
val USER_SPACE_SIZE = 8192.kB()
const val PERITYPE_GRAPHICS = "gpu"
const val PERITYPE_TERM = "gpu"
}
internal fun translateAddr(addr: Long): Pair<Any?, Long> {

View File

@@ -7,8 +7,8 @@ import com.badlogic.gdx.graphics.OrthographicCamera
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import kotlinx.coroutines.*
import net.torvald.tsvm.peripheral.GraphicsAdapter
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.suspendCoroutine
import java.io.InputStream
import java.io.OutputStream
class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter() {
@@ -28,9 +28,9 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter()
gpu = GraphicsAdapter(lcdMode = false)
vm.peripheralTable[1] = PeripheralEntry(
VM.PERITYPE_GRAPHICS,
VM.PERITYPE_TERM,
gpu,
256.kB(),
GraphicsAdapter.VRAM_SIZE,
16,
0
)
@@ -42,6 +42,9 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter()
batch.projectionMatrix = camera.combined
Gdx.gl20.glViewport(0, 0, appConfig.width, appConfig.height)
vm.printStream = gpu.getPrintStream()
vm.errorStream = gpu.getErrorStream()
//inputStream = gpu.getInputStream()
// TEST PRG
vmRunner = VMRunnerFactory(vm, "js")
@@ -244,7 +247,7 @@ while (true) {
for (var k = 0; k < 2560; k++) {
vm.poke(-(253952 + k + 1) - hwoff, -2); // transparent
vm.poke(-(253952 + 2560 + k + 1) - hwoff, -1); // white
vm.poke(-(253952 + 2560*2 + k + 1) - hwoff, Math.round(Math.random() * 255));
/*vm.poke(-(253952 + 2560*2 + k + 1) - hwoff, Math.round(Math.random() * 255));*/
}
rng = inthash(rng);
@@ -255,7 +258,7 @@ while (true) {
}
""".trimIndent()
private val gpuTestPaletteJs = "eval('${jscode.replace(Regex("//[^\\n]*"), "").replace('\n', ' ')}')"
private val gpuTestPaletteJs = "function print(s){vm.print(s)}eval('${jscode.replace(Regex("//[^\\n]*"), "").replace('\n', ' ')}')"
private val gpuTestPaletteJava = """

View File

@@ -13,4 +13,13 @@ class VMJSR223Delegate(val vm: VM) {
fun malloc(size: Int) = vm.malloc(size)
fun free(ptr: Int) = vm.free(ptr)
fun print(s: String) {
//println("[Nashorn] $s")
vm.printStream.write((s + '\n').toByteArray())
}
}
class VMSerialDebugger(val vm: VM) {
fun print(s: String) = System.out.println(s)
}

View File

@@ -52,6 +52,7 @@ object VMRunnerFactory {
bind.put("graphics", GraphicsJSR223Delegate(vm))
//bind.put("poke", { a: Long, b: Byte -> vm.poke(a, b) }) // kts: lambda does not work...
//bind.put("nanotime", { System.nanoTime() })
bind.put("serial", VMSerialDebugger(vm))
}
override suspend fun executeCommand(command: String) {

View File

@@ -1,13 +1,27 @@
package net.torvald.tsvm.peripheral
import java.io.InputStream
import java.io.OutputStream
import java.util.*
/**
* Implements standard TTY that can interpret some of the ANSI escape sequences
*
* A paper tty must be able to implemented by extending this class (and butchering some of the features), of which it
* sets limits on some of the functions (notably 'setCursorPos')
*/
abstract class GlassTty(val TEXT_ROWS: Int, val TEXT_COLS: Int) {
/**
* (x, y)
*/
abstract fun getCursorPos(): Pair<Int, Int>
/**
* Think of it as a real paper tty;
* setCursorPos must "wrap" the cursor properly when x-value goes out of screen bound.
* For y-value, only when y < 0, set y to zero and don't care about the y-value goes out of bound.
*/
abstract fun setCursorPos(x: Int, y: Int)
abstract var rawCursorPos: Int
@@ -19,6 +33,23 @@ abstract class GlassTty(val TEXT_ROWS: Int, val TEXT_COLS: Int) {
abstract fun putChar(x: Int, y: Int, text: Byte, foreColour: Byte = ttyFore.toByte(), backColour: Byte = ttyBack.toByte())
fun writeOut(char: Byte) {
val printable = acceptChar(char)
if (printable) {
val (x, y) = getCursorPos()
putChar(x, y, char)
setCursorPos(x + 1, y) // should automatically wrap and advance a line for out-of-bound x-value
}
// deal with y-axis out-of-bounds
val (cx, cy) = getCursorPos()
if (cy >= TEXT_ROWS) {
scrollUp(cy - TEXT_ROWS + 1)
setCursorPos(cx, TEXT_ROWS - 1)
}
}
private var ttyEscState = TTY_ESC_STATE.INITIAL
private val ttyEscArguments = Stack<Int>()
/**
@@ -52,11 +83,14 @@ abstract class GlassTty(val TEXT_ROWS: Int, val TEXT_COLS: Int) {
when (ttyEscState) {
TTY_ESC_STATE.INITIAL -> {
if (char == ESC) {
ttyEscState = TTY_ESC_STATE.ESC
}
else {
return true
when (char) {
ESC -> ttyEscState = TTY_ESC_STATE.ESC
LF -> crlf()
BS -> backspace()
TAB -> insertTab()
BEL -> ringBell()
in 0x00.toByte()..0x1F.toByte() -> return false
else -> return true
}
}
TTY_ESC_STATE.ESC -> {
@@ -170,6 +204,8 @@ abstract class GlassTty(val TEXT_ROWS: Int, val TEXT_COLS: Int) {
return false
}
abstract fun resetTtyStatus()
abstract fun cursorUp(arg: Int = 1)
abstract fun cursorDown(arg: Int = 1)
@@ -180,15 +216,31 @@ abstract class GlassTty(val TEXT_ROWS: Int, val TEXT_COLS: Int) {
abstract fun cursorX(arg: Int = 1) // aka Cursor Horizintal Absolute
abstract fun eraseInDisp(arg: Int = 0)
abstract fun eraseInLine(arg: Int = 0)
/** New lines are added at the bottom */
abstract fun scrollUp(arg: Int = 1)
/** New lines are added at the top */
abstract fun scrollDown(arg: Int = 1)
abstract fun sgrOneArg(arg: Int = 0)
abstract fun sgrTwoArg(arg1: Int, arg2: Int)
abstract fun sgrThreeArg(arg1: Int, arg2: Int, arg3: Int)
/** The values are one-based
* @param arg1 y-position (row)
* @param arg2 x-position (column) */
abstract fun cursorXY(arg1: Int, arg2: Int)
abstract fun ringBell()
abstract fun insertTab()
abstract fun crlf()
abstract fun backspace()
abstract fun getPrintStream(): OutputStream
abstract fun getErrorStream(): OutputStream
abstract fun getInputStream(): InputStream
private val CR = 0x0D.toByte()
private val LF = 0x0A.toByte()
private val TAB = 0x09.toByte()
private val BS = 0x08.toByte()
private val BEL = 0x07.toByte()
private val ESC = 0x1B.toByte()
private enum class TTY_ESC_STATE {

View File

@@ -9,6 +9,9 @@ import net.torvald.tsvm.AppLoader
import net.torvald.tsvm.VM
import net.torvald.tsvm.kB
import sun.nio.ch.DirectBuffer
import java.io.InputStream
import java.io.OutputStream
import java.io.PrintStream
import kotlin.experimental.and
class GraphicsAdapter(val lcdMode: Boolean = false) : GlassTty(Companion.TEXT_ROWS, Companion.TEXT_COLS), PeriBase {
@@ -58,8 +61,28 @@ class GraphicsAdapter(val lcdMode: Boolean = false) : GlassTty(Companion.TEXT_RO
set(value) { spriteAndTextArea.setShort(memTextCursorPosOffset, value.toShort()) }
override fun getCursorPos() = rawCursorPos % TEXT_COLS to rawCursorPos / TEXT_COLS
/**
* Think of it as a real paper tty;
* setCursorPos must "wrap" the cursor properly when x-value goes out of screen bound.
* For y-value, only when y < 0, set y to zero and don't care about the y-value goes out of bound.
*/
override fun setCursorPos(x: Int, y: Int) {
rawCursorPos = toTtyTextOffset(x, y)
var newx = x
var newy = y
if (newx >= TEXT_COLS) {
newx = 0
newy += 1
}
else if (newx < 0) {
newx = 0
}
if (newy < 0) {
newy = 0 // DON'T SCROLL when cursor goes ABOVE the screen
}
rawCursorPos = toTtyTextOffset(newx, newy)
}
private fun toTtyTextOffset(x: Int, y: Int) = y * TEXT_COLS + x
@@ -80,6 +103,7 @@ class GraphicsAdapter(val lcdMode: Boolean = false) : GlassTty(Companion.TEXT_RO
// -1 is preferred because it points to the colour CLEAR, and it's constant.
spriteAndTextArea.fillWith(-1)
setCursorPos(0, 0)
println(framebuffer.pixels.limit())
}
@@ -194,6 +218,157 @@ class GraphicsAdapter(val lcdMode: Boolean = false) : GlassTty(Companion.TEXT_RO
spriteAndTextArea[memTextOffset + textOff] = text
}
override fun cursorUp(arg: Int) {
val (x, y) = getCursorPos()
setCursorPos(x, y - arg)
}
override fun cursorDown(arg: Int) {
val (x, y) = getCursorPos()
val newy = y + arg
setCursorPos(x, if (newy >= TEXT_ROWS) TEXT_ROWS - 1 else newy)
}
override fun cursorFwd(arg: Int) {
val (x, y) = getCursorPos()
setCursorPos(x + arg, y)
}
override fun cursorBack(arg: Int) {
val (x, y) = getCursorPos()
setCursorPos(x - arg, y)
}
override fun cursorNextLine(arg: Int) {
val (_, y) = getCursorPos()
val newy = y + arg
setCursorPos(0, if (newy >= TEXT_ROWS) TEXT_ROWS - 1 else newy)
if (newy >= TEXT_ROWS) {
scrollUp(newy - TEXT_ROWS + 1)
}
}
override fun cursorPrevLine(arg: Int) {
val (_, y) = getCursorPos()
setCursorPos(0, y - arg)
}
override fun cursorX(arg: Int) {
val (_, y) = getCursorPos()
setCursorPos(arg, y)
}
override fun eraseInDisp(arg: Int) {
when (arg) {
else -> TODO()
}
}
override fun eraseInLine(arg: Int) {
when (arg) {
else -> TODO()
} }
/** New lines are added at the bottom */
override fun scrollUp(arg: Int) {
//TODO("Not yet implemented")
}
/** New lines are added at the top */
override fun scrollDown(arg: Int) {
TODO("Not yet implemented")
}
override fun sgrOneArg(arg: Int) {
TODO("Not yet implemented")
}
override fun sgrTwoArg(arg1: Int, arg2: Int) {
TODO("Not yet implemented")
}
override fun sgrThreeArg(arg1: Int, arg2: Int, arg3: Int) {
TODO("Not yet implemented")
}
/** The values are one-based
* @param arg1 y-position (row)
* @param arg2 x-position (column) */
override fun cursorXY(arg1: Int, arg2: Int) {
TODO("Not yet implemented")
}
override fun ringBell() {
TODO("Not yet implemented")
}
override fun insertTab() {
TODO("Not yet implemented")
}
override fun crlf() {
val (_, y) = getCursorPos()
val newy = y + 1
setCursorPos(0, if (newy >= TEXT_ROWS) TEXT_ROWS - 1 else newy)
if (newy >= TEXT_ROWS) scrollUp(1)
}
override fun backspace() {
val (x, y) = getCursorPos()
putChar(x, y, 0x20.toByte())
setCursorPos(x - 1, y)
}
private lateinit var PRINTSTREAM_INSTANCE: OutputStream
private lateinit var ERRORSTREAM_INSTANCE: OutputStream
private lateinit var INPUTSTREAM_INSTANCE: InputStream
override fun getPrintStream(): OutputStream {
try {
return PRINTSTREAM_INSTANCE
}
catch (e: UninitializedPropertyAccessException) {
PRINTSTREAM_INSTANCE = object : OutputStream() {
override fun write(p0: Int) {
writeOut(p0.toByte())
}
}
return PRINTSTREAM_INSTANCE
}
}
override fun getErrorStream(): OutputStream {
try {
return ERRORSTREAM_INSTANCE
}
catch (e: UninitializedPropertyAccessException) {
ERRORSTREAM_INSTANCE = object : OutputStream() {
private val SGI_RED = byteArrayOf(0x1B, 0x5B, 0x33, 0x31, 0x6D)
private val SGI_RESET = byteArrayOf(0x1B, 0x5B, 0x6D)
override fun write(p0: Int) {
SGI_RED.forEach { writeOut(it) }
writeOut(p0.toByte())
SGI_RESET.forEach { writeOut(it) }
}
override fun write(p0: ByteArray) {
SGI_RED.forEach { writeOut(it) }
p0.forEach { writeOut(it) }
SGI_RESET.forEach { writeOut(it) }
}
}
return ERRORSTREAM_INSTANCE
}
}
override fun getInputStream(): InputStream {
TODO("Not yet implemented")
}
override fun dispose() {
framebuffer.dispose()
rendertex.dispose()