mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 19:51:51 +09:00
working print() sans screen scroll
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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 = """
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user