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) {
UnsafeHelper.memcpy(
vm.usermem.ptr + fromAddr,

View File

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

View File

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

View File

@@ -8,7 +8,7 @@ import net.torvald.tsvm.peripheral.GraphicsAdapter
class VMJSR223Delegate(val vm: VM) {
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 malloc(size: Int) = vm.malloc(size)
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
}
abstract fun resetTtyStatus()
abstract fun cursorUp(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
}
/**
* 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.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 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)
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

View File

@@ -1,22 +1,94 @@
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? {
TODO("Not yet implemented")
return mmio_read(addr)
}
override fun poke(addr: Long, byte: Byte) {
TODO("Not yet implemented")
mmio_write(addr, 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) {
TODO("Not yet implemented")
val adi = addr.toInt()
val bi = byte.toInt().and(255)
when (addr) {
}
}
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
import net.torvald.tsvm.VM
interface PeriBase {
/**
@@ -16,4 +18,6 @@ interface PeriBase {
fun mmio_write(addr: Long, byte: Byte)
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)
Endianness: little