diff --git a/assets/jscon.js b/assets/jscon.js new file mode 100644 index 0000000..2b19468 --- /dev/null +++ b/assets/jscon.js @@ -0,0 +1,44 @@ +function readConsoleInput() { + var cmdbuf = ""; + var key = -1; + while (key != 10 && key != 13) { + key = vm.readKey(); + // printable chars + if (key >= 32 && key <= 126) { + var s = String.fromCharCode(key); + cmdbuf += s; + print(s); + } + // backspace + else if (key == 8 && cmdbuf.length > 0) { + cmdbuf = cmdbuf.substring(0, cmdbuf.length - 1); + print(String.fromCharCode(key)); + } + // up down key + else if (key >= 19 && key <= 20) { + return key; + } + // left right key + else if (key >= 19 && key <= 20) { + // + } + } + return cmdbuf; +} + +println("JS Console"); +while (true) { + print("JS> "); + + var cmdbuf = readConsoleInput(); + + if (typeof cmdbuf == "string") { + println(); + try { + println(eval(cmdbuf)); + } + catch (e) { + println(e); + } + } +} \ No newline at end of file diff --git a/assets/tvdos/command.js b/assets/tvdos/command.js index da34017..7918963 100644 --- a/assets/tvdos/command.js +++ b/assets/tvdos/command.js @@ -22,16 +22,9 @@ println("Starting TVDOS..."); greet(); -/*while (true) { +while (true) { print(get_prompt_text()); var s = read(); println(); println("String read: " + s + "@"); -}*/ - -println(vm.peek(-4093)); // expecting an odd number -vm.poke(-4093, 6); -for (i = 0; i < 4096; i++) { - print(String.fromCharCode(vm.peek(-4097 - i))); -} -println(); \ No newline at end of file +} \ No newline at end of file diff --git a/src/net/torvald/tsvm/VMGUI.kt b/src/net/torvald/tsvm/VMGUI.kt index 9a3f7ff..63b1488 100644 --- a/src/net/torvald/tsvm/VMGUI.kt +++ b/src/net/torvald/tsvm/VMGUI.kt @@ -51,7 +51,8 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter() vm.getInputStream = { gpu.getInputStream() } // TEST PRG - val fr = FileReader("./assets/tvdos/command.js") + //val fr = FileReader("./assets/tvdos/command.js") + val fr = FileReader("./assets/jscon.js") val prg = fr.readText() fr.close() diff --git a/src/net/torvald/tsvm/VMJSR223Delegate.kt b/src/net/torvald/tsvm/VMJSR223Delegate.kt index 2eac43b..4503030 100644 --- a/src/net/torvald/tsvm/VMJSR223Delegate.kt +++ b/src/net/torvald/tsvm/VMJSR223Delegate.kt @@ -23,10 +23,16 @@ class VMJSR223Delegate(val vm: VM) { } fun println() = print('\n') - //fun readKey() = vm.inputStream.read() + fun readKey(): Int { + val inputStream = vm.getInputStream() + var key: Int = inputStream.read() + inputStream.close() + return key + } /** - * Read series of key inputs until Enter/Return key is pressed + * Read series of key inputs until Enter/Return key is pressed. Backspace will work but any other non-printable + * characters (e.g. arrow keys) won't work. */ fun read(): String { val inputStream = vm.getInputStream() diff --git a/src/net/torvald/tsvm/peripheral/BlockTransferInterface.kt b/src/net/torvald/tsvm/peripheral/BlockTransferInterface.kt index fe50a04..86a6ead 100644 --- a/src/net/torvald/tsvm/peripheral/BlockTransferInterface.kt +++ b/src/net/torvald/tsvm/peripheral/BlockTransferInterface.kt @@ -8,6 +8,7 @@ abstract class BlockTransferInterface(val isMaster: Boolean, val isSlave: Boolea open var busy = false protected var sendmode = false; private set + open var blockSize = 0 open fun attachDevice(device: BlockTransferInterface?) { recipient = device @@ -39,10 +40,14 @@ abstract class BlockTransferInterface(val isMaster: Boolean, val isSlave: Boolea open fun writeout(inputData: ByteArray, writeoutfun: (() -> Unit)? = null) { busy = true ready = false + blockSize = minOf(inputData.size, BLOCK_SIZE) writeoutfun?.invoke() busy = false ready = true } + abstract fun hasNext(): Boolean + open fun doYouHaveNext(): Boolean = recipient?.hasNext() ?: false + open fun yourBlockSize(): Int = recipient?.blockSize ?: 0 /** @param sendmode TRUE for send, FALSE for receive */ open fun setMode(sendmode: Boolean) { @@ -52,4 +57,8 @@ abstract class BlockTransferInterface(val isMaster: Boolean, val isSlave: Boolea open fun getMode(): Boolean = sendmode open fun cableConnected(): Boolean = recipient?.recipient == this + + companion object { + const val BLOCK_SIZE = 4096 + } } \ No newline at end of file diff --git a/src/net/torvald/tsvm/peripheral/BlockTransferPort.kt b/src/net/torvald/tsvm/peripheral/BlockTransferPort.kt index 058eafb..174b79d 100644 --- a/src/net/torvald/tsvm/peripheral/BlockTransferPort.kt +++ b/src/net/torvald/tsvm/peripheral/BlockTransferPort.kt @@ -8,19 +8,22 @@ import net.torvald.tsvm.VM */ internal class BlockTransferPort(val vm: VM, val portno: Int) : BlockTransferInterface(true, false) { + internal var hasNext = false override fun startSend(sendfun: ((BlockTransferInterface) -> Unit)?) { super.startSend { recipient -> - val ba = ByteArray(4096) { vm.getIO().blockTransforBlock[portno][it.toLong()] } + val ba = ByteArray(BLOCK_SIZE) { vm.getIO().blockTransferRx[portno][it.toLong()] } recipient.writeout(ba) } } + override fun hasNext(): Boolean = hasNext + override fun writeout(inputData: ByteArray, writeoutfun: (() -> Unit)?) { super.writeout(inputData) { - val copySize = minOf(4096, inputData.size).toLong() + val copySize = minOf(BLOCK_SIZE, inputData.size).toLong() val arrayOffset = UnsafeHelper.getArrayOffset(inputData).toLong() - UnsafeHelper.memcpyRaw(inputData, arrayOffset, null, vm.getIO().blockTransforBlock[portno].ptr, copySize) + UnsafeHelper.memcpyRaw(inputData, arrayOffset, null, vm.getIO().blockTransferRx[portno].ptr, copySize) } } diff --git a/src/net/torvald/tsvm/peripheral/IOSpace.kt b/src/net/torvald/tsvm/peripheral/IOSpace.kt index 2f724ac..85f48cc 100644 --- a/src/net/torvald/tsvm/peripheral/IOSpace.kt +++ b/src/net/torvald/tsvm/peripheral/IOSpace.kt @@ -18,14 +18,16 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor { /** Absolute y-position of the computer GUI */ var guiPosY = 0 + /** Accepts a keycode */ private val keyboardBuffer = CircularArray(32, true) - private var mouseX: Short = 0 - private var mouseY: Short = 0 - private var mouseDown = false - private var keyboardInputRequested = false - - internal val blockTransforBlock = arrayOf( + internal val blockTransferRx = arrayOf( + UnsafeHelper.allocate(4096), + UnsafeHelper.allocate(4096), + UnsafeHelper.allocate(4096), + UnsafeHelper.allocate(4096) + ) + internal val blockTransferTx = arrayOf( UnsafeHelper.allocate(4096), UnsafeHelper.allocate(4096), UnsafeHelper.allocate(4096), @@ -33,6 +35,8 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor { ) private val blockTransferPorts = Array(4) { BlockTransferPort(vm, it) } + private val keyEventBuffers = ByteArray(8) + init { blockTransferPorts[0].attachDevice(TestFunctionGenerator()) } @@ -71,17 +75,33 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor { 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 + 36L -> mouseDown.toInt().toByte() 37L -> keyboardBuffer.removeTail() ?: -1 - 38L -> if (keyboardInputRequested) 1 else 0 + 38L -> keyboardInputRequested.toInt().toByte() + 39L -> rawInputFunctionLatched.toInt().toByte() + in 40..47 -> keyEventBuffers[adi - 40] + in 64..67 -> vm.memsize.shr((adi - 64) * 8).toByte() + 68L -> (uptimeCounterLatched.toInt() or RTClatched.toInt().shl(1)).toByte() + + in 72..79 -> systemUptime.ushr((adi - 72) * 8).and(255).toByte() + in 80..87 -> rtc.ushr((adi - 80) * 8).and(255).toByte() + + 4084L -> (blockTransferPorts[0].yourBlockSize().toByte()) + 4085L -> (blockTransferPorts[0].doYouHaveNext().toInt().shl(7) or blockTransferPorts[0].yourBlockSize().ushr(8).and(15)).toByte() + 4086L -> (blockTransferPorts[1].yourBlockSize().toByte()) + 4087L -> (blockTransferPorts[1].doYouHaveNext().toInt().shl(7) or blockTransferPorts[1].yourBlockSize().ushr(8).and(15)).toByte() + 4088L -> (blockTransferPorts[2].yourBlockSize().toByte()) + 4089L -> (blockTransferPorts[2].doYouHaveNext().toInt().shl(7) or blockTransferPorts[2].yourBlockSize().ushr(8).and(15)).toByte() + 4090L -> (blockTransferPorts[3].yourBlockSize().toByte()) + 4091L -> (blockTransferPorts[4].doYouHaveNext().toInt().shl(7) or blockTransferPorts[3].yourBlockSize().ushr(8).and(15)).toByte() in 4092..4095 -> composeBlockTransferStatus(adi - 4092).toByte() - in 4096..8191 -> blockTransforBlock[0][addr - 4096] - in 8192..12287 -> blockTransforBlock[1][addr - 8192] - in 12288..16383 -> blockTransforBlock[2][addr - 12288] - in 16384..20479 -> blockTransforBlock[3][addr - 16384] + in 4096..8191 -> blockTransferRx[0][addr - 4096] + in 8192..12287 -> blockTransferRx[1][addr - 8192] + in 12288..16383 -> blockTransferRx[2][addr - 12288] + in 16384..20479 -> blockTransferRx[3][addr - 16384] in 131072..262143 -> vm.peripheralTable[1].peripheral?.mmio_read(addr - 131072) in 262144..393215 -> vm.peripheralTable[2].peripheral?.mmio_read(addr - 262144) @@ -101,15 +121,43 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor { when (addr) { 37L -> keyboardBuffer.appendHead(byte) 38L -> { - keyboardInputRequested = (byte != 0.toByte()) + keyboardInputRequested = (byte.isNonZero()) if (keyboardInputRequested) keyboardBuffer.clear() } + 39L -> rawInputFunctionLatched = (byte.isNonZero()) + in 40..47 -> keyEventBuffers[adi - 40] = byte + 68L -> { + uptimeCounterLatched = byte.and(0b01).isNonZero() + RTClatched = byte.and(0b10).isNonZero() + } + + 4084L -> blockTransferPorts[0].blockSize = blockTransferPorts[0].blockSize.and(0xFF00) or byte.toInt().and(255) + 4085L -> { + blockTransferPorts[0].hasNext = (byte < 0) + blockTransferPorts[0].blockSize = blockTransferPorts[0].blockSize.and(0x00FF) or byte.toInt().and(15) + } + 4086L -> blockTransferPorts[1].blockSize = blockTransferPorts[1].blockSize.and(0xFF00) or byte.toInt().and(255) + 4087L -> { + blockTransferPorts[1].hasNext = (byte < 0) + blockTransferPorts[1].blockSize = blockTransferPorts[1].blockSize.and(0x00FF) or byte.toInt().and(15) + } + 4088L -> blockTransferPorts[2].blockSize = blockTransferPorts[2].blockSize.and(0xFF00) or byte.toInt().and(255) + 4089L -> { + blockTransferPorts[2].hasNext = (byte < 0) + blockTransferPorts[2].blockSize = blockTransferPorts[2].blockSize.and(0x00FF) or byte.toInt().and(15) + } + 4090L -> blockTransferPorts[3].blockSize = blockTransferPorts[3].blockSize.and(0xFF00) or byte.toInt().and(255) + 4091L -> { + blockTransferPorts[3].hasNext = (byte < 0) + blockTransferPorts[3].blockSize = blockTransferPorts[3].blockSize.and(0x00FF) or byte.toInt().and(15) + } + in 4092..4095 -> setBlockTransferPortStatus(adi - 4092, byte) - in 4096..8191 -> blockTransforBlock[0][addr - 4096] = byte - in 8192..12287 -> blockTransforBlock[1][addr - 8192] = byte - in 12288..16383 -> blockTransforBlock[2][addr - 12288] = byte - in 16384..20479 -> blockTransforBlock[3][addr - 16384] = byte + in 4096..8191 -> blockTransferTx[0][addr - 4096] = byte + in 8192..12287 -> blockTransferTx[1][addr - 8192] = byte + in 12288..16383 -> blockTransferTx[2][addr - 12288] = byte + in 16384..20479 -> blockTransferTx[3][addr - 16384] = byte in 131072..262143 -> vm.peripheralTable[1].peripheral?.mmio_write(addr - 131072, byte) in 262144..393215 -> vm.peripheralTable[2].peripheral?.mmio_write(addr - 262144, byte) @@ -122,13 +170,47 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor { } override fun dispose() { - blockTransforBlock.forEach { it.destroy() } + blockTransferRx.forEach { it.destroy() } + blockTransferTx.forEach { it.destroy() } } + private var mouseX: Short = 0 + private var mouseY: Short = 0 + private var mouseDown = false + private var systemUptime = 0L + private var rtc = 0L + fun update(delta: Float) { - mouseX = (Gdx.input.x + guiPosX).toShort() - mouseY = (Gdx.input.y + guiPosY).toShort() - mouseDown = Gdx.input.isTouched + if (rawInputFunctionLatched) { + rawInputFunctionLatched = false + + // store mouse info + mouseX = (Gdx.input.x + guiPosX).toShort() + mouseY = (Gdx.input.y + guiPosY).toShort() + mouseDown = Gdx.input.isTouched + + // strobe keys to fill the key read buffer + var keysPushed = 0 + keyEventBuffers.fill(0) + for (k in 1..254) { + if (Gdx.input.isKeyPressed(k)) { + keyEventBuffers[keysPushed] = k.toByte() + keysPushed += 1 + } + + if (keysPushed >= 8) break + } + } + + if (uptimeCounterLatched) { + uptimeCounterLatched = false + systemUptime = vm.getUptime() + } + + if (RTClatched) { + RTClatched = false + rtc = System.currentTimeMillis() + } } override fun touchUp(p0: Int, p1: Int, p2: Int, p3: Int): Boolean { @@ -140,7 +222,8 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor { } override fun keyTyped(p0: Char): Boolean { - if (keyboardInputRequested) { + if (keyboardInputRequested && !ttySpecialKeyLatched && p0.toInt() > 0) { + println("[IO] Key typed: ${p0.toInt()}") keyboardBuffer.appendHead(p0.toByte()) return true } @@ -154,15 +237,28 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor { } override fun keyUp(p0: Int): Boolean { - return false + ttySpecialKeyLatched = false + return true } override fun touchDragged(p0: Int, p1: Int, p2: Int): Boolean { return false } + private var keyboardInputRequested = false + private var uptimeCounterLatched = false + private var RTClatched = false + private var rawInputFunctionLatched = false + private var ttySpecialKeyLatched = false + // UP DN LE RI + private var specialKey = listOf(19,20,21,22).toSortedSet() override fun keyDown(p0: Int): Boolean { - return false + if (keyboardInputRequested && !ttySpecialKeyLatched && p0 in specialKey) { + println("[IO] Key down: $p0") + keyboardBuffer.appendHead(p0.toByte()) + } + + return true } override fun touchDown(p0: Int, p1: Int, p2: Int, p3: Int): Boolean { @@ -170,4 +266,5 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor { } private fun Boolean.toInt() = if (this) 1 else 0 -} \ No newline at end of file + private fun Byte.isNonZero() = this != 0.toByte() +} diff --git a/src/net/torvald/tsvm/peripheral/SerialDiskDrive.kt b/src/net/torvald/tsvm/peripheral/SerialDiskDrive.kt new file mode 100644 index 0000000..98065f6 --- /dev/null +++ b/src/net/torvald/tsvm/peripheral/SerialDiskDrive.kt @@ -0,0 +1,14 @@ +package net.torvald.tsvm.peripheral + +import java.util.* + +class SerialDiskDrive : BlockTransferInterface(false, true) { + + override fun hasNext(): Boolean { + TODO("Not yet implemented") + } + + val diskID: UUID = UUID(0, 0) + + +} \ No newline at end of file diff --git a/src/net/torvald/tsvm/peripheral/TestFunctionGenerator.kt b/src/net/torvald/tsvm/peripheral/TestFunctionGenerator.kt index 710267f..6df2d86 100644 --- a/src/net/torvald/tsvm/peripheral/TestFunctionGenerator.kt +++ b/src/net/torvald/tsvm/peripheral/TestFunctionGenerator.kt @@ -18,6 +18,8 @@ Nunc mollis nibh vitae sapien consequat, ut vestibulum sem pharetra. Aliquam iac super.startSend { it.writeout(msg) } } + override fun hasNext(): Boolean = false + override fun writeout(inputData: ByteArray, writeoutfun: (() -> Unit)?) { } diff --git a/terranmon.txt b/terranmon.txt index f94d864..bdf84aa 100644 --- a/terranmon.txt +++ b/terranmon.txt @@ -42,6 +42,10 @@ IO Device Endianness: little Note: Always takes up the peripheral slot of zero +Latching: latching is used to "lock" the fluctuating values when you attempt to read them so you would get + reliable values when you try to read them, especially the multibyte values where another byte would + change after you read one byte, e.g. System uptime in nanoseconds + MMIO 0..31 RO: Raw Keyboard Buffer read. Won't shift the key buffer @@ -51,11 +55,37 @@ MMIO 37 RW: 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. -38 RW: Request keyboard input be read. Write nonzero value to enable, write zero to + Stores ASCII code representing the character, plus: + 19: Up arrow + 20: Down arrow + 21: Left arrow + 22: Right arrow +38 RW: Request keyboard input be read (TTY Function). Write nonzero value to enable, write zero to close it. Keyboard buffer will be cleared whenever request is received, so MAKE SURE YOU REQUEST THE KEY INPUT ONLY ONCE! +39 WO: Latch Key/Mouse Input (Raw Input function). Write nonzero value to latch. + Stores LibGDX Key code +40..47 RO: Key Press buffer + stores keys that are held down. Can accomodate 8-key rollover (in keyboard geeks' terms) 64..67 RO: User area memory size in bytes +68 WO: Counter latch + 0b 0000 00ba + a: System uptime + b: RTC +72..79 RO: System uptime in nanoseconds +80..87 RO: RTC in microseconds + +4084..4091 RO: Block transfer status + 0b nnnnnnnn a000 mmmm + + n-read: size of the block from the other device, LSB (4096-full block size is zero) + m-read: size of the block from the other device, MSB (4096-full block size is zero) + a-read: if the other device hasNext (doYouHaveNext), false if device not present + + n-write: size of the block I'm sending, LSB (4096-full block size is zero) + m-write: size of the block I'm sending, MSB (4096-full block size is zero) + a-write: if there's more to send (hasNext) 4092..4095 RW: Block transfer control for Port 1 through 4 0b 00ms abcd