can read mouse position via mmio

This commit is contained in:
minjaesong
2020-05-15 02:54:01 +09:00
parent ffe314bd61
commit 75bb177106
10 changed files with 348 additions and 18 deletions

View File

@@ -39,6 +39,14 @@ class GraphicsJSR223Delegate(val vm: VM) {
} }
} }
fun plotPixel(x: Int, y: Int, color: Byte) {
getFirstGPU()?.let {
if (x in 0 until GraphicsAdapter.WIDTH && y in 0 until GraphicsAdapter.HEIGHT) {
it.poke(y.toLong() * GraphicsAdapter.WIDTH + x, color)
}
}
}
private fun GraphicsAdapter._loadbulk(fromAddr: Int, toAddr: Int, length: Int) { private fun GraphicsAdapter._loadbulk(fromAddr: Int, toAddr: Int, length: Int) {
UnsafeHelper.memcpy( UnsafeHelper.memcpy(
vm.usermem.ptr + fromAddr, vm.usermem.ptr + fromAddr,

View File

@@ -66,6 +66,8 @@ class VM(
val peripheralTable = Array(8) { PeripheralEntry() } val peripheralTable = Array(8) { PeripheralEntry() }
internal fun getIO(): IOSpace = peripheralTable[0].peripheral as IOSpace
lateinit var printStream: OutputStream lateinit var printStream: OutputStream
lateinit var errorStream: OutputStream lateinit var errorStream: OutputStream
lateinit var inputStream: InputStream lateinit var inputStream: InputStream
@@ -73,7 +75,7 @@ class VM(
init { init {
peripheralTable[0] = PeripheralEntry( peripheralTable[0] = PeripheralEntry(
"io", "io",
IOSpace(), IOSpace(this),
HW_RESERVE_SIZE, HW_RESERVE_SIZE,
MMIO_SIZE.toInt() - 256, MMIO_SIZE.toInt() - 256,
64 64
@@ -90,6 +92,10 @@ class VM(
return null return null
} }
fun update(delta: Float) {
getIO().update(delta)
}
fun dispose() { fun dispose() {
usermem.destroy() usermem.destroy()
peripheralTable.forEach { it.peripheral?.dispose() } peripheralTable.forEach { it.peripheral?.dispose() }

View File

@@ -2,11 +2,13 @@ package net.torvald.tsvm
import com.badlogic.gdx.ApplicationAdapter import com.badlogic.gdx.ApplicationAdapter
import com.badlogic.gdx.Gdx import com.badlogic.gdx.Gdx
import com.badlogic.gdx.InputProcessor
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration
import com.badlogic.gdx.graphics.OrthographicCamera import com.badlogic.gdx.graphics.OrthographicCamera
import com.badlogic.gdx.graphics.g2d.SpriteBatch 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 java.io.InputStream import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
@@ -25,7 +27,7 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter()
override fun create() { override fun create() {
super.create() super.create()
gpu = GraphicsAdapter(lcdMode = true) gpu = GraphicsAdapter(vm, lcdMode = false)
vm.peripheralTable[1] = PeripheralEntry( vm.peripheralTable[1] = PeripheralEntry(
VM.PERITYPE_TERM, VM.PERITYPE_TERM,
@@ -49,8 +51,11 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter()
// TEST PRG // TEST PRG
vmRunner = VMRunnerFactory(vm, "js") vmRunner = VMRunnerFactory(vm, "js")
coroutineJob = GlobalScope.launch { coroutineJob = GlobalScope.launch {
vmRunner.executeCommand(sanitiseJS(gpuTestPaletteJs)) vmRunner.executeCommand(sanitiseJS(shitcode))
} }
Gdx.input.inputProcessor = vm.getIO()
} }
private var updateAkku = 0.0 private var updateAkku = 0.0
@@ -77,16 +82,11 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter()
private var latch = true private var latch = true
private fun updateGame(delta: Float) { private fun updateGame(delta: Float) {
vm.update(delta)
} }
fun poke(addr: Long, value: Byte) = vm.poke(addr, value) fun poke(addr: Long, value: Byte) = vm.poke(addr, value)
private fun paintTestPalette() {
}
private val gpuTestPaletteKt = """ private val gpuTestPaletteKt = """
val w = 560 val w = 560
val h = 448 val h = 448
@@ -265,6 +265,13 @@ println("Starting TVDOS...");
println("TSVM Disk Operating System, version 1.20"); println("TSVM Disk Operating System, version 1.20");
println(""); println("");
print("C:\\\\>"); print("C:\\\\>");
while (true) {
var mx = vm.peek(-33) + vm.peek(-34) * 256;
var my = vm.peek(-35) + vm.peek(-36) * 256;
println("mx: "+mx+", my: "+my);
graphics.plotPixel(mx, my, (mx + my) % 255);
}
""".trimIndent() """.trimIndent()
private val gpuTestPaletteJava = """ private val gpuTestPaletteJava = """

View File

@@ -8,7 +8,7 @@ import net.torvald.tsvm.peripheral.GraphicsAdapter
class VMJSR223Delegate(val vm: VM) { class VMJSR223Delegate(val vm: VM) {
fun poke(addr: Int, value: Int) = vm.poke(addr.toLong(), value.toByte()) fun poke(addr: Int, value: Int) = vm.poke(addr.toLong(), value.toByte())
fun peek(addr: Int) = vm.peek(addr.toLong()) fun peek(addr: Int) = vm.peek(addr.toLong())!!.toInt().and(255)
fun nanoTime() = System.nanoTime() fun nanoTime() = System.nanoTime()
fun malloc(size: Int) = vm.malloc(size) fun malloc(size: Int) = vm.malloc(size)
fun free(ptr: Int) = vm.free(ptr) fun free(ptr: Int) = vm.free(ptr)

View File

@@ -204,8 +204,6 @@ abstract class GlassTty(val TEXT_ROWS: Int, val TEXT_COLS: Int) {
return false return false
} }
abstract fun resetTtyStatus() abstract fun resetTtyStatus()
abstract fun cursorUp(arg: Int = 1) abstract fun cursorUp(arg: Int = 1)
abstract fun cursorDown(arg: Int = 1) abstract fun cursorDown(arg: Int = 1)
@@ -247,6 +245,17 @@ abstract class GlassTty(val TEXT_ROWS: Int, val TEXT_COLS: Int) {
INITIAL, ESC, CSI, NUM1, SEP1, NUM2, SEP2, NUM3 INITIAL, ESC, CSI, NUM1, SEP1, NUM2, SEP2, NUM3
} }
/**
* Puts a key into a keyboard buffer
*/
abstract fun putKey(key: Int)
/**
* Takes a key from a keyboard buffer
*/
abstract fun takeKey(): Int
} }
/* /*

View File

@@ -8,13 +8,18 @@ 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 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 java.io.PrintStream
import kotlin.experimental.and import kotlin.experimental.and
class GraphicsAdapter(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 {
override fun getVM(): VM {
return vm
}
internal val framebuffer = Pixmap(WIDTH, HEIGHT, Pixmap.Format.Alpha) internal val framebuffer = Pixmap(WIDTH, HEIGHT, Pixmap.Format.Alpha)
private var rendertex = Texture(1, 1, Pixmap.Format.RGBA8888) private var rendertex = Texture(1, 1, Pixmap.Format.RGBA8888)
@@ -569,6 +574,16 @@ class GraphicsAdapter(val lcdMode: Boolean = false) : GlassTty(Companion.TEXT_RO
} }
} }
override fun putKey(key: Int) {
vm.poke(-39, key.toByte())
}
/**
* @return key code in 0..255 (TODO: JInput Keycode or ASCII-Code?)
*/
override fun takeKey(): Int {
return vm.peek(-38)!!.toInt().and(255)
}
private fun Boolean.toInt() = if (this) 1 else 0 private fun Boolean.toInt() = if (this) 1 else 0

View File

@@ -1,22 +1,94 @@
package net.torvald.tsvm.peripheral package net.torvald.tsvm.peripheral
class IOSpace : PeriBase { import com.badlogic.gdx.Gdx
import com.badlogic.gdx.InputProcessor
import net.torvald.UnsafeHelper
import net.torvald.tsvm.VM
import net.torvald.util.CircularArray
class IOSpace(val vm: VM) : PeriBase, InputProcessor {
override fun getVM(): VM {
return vm
}
/** Absolute x-position of the computer GUI */
var guiPosX = 0
/** Absolute y-position of the computer GUI */
var guiPosY = 0
private val keyboardBuffer = CircularArray<Byte>(32, true)
private var mouseX: Short = 0
private var mouseY: Short = 0
private var mouseDown = false
override fun peek(addr: Long): Byte? { override fun peek(addr: Long): Byte? {
TODO("Not yet implemented") return mmio_read(addr)
} }
override fun poke(addr: Long, byte: Byte) { override fun poke(addr: Long, byte: Byte) {
TODO("Not yet implemented") mmio_write(addr, byte)
} }
override fun mmio_read(addr: Long): Byte? { override fun mmio_read(addr: Long): Byte? {
TODO("Not yet implemented") val adi = addr.toInt()
return when (addr) {
in 0..31 -> keyboardBuffer[(addr.toInt())] ?: -1
in 32..33 -> (mouseX.toInt() shr (adi - 32).times(8)).toByte()
in 34..35 -> (mouseY.toInt() shr (adi - 34).times(8)).toByte()
36L -> if (mouseDown) 1 else 0
37L -> keyboardBuffer.removeHead() ?: -1
else -> -1
}
} }
override fun mmio_write(addr: Long, byte: Byte) { override fun mmio_write(addr: Long, byte: Byte) {
TODO("Not yet implemented") val adi = addr.toInt()
val bi = byte.toInt().and(255)
when (addr) {
}
} }
override fun dispose() { override fun dispose() {
} }
fun update(delta: Float) {
mouseX = (Gdx.input.x + guiPosX).toShort()
mouseY = (Gdx.input.y + guiPosY).toShort()
mouseDown = Gdx.input.isTouched
}
override fun touchUp(p0: Int, p1: Int, p2: Int, p3: Int): Boolean {
return false
}
override fun mouseMoved(p0: Int, p1: Int): Boolean {
return false
}
override fun keyTyped(p0: Char): Boolean {
keyboardBuffer.appendTail(p0.toByte())
println("[IO] Key typed: $p0")
return true
}
override fun scrolled(p0: Int): Boolean {
return false
}
override fun keyUp(p0: Int): Boolean {
return false
}
override fun touchDragged(p0: Int, p1: Int, p2: Int): Boolean {
return false
}
override fun keyDown(p0: Int): Boolean {
return false
}
override fun touchDown(p0: Int, p1: Int, p2: Int, p3: Int): Boolean {
return false
}
} }

View File

@@ -1,5 +1,7 @@
package net.torvald.tsvm.peripheral package net.torvald.tsvm.peripheral
import net.torvald.tsvm.VM
interface PeriBase { interface PeriBase {
/** /**
@@ -16,4 +18,6 @@ interface PeriBase {
fun mmio_write(addr: Long, byte: Byte) fun mmio_write(addr: Long, byte: Byte)
fun dispose() fun dispose()
fun getVM(): VM
} }

View File

@@ -0,0 +1,192 @@
package net.torvald.util
import java.util.*
/**
* buffer[head] contains the most recent item, whereas buffer[tail] contains the oldest one.
*
* Notes for particle storage:
* Particles does not need to be removed, just let it overwrite as their operation is rather
* lightweight. So, just flagDespawn = true if it need to be "deleted" so that it won't update
* anymore.
*
* Created by minjaesong on 2017-01-22.
*/
class CircularArray<T>(val size: Int, val overwriteOnOverflow: Boolean): Iterable<T> {
/**
* What to do RIGHT BEFORE old element is being overridden by the new element (only makes sense when ```overwriteOnOverflow = true```)
*
* This function will not be called when ```removeHead()``` or ```removeTail()``` is called.
*/
var overwritingPolicy: (T) -> Unit = {
// do nothing
}
val buffer: Array<T> = arrayOfNulls<Any>(size) as Array<T>
/** Tail stands for the oldest element. The tail index points AT the tail element */
var tail: Int = 0; private set
/** Head stands for the youngest element. The head index points AFTER the head element */
var head: Int = 0; private set
private var overflow = false
val lastIndex = size - 1
/**
* Number of elements that forEach() or fold() would iterate.
*/
val elemCount: Int
get() = if (overflow) size else head - tail
val isEmpty: Boolean
get() = !overflow && head == tail
private inline fun incHead() { head = (head + 1).wrap() }
private inline fun decHead() { head = (head - 1).wrap() }
private inline fun incTail() { tail = (tail + 1).wrap() }
private inline fun decTail() { tail = (tail - 1).wrap() }
fun clear() {
tail = 0
head = 0
overflow = false
}
/**
* When the overflowing is enabled, tail element (ultimate element) will be changed into the penultimate element.
*/
fun appendHead(item: T) {
if (overflow && !overwriteOnOverflow) {
throw StackOverflowError()
}
else {
if (overflow) {
overwritingPolicy.invoke(buffer[head])
}
buffer[head] = item
incHead()
}
if (overflow) {
incTail()
}
// must be checked AFTER the actual head increment; otherwise this condition doesn't make sense
if (tail == head) {
overflow = true
}
}
fun appendTail(item: T) {
// even if overflowing is enabled, appending at tail causes head element to be altered, therefore such action
// must be blocked by throwing overflow error
if (overflow) {
throw StackOverflowError()
}
else {
decTail()
buffer[tail] = item
}
// must be checked AFTER the actual head increment; otherwise this condition doesn't make sense
if (tail == head) {
overflow = true
}
}
fun removeHead(): T? {
if (isEmpty) return null
decHead()
overflow = false
return buffer[head]
}
fun removeTail(): T? {
if (isEmpty) return null
val ret = buffer[tail]
incTail()
overflow = false
return ret
}
/** Returns the youngest (last of the array) element */
fun getHeadElem(): T? = if (isEmpty) null else buffer[(head - 1).wrap()]
/** Returns the oldest (first of the array) element */
fun getTailElem(): T? = if (isEmpty) null else buffer[tail]
/**
* Relative-indexed get. Index of zero will return the head element.
*/
operator fun get(index: Int): T? = buffer[(head - 1 - index).wrap()]
private fun getAbsoluteRange() = 0 until when {
head == tail -> buffer.size
tail > head -> buffer.size - (((head - 1).wrap()) - tail)
else -> head - tail
}
override fun iterator(): Iterator<T> {
if (isEmpty) {
return object : Iterator<T> {
override fun next(): T = throw EmptyStackException()
override fun hasNext() = false
}
}
val rangeMax = getAbsoluteRange().last
var counter = 0
return object : Iterator<T> {
override fun next(): T {
val ret = buffer[(counter + tail).wrap()]
counter += 1
return ret
}
override fun hasNext() = (counter <= rangeMax)
}
}
/**
* Iterates the array with oldest element (tail) first.
*/
fun forEach(action: (T) -> Unit) {
// for (element in buffer) action(element)
// return nothing
iterator().forEach(action)
}
fun <R> fold(initial: R, operation: (R, T) -> R): R {
// accumulator = initial
// for (element in buffer) accumulator = operation(accumulator, element)
// return accumulator
var accumulator = initial
if (isEmpty)
return initial
else {
iterator().forEach {
accumulator = operation(accumulator, it)
}
}
return accumulator
}
private inline fun Int.wrap() = this fmod size
override fun toString(): String {
return "CircularArray(size=${buffer.size}, head=$head, tail=$tail, overflow=$overflow)"
}
private inline infix fun Int.fmod(other: Int) = Math.floorMod(this, other)
}

View File

@@ -37,6 +37,23 @@ User area: 8 MB, hardware area: 8 MB
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
IO Device
Endianness: little
MMIO
0..31: Raw Keyboard Buffer read. Won't shift the key buffer
32..33: Mouse X pos
34..35: Mouse Y pos
36: Mouse down? (1 for TRUE, 0 for FALSE)
37: Read/Write single key input. Key buffer will be shifted. Manual writing is
usually unnecessary as such action must be automatically managed via LibGDX
input processing.
--------------------------------------------------------------------------------
VRAM Bank 0 (256 kB) VRAM Bank 0 (256 kB)
Endianness: little Endianness: little