diff --git a/src/net/torvald/tsvm/UnsafePtr.kt b/src/net/torvald/tsvm/UnsafePtr.kt index 3a4d413..1562fe5 100644 --- a/src/net/torvald/tsvm/UnsafePtr.kt +++ b/src/net/torvald/tsvm/UnsafePtr.kt @@ -114,6 +114,11 @@ internal class UnsafePtr(pointer: Long, allocSize: Long) { return UnsafeHelper.unsafe.getInt(ptr + index) } + fun getShort(index: Long): Short { + checkNullPtr(index) + return UnsafeHelper.unsafe.getShort(ptr + index) + } + fun setFloat(index: Long, value: Float) { checkNullPtr(index) UnsafeHelper.unsafe.putFloat(ptr + index, value) @@ -124,6 +129,11 @@ internal class UnsafePtr(pointer: Long, allocSize: Long) { UnsafeHelper.unsafe.putInt(ptr + index, value) } + fun setShort(index: Long, value: Short) { + checkNullPtr(index) + UnsafeHelper.unsafe.putShort(ptr + index, value) + } + fun fillWith(byte: Byte) { UnsafeHelper.unsafe.setMemory(ptr, size, byte) } diff --git a/src/net/torvald/tsvm/VM.kt b/src/net/torvald/tsvm/VM.kt index 00bc81c..72bfc49 100644 --- a/src/net/torvald/tsvm/VM.kt +++ b/src/net/torvald/tsvm/VM.kt @@ -7,6 +7,7 @@ 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 kotlin.random.Random /** * 1 byte = 2 pixels @@ -51,6 +52,8 @@ class VM( _memsize: Int ) { + val id = java.util.Random().nextInt() + val memsize = minOf(USER_SPACE_SIZE, _memsize.toLong()) internal val usermem = UnsafeHelper.allocate(memsize) @@ -65,6 +68,8 @@ class VM( MMIO_SIZE.toInt() - 256, 64 ) + + println("[VM] Creating new VM with ID of $id, memesize $memsize") } diff --git a/src/net/torvald/tsvm/VMGUI.kt b/src/net/torvald/tsvm/VMGUI.kt index db1f675..149bafe 100644 --- a/src/net/torvald/tsvm/VMGUI.kt +++ b/src/net/torvald/tsvm/VMGUI.kt @@ -11,12 +11,13 @@ import kotlin.math.roundToInt class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter() { val vm = VM(8192) - val vmLua = VMLuaAdapter(vm) lateinit var gpu: GraphicsAdapter lateinit var batch: SpriteBatch lateinit var camera: OrthographicCamera + lateinit var vmRunner: VMRunner + override fun create() { super.create() @@ -37,9 +38,11 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter() batch.projectionMatrix = camera.combined Gdx.gl20.glViewport(0, 0, appConfig.width, appConfig.height) + vmRunner = VMRunnerFactory(vm, "lua") // TEST LUA PRG //vmLua.lua.load(gpuTestPalette).call() + vmRunner.executeCommand(gpuTestPalette) } private var updateAkku = 0.0 @@ -60,18 +63,19 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter() i += 1 } - renderGame() + renderGame(dt) } + private fun updateGame(delta: Float) { - paintTestPalette() + } private fun paintTestPalette() { val peripheralSlot = vm.findPeribyType(VM.PERITYPE_GRAPHICS)!! val hwoff = VM.HW_RESERVE_SIZE * peripheralSlot - for (y in 0 until 360) { + /*for (y in 0 until 360) { for (x in 0 until GraphicsAdapter.WIDTH) { val palnum = 20 * (y / 30) + (x / (GraphicsAdapter.WIDTH / 20)) vm.poke(-(y.toLong() * GraphicsAdapter.WIDTH + x + 1) - hwoff, palnum.toByte()) @@ -83,47 +87,57 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter() val palnum = 240 + (x / 35) vm.poke(-(y.toLong() * GraphicsAdapter.WIDTH + x + 1) - hwoff, palnum.toByte()) } - } + }*/ //vm.poke(-262143L - hwoff, Math.random().times(255.0).toByte()) //vm.poke(-262144L - hwoff, Math.random().times(255.0).toByte()) for (k in 0 until 2240) { // text foreground - vm.poke(-(254912 + k + 1) - hwoff, (Math.random().times(255f).roundToInt()).toByte()) // white + vm.poke(-(254912 + k + 1) - hwoff, -2) // white // text background - vm.poke(-(254912 + 2240 + k + 1) - hwoff, (Math.random().times(255f).roundToInt()).toByte()) // transparent + vm.poke(-(254912 + 2240 + k + 1) - hwoff, -1) // transparent // texts - vm.poke(-(254912 + 2240*2 + k + 1) - hwoff, (Math.random().times(255f).roundToInt()).toByte()) + vm.poke(-(254912 + 2240*2 + k + 1) - hwoff, if (k == 0) 0x30 else 0x40) } } - private val gpuTestPalette = """ - local vm = require("rawmem") - local w = 560 - local h = 448 - local hwoff = 1048576 +local vm = require("rawmem") +local w = 560 +local h = 448 +local hwoff = 1048576 - for y = 0, 359 do - for x = 0, w - 1 do - palnum = 20 * int(y / 30) + int(x / 28) - vm.poke(-(y * w + x + 1) - hwoff, palnum) - end +while true do + local tstart = os.clock() + + for y = 0, 359 do + for x = 0, w - 1 do + palnum = 20 * int(y / 30) + int(x / 28) + vm.poke(-(y * w + x + 1) - hwoff, palnum) end + end - for y = 360, h - 1 do - for x = 0, w - 1 do - palnum = 240 + int(x / 35) - vm.poke(-(y * w + x + 1) - hwoff, palnum) - end + for y = 360, h - 1 do + for x = 0, w - 1 do + palnum = 240 + int(x / 35) + vm.poke(-(y * w + x + 1) - hwoff, palnum) end + end - vm.poke(-262143 - hwoff, math.floor(math.random() * 255.0)) - vm.poke(-262144 - hwoff, math.floor(math.random() * 255.0)) + for k = 0, 2239 do + vm.poke(-(254912 + k + 1) - hwoff, 254) + vm.poke(-(254912 + 2240 + k + 1) - hwoff, 255) + vm.poke(-(254912 + 2240*2 + k + 1) - hwoff, math.floor(math.random() * 255.0)) + end + + local tend = os.clock() + + print("Apparent FPS: "..tostring(1.0 / (tend - tstart))) +end """.trimIndent() - private fun renderGame() { - gpu.render(batch, 0f, 0f) + private fun renderGame(delta: Float) { + gpu.render(delta, batch, 0f, 0f) } diff --git a/src/net/torvald/tsvm/VMRunnerFactory.kt b/src/net/torvald/tsvm/VMRunnerFactory.kt new file mode 100644 index 0000000..0730caf --- /dev/null +++ b/src/net/torvald/tsvm/VMRunnerFactory.kt @@ -0,0 +1,30 @@ +package net.torvald.tsvm + +abstract class VMRunner(val extension: String) { + + var thread = Thread() + abstract fun executeCommand(command: String) + +} + +object VMRunnerFactory { + + operator fun invoke(vm: VM, extension: String): VMRunner { + return when (extension) { + "lua" -> { + object : VMRunner(extension) { + private val vmLua = VMLuaAdapter(vm) + + override fun executeCommand(command: String) { + thread = Thread { + vmLua.lua.load(command).call() + } + thread.start() + } + } + } + else -> throw UnsupportedOperationException("Unsupported script extension: $extension") + } + } + +} \ No newline at end of file diff --git a/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt b/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt index eb45d25..7654e03 100644 --- a/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt +++ b/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt @@ -16,7 +16,7 @@ class GraphicsAdapter : PeriBase { internal val framebuffer = Pixmap(WIDTH, HEIGHT, Pixmap.Format.RGBA8888) private var rendertex = Texture(1, 1, Pixmap.Format.RGBA8888) - private val paletteOfFloats = FloatArray(1024) { + internal val paletteOfFloats = FloatArray(1024) { val rgba = DEFAULT_PALETTE[it / 4] val channel = it % 4 rgba.shr((3 - channel) * 8).and(255) / 255f @@ -34,7 +34,11 @@ class GraphicsAdapter : PeriBase { private var graphicsUseSprites = false private var lastUsedColour = (-1).toByte() private var currentChrRom = 0 + private var chrWidth = 8f + private var chrHeight = 14f + private var ttyFore = 254 + private var ttyBack = 255 private val textForePixmap = Pixmap(TEXT_COLS, TEXT_ROWS, Pixmap.Format.RGBA8888) private val textBackPixmap = Pixmap(TEXT_COLS, TEXT_ROWS, Pixmap.Format.RGBA8888) @@ -44,7 +48,7 @@ class GraphicsAdapter : PeriBase { private var textBackTex = Texture(textBackPixmap) private var textTex = Texture(textPixmap) - + private fun getTtyCursorPos() = spriteAndTextArea.getShort(3938L) % TEXT_COLS to spriteAndTextArea.getShort(3938L) / TEXT_COLS init { framebuffer.blending = Pixmap.Blending.None @@ -57,6 +61,8 @@ class GraphicsAdapter : PeriBase { pm.drawPixel(0, 0, -1) faketex = Texture(pm) pm.dispose() + + spriteAndTextArea.fillWith(0) } override fun peek(addr: Long): Byte? { @@ -143,7 +149,11 @@ class GraphicsAdapter : PeriBase { chrrom0.dispose() } - fun render(batch: SpriteBatch, x: Float, y: Float) { + private var textCursorBlinkTimer = 0f + private val textCursorBlinkInterval = 0.5f + private var textCursorIsOn = true + + fun render(delta: Float, batch: SpriteBatch, x: Float, y: Float) { rendertex.dispose() rendertex = Texture(framebuffer) @@ -163,16 +173,13 @@ class GraphicsAdapter : PeriBase { // feed palette data // must be done every time the shader is "actually loaded" // try this: if above line precedes 'batch.shader = paletteShader', it won't work - paletteShader.setUniform4fv("pal", paletteOfFloats, 0, paletteOfFloats.size) + batch.shader.setUniform4fv("pal", paletteOfFloats, 0, paletteOfFloats.size) // draw framebuffer batch.draw(rendertex, x, y) - batch.end() - // draw texts or sprites - batch.begin() batch.color = Color.WHITE @@ -222,6 +229,19 @@ class GraphicsAdapter : PeriBase { textShader.setUniformf("atlasTexSize", chrrom0.width.toFloat(), chrrom0.height.toFloat()) batch.draw(faketex, 0f, 0f, WIDTH.toFloat(), HEIGHT.toFloat()) + + batch.shader = null + + if (textCursorIsOn) { + batch.color = Color( + paletteOfFloats[4 * ttyFore], + paletteOfFloats[4 * ttyFore + 1], + paletteOfFloats[4 * ttyFore + 2], + paletteOfFloats[4 * ttyFore + 3] + ) + val (cursorx, cursory) = getTtyCursorPos() + batch.draw(faketex, cursorx * chrWidth, (TEXT_ROWS - cursory - 1) * chrHeight, chrWidth, chrHeight) + } } else { // draw sprites @@ -238,11 +258,19 @@ class GraphicsAdapter : PeriBase { batch.end() batch.shader = null + + textCursorBlinkTimer += delta + if (textCursorBlinkTimer > textCursorBlinkInterval) { + textCursorBlinkTimer -= 0.5f + textCursorIsOn = !textCursorIsOn + } } private fun peekPalette(offset: Int): Byte { if (offset == 255) return 0 // palette 255 is always transparent + // FIXME always return zero? + val highvalue = paletteOfFloats[offset * 2] // R, B val lowvalue = paletteOfFloats[offset * 2 + 1] // G, A return (highvalue.div(15f).toInt().shl(4) or lowvalue.div(15f).toInt()).toByte() @@ -322,7 +350,7 @@ uniform sampler2D u_texture; uniform vec2 screenDimension; -uniform vec2 tilesInAxes; // basically a screen dimension; vec2(tiles_in_horizontal, tiles_in_vertical) +uniform vec2 tilesInAxes; // size of the tilemap texture; vec2(tiles_in_horizontal, tiles_in_vertical) uniform sampler2D tilesAtlas; uniform sampler2D foreColours; @@ -358,14 +386,13 @@ void main() { // default gl_FragCoord takes half-integer (represeting centre of the pixel) -- could be useful for phys solver? // This one, however, takes exact integer by rounding down. // - vec2 overscannedScreenDimension = tilesInAxes * tileSizeInPx; // how many tiles will fit into a screen; one used by the tileFromMap vec2 flippedFragCoord = vec2(gl_FragCoord.x, screenDimension.y - gl_FragCoord.y); // NO IVEC2!!; this flips Y // get required tile numbers // - vec4 tileFromMap = texture2D(tilemap, flippedFragCoord / overscannedScreenDimension); // raw tile number - vec4 foreColFromMap = texture2D(foreColours, flippedFragCoord / overscannedScreenDimension); - vec4 backColFromMap = texture2D(backColours, flippedFragCoord / overscannedScreenDimension); + vec4 tileFromMap = texture2D(tilemap, flippedFragCoord / screenDimension); // raw tile number + vec4 foreColFromMap = texture2D(foreColours, flippedFragCoord / screenDimension); + vec4 backColFromMap = texture2D(backColours, flippedFragCoord / screenDimension); int tile = getTileFromColor(tileFromMap); ivec2 tileXY = getTileXY(tile); diff --git a/terranmon.txt b/terranmon.txt index 832b1cc..85f32e9 100644 --- a/terranmon.txt +++ b/terranmon.txt @@ -39,6 +39,8 @@ User area: 8 MB, hardware area: 8 MB VRAM Bank 0 (256 kB) +Endianness: little + From the start of the memory space: 250880 bytes Framebuffer @@ -87,6 +89,10 @@ MMIO 0b 0000 000g (g: Use sprites(wipes out text buffer)) 1 bytes RO Last used colour (set by poking at the framebuffer) +1 bytes RW + current TTY foreground colour (useful for print() function) +1 bytes RW + current TTY background colour (useful for print() function) Text-mode-font-ROM is immutable and does not belong to VRAM Even in the text mode framebuffer is still being drawn onto the screen, and the texts are drawn on top of it \ No newline at end of file