diff --git a/tsvm_executable/src/net/torvald/terrarum/FlippingSpriteBatch.kt b/tsvm_executable/src/net/torvald/terrarum/FlippingSpriteBatch.kt new file mode 100644 index 0000000..8f36a5c --- /dev/null +++ b/tsvm_executable/src/net/torvald/terrarum/FlippingSpriteBatch.kt @@ -0,0 +1,52 @@ +package net.torvald.terrarum + +import com.badlogic.gdx.graphics.Texture +import com.badlogic.gdx.graphics.g2d.SpriteBatch +import com.badlogic.gdx.graphics.g2d.TextureRegion + +/** + * Don't flip the assets! Flip the draw command instead! + * + * Created by minjaesong on 2021-12-13. + */ +class FlippingSpriteBatch : SpriteBatch() { + + /** + * This function draws the flipped version of the image by giving flipped uv-coord to the SpriteBatch + */ + override fun draw(texture: Texture, x: Float, y: Float, width: Float, height: Float) = + draw(texture, x, y, width, height, 0f, 0f, 1f, 1f) + + override fun draw(texture: Texture, x: Float, y: Float) = + draw(texture, x, y, texture.width.toFloat(), texture.height.toFloat(), 0f, 0f, 1f, 1f) + + fun drawFlipped(texture: Texture, x: Float, y: Float, width: Float, height: Float) = + draw(texture, x, y, width, height, 0f, 1f, 1f, 0f) + fun drawFlipped(texture: Texture, x: Float, y: Float) = + draw(texture, x, y, texture.width.toFloat(), texture.height.toFloat(), 0f, 1f, 1f, 0f) + + + /** + * This function does obey the flipping set to the TextureRegion and try to draw flipped version of it, + * without touching the flipping setting of the given region. + */ + override fun draw(region: TextureRegion, x: Float, y: Float, width: Float, height: Float) = + draw(region.texture, x, y, width, height, region.u, region.v, region.u2, region.v2) + + override fun draw(region: TextureRegion, x: Float, y: Float) = + draw(region.texture, x, y, region.regionWidth.toFloat(), region.regionHeight.toFloat(), region.u, region.v, region.u2, region.v2) + + fun drawFlipped(region: TextureRegion, x: Float, y: Float, width: Float, height: Float) = + draw(region.texture, x, y, width, height, region.u, region.v2, region.u2, region.v) + fun drawFlipped(region: TextureRegion, x: Float, y: Float) = + draw(region.texture, x, y, region.regionWidth.toFloat(), region.regionHeight.toFloat(), region.u, region.v2, region.u2, region.v) + + + /** + * NOTE TO SELF: + * + * It seems that original SpriteBatch Y-flips when it's drawing a texture, but NOT when it's drawing a textureregion + * + * (textureregion's default uv-coord is (0,0,1,1) + */ +} \ No newline at end of file diff --git a/tsvm_executable/src/net/torvald/terrarum/imagefont/TinyAlphNum.kt b/tsvm_executable/src/net/torvald/terrarum/imagefont/TinyAlphNum.kt new file mode 100644 index 0000000..807e03f --- /dev/null +++ b/tsvm_executable/src/net/torvald/terrarum/imagefont/TinyAlphNum.kt @@ -0,0 +1,106 @@ +package net.torvald.terrarum.imagefont + +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.graphics.Color +import com.badlogic.gdx.graphics.g2d.Batch +import com.badlogic.gdx.graphics.g2d.BitmapFont +import com.badlogic.gdx.graphics.g2d.GlyphLayout +import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack +import kotlin.math.roundToInt + +/** + * Created by minjaesong on 2016-04-15. + */ +object TinyAlphNum : BitmapFont() { + + internal val W = 7 + internal val H = 13 + + internal val fontSheet = TextureRegionPack(Gdx.files.internal("net/torvald/terrarum/imagefont/font.tga"), W, H) + + + init { + setOwnsTexture(true) + setUseIntegerPositions(true) + } + + fun getWidth(str: String): Int { + var l = 0 + for (char in str) { + if (!isColourCodeHigh(char) && !isColourCodeLow(char)) { + l += 1 + } + } + return W * l + } + + lateinit var colMain: Color + lateinit var colShadow: Color + + override fun draw(batch: Batch, text: CharSequence, x: Float, y: Float): GlyphLayout? { + val originalColour = batch.color.cpy() + colMain = batch.color.cpy() + colShadow = colMain.cpy().mul(0.5f, 0.5f, 0.5f, 1f) + + val x = x.roundToInt().toFloat() + val y = y.roundToInt().toFloat() + + var charsPrinted = 0 + text.forEachIndexed { index, c -> + if (isColourCodeHigh(c)) { + val cchigh = c + val cclow = text[index + 1] + val colour = getColour(cchigh, cclow) + + colMain = colour + colShadow = colMain.cpy().mul(0.5f, 0.5f, 0.5f, 1f) + } + else if (c in 0.toChar()..255.toChar()) { + batch.color = colShadow + batch.draw(fontSheet.get(c.toInt() % 16, c.toInt() / 16), x + charsPrinted * W + 1, y) + batch.draw(fontSheet.get(c.toInt() % 16, c.toInt() / 16), x + charsPrinted * W, y + 1) + batch.draw(fontSheet.get(c.toInt() % 16, c.toInt() / 16), x + charsPrinted * W + 1, y + 1) + + + batch.color = colMain + batch.draw(fontSheet.get(c.toInt() % 16, c.toInt() / 16), x + charsPrinted * W, y) + + charsPrinted += 1 + } + } + + + batch.color = originalColour + + return null + } + + override fun getLineHeight() = H.toFloat() + override fun getCapHeight() = getLineHeight() + override fun getXHeight() = getLineHeight() + + + + private fun isColourCodeHigh(c: Char) = c.toInt() in 0b110110_1111000000..0b110110_1111111111 + private fun isColourCodeLow(c: Char) = c.toInt() in 0b110111_0000000000..0b110111_1111111111 + + private fun getColour(charHigh: Char, charLow: Char): Color { // input: 0x10ARGB, out: RGBA8888 + val codePoint = Character.toCodePoint(charHigh, charLow) + + if (colourBuffer.containsKey(codePoint)) + return colourBuffer[codePoint]!! + + val a = codePoint.and(0xF000).ushr(12) + val r = codePoint.and(0x0F00).ushr(8) + val g = codePoint.and(0x00F0).ushr(4) + val b = codePoint.and(0x000F) + + val col = Color(r.shl(28) or r.shl(24) or g.shl(20) or g.shl(16) or b.shl(12) or b.shl(8) or a.shl(4) or a) + + + colourBuffer[codePoint] = col + return col + } + + private val colourBuffer = HashMap() +} \ No newline at end of file diff --git a/tsvm_executable/src/net/torvald/terrarum/imagefont/font.tga b/tsvm_executable/src/net/torvald/terrarum/imagefont/font.tga new file mode 100644 index 0000000..778d86f --- /dev/null +++ b/tsvm_executable/src/net/torvald/terrarum/imagefont/font.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9458daf7338a9f734a90cb3babdcb07249e2f1ab7997e93d05ef8eb64099254e +size 93202 diff --git a/tsvm_executable/src/net/torvald/terrarumsansbitmap/gdx/TextureRegionPack.kt b/tsvm_executable/src/net/torvald/terrarumsansbitmap/gdx/TextureRegionPack.kt new file mode 100644 index 0000000..f8b71f3 --- /dev/null +++ b/tsvm_executable/src/net/torvald/terrarumsansbitmap/gdx/TextureRegionPack.kt @@ -0,0 +1,103 @@ +/* + * Terrarum Sans Bitmap + * + * Copyright (c) 2017-2021 Minjae Song (Torvald) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.torvald.terrarumsansbitmap.gdx + +import com.badlogic.gdx.files.FileHandle +import com.badlogic.gdx.graphics.Texture +import com.badlogic.gdx.graphics.g2d.TextureRegion +import com.badlogic.gdx.utils.Disposable + +/** + * Created by minjaesong on 2017-06-15. + */ +class TextureRegionPack( + val texture: Texture, + val tileW: Int, + val tileH: Int, + val hGap: Int = 0, + val vGap: Int = 0, + val hFrame: Int = 0, + val vFrame: Int = 0, + val xySwapped: Boolean = false, // because Unicode chart does, duh + val flipX: Boolean = false, + val flipY: Boolean = false +): Disposable { + + constructor(ref: String, tileW: Int, tileH: Int, hGap: Int = 0, vGap: Int = 0, hFrame: Int = 0, vFrame: Int = 0, xySwapped: Boolean = false, flipX: Boolean = false, flipY: Boolean = false) : + this(Texture(ref), tileW, tileH, hGap, vGap, hFrame, vFrame, xySwapped, flipX, flipY) + constructor(fileHandle: FileHandle, tileW: Int, tileH: Int, hGap: Int = 0, vGap: Int = 0, hFrame: Int = 0, vFrame: Int = 0, xySwapped: Boolean = false, flipX: Boolean = false, flipY: Boolean = false) : + this(Texture(fileHandle), tileW, tileH, hGap, vGap, hFrame, vFrame, xySwapped, flipX, flipY) + + companion object { + + } + + val regions: Array + + val horizontalCount = (texture.width - 2 * hFrame + hGap) / (tileW + hGap) + val verticalCount = (texture.height - 2 * vFrame + vGap) / (tileH + vGap) + + init { + //println("texture: $texture, dim: ${texture.width} x ${texture.height}, grid: $horizontalCount x $verticalCount, cellDim: $tileW x $tileH") + + if (!xySwapped) { + regions = Array(horizontalCount * verticalCount) { + val region = TextureRegion() + val rx = (it % horizontalCount * (tileW + hGap)) + hFrame + val ry = (it / horizontalCount * (tileH + vGap)) + vFrame + + region.setRegion(texture) + region.setRegion(rx, ry, tileW, tileH) + + region.flip(flipX, flipY) + + /*return*/region + } + } + else { + regions = Array(horizontalCount * verticalCount) { + val region = TextureRegion() + val rx = (it / verticalCount * (tileW + hGap)) + hFrame + val ry = (it % verticalCount * (tileH + vGap)) + vFrame + + region.setRegion(texture) + region.setRegion(rx, ry, tileW, tileH) + + region.flip(flipX, flipY) + + /*return*/region + } + } + } + + fun get(x: Int, y: Int) = regions[y * horizontalCount + x] + + fun forEach(action: (TextureRegion) -> Unit) = regions.forEach(action) + + override fun dispose() { + texture.dispose() + } + +} \ No newline at end of file diff --git a/tsvm_executable/src/net/torvald/tsvm/AppLoader.java b/tsvm_executable/src/net/torvald/tsvm/AppLoader.java index 0353f62..be0a5d8 100644 --- a/tsvm_executable/src/net/torvald/tsvm/AppLoader.java +++ b/tsvm_executable/src/net/torvald/tsvm/AppLoader.java @@ -14,8 +14,8 @@ public class AppLoader { public static String appTitle = "tsvm"; public static Lwjgl3ApplicationConfiguration appConfig; - public static int WIDTH = 1080;//640; - public static int HEIGHT = 436;//480; + public static int WIDTH = 1280;//1080;//640; + public static int HEIGHT = 960;//436;//480; public static void main(String[] args) { ShaderProgram.pedantic = false; @@ -62,6 +62,6 @@ public class AppLoader { pipvm, 160, 140 ))));*/ - new Lwjgl3Application(new VMGUI(portable, WIDTH, HEIGHT), appConfig); + new Lwjgl3Application(new VMGUI(reference, 640, 480), appConfig); } } diff --git a/tsvm_executable/src/net/torvald/tsvm/TsvmEmulator.java b/tsvm_executable/src/net/torvald/tsvm/TsvmEmulator.java new file mode 100644 index 0000000..32188d3 --- /dev/null +++ b/tsvm_executable/src/net/torvald/tsvm/TsvmEmulator.java @@ -0,0 +1,37 @@ +package net.torvald.tsvm; + +import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application; +import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration; +import com.badlogic.gdx.graphics.glutils.ShaderProgram; +import net.torvald.tsvm.peripheral.*; + +import java.io.File; + +/** + * Created by minjaesong on 2022-10-22. + */ +public class TsvmEmulator { + + + public static String appTitle = "tsvm"; + public static Lwjgl3ApplicationConfiguration appConfig; + + public static int WIDTH = 640 * 2; + public static int HEIGHT = 480 * 2; + + public static void main(String[] args) { + ShaderProgram.pedantic = false; + + appConfig = new Lwjgl3ApplicationConfiguration(); + appConfig.setIdleFPS(60); + appConfig.setForegroundFPS(60); + appConfig.useVsync(false); + appConfig.setResizable(false); + appConfig.setTitle(appTitle); + + appConfig.setWindowedMode(WIDTH, HEIGHT); + + new Lwjgl3Application(new VMEmuExecutable(640, 480, 2, 2,"assets/"), appConfig); + } + +} diff --git a/tsvm_executable/src/net/torvald/tsvm/VMEmuExecutable.kt b/tsvm_executable/src/net/torvald/tsvm/VMEmuExecutable.kt new file mode 100644 index 0000000..cd23f48 --- /dev/null +++ b/tsvm_executable/src/net/torvald/tsvm/VMEmuExecutable.kt @@ -0,0 +1,295 @@ +package net.torvald.tsvm + +import com.badlogic.gdx.ApplicationAdapter +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.graphics.* +import com.badlogic.gdx.graphics.g2d.SpriteBatch +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.cancel +import kotlinx.coroutines.launch +import net.torvald.terrarum.FlippingSpriteBatch +import net.torvald.terrarum.imagefont.TinyAlphNum +import net.torvald.tsvm.peripheral.GraphicsAdapter +import net.torvald.tsvm.peripheral.ReferenceGraphicsAdapter2 +import net.torvald.tsvm.peripheral.TestDiskDrive +import net.torvald.tsvm.peripheral.TsvmBios +import java.io.File +import java.util.* +import kotlin.collections.HashMap + +/** + * Created by minjaesong on 2022-10-22. + */ +class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX: Int, var panelsY: Int, val diskPathRoot: String) : ApplicationAdapter() { + + private data class VMRunnerInfo(val vm: VM, val name: String) + + private val vms = arrayOfNulls(this.panelsX * this.panelsY - 1) // index: # of the window where the reboot was requested + + private var currentVMselection = 0 + + lateinit var batch: SpriteBatch + lateinit var fbatch: FlippingSpriteBatch + lateinit var camera: OrthographicCamera + + var vmRunners = HashMap() // + var coroutineJobs = HashMap() // + + lateinit var fullscreenQuad: Mesh + + private lateinit var sqtex: Texture + + private lateinit var font: TinyAlphNum + + override fun create() { + super.create() + + sqtex = Texture(Gdx.files.internal("net/torvald/tsvm/sq.tga")) + + font = TinyAlphNum + + fullscreenQuad = Mesh( + true, 4, 6, + VertexAttribute.Position(), + VertexAttribute.ColorUnpacked(), + VertexAttribute.TexCoords(0) + ) + updateFullscreenQuad(AppLoader.WIDTH, AppLoader.HEIGHT) + + batch = SpriteBatch() + fbatch = FlippingSpriteBatch() + camera = OrthographicCamera(AppLoader.WIDTH.toFloat(), AppLoader.HEIGHT.toFloat()) + camera.setToOrtho(true) + camera.update() + batch.projectionMatrix = camera.combined + fbatch.projectionMatrix = camera.combined + + + // install the default VM on slot 0 + val vm = VM("./assets", 8192 shl 10, TheRealWorld(), arrayOf(TsvmBios), 8) + vm.getIO().blockTransferPorts[0].attachDevice(TestDiskDrive(vm, 0, File("assets/disk0"))) + initVMenv(vm) + vms[0] = VMRunnerInfo(vm, "Initial VM") + + val vm2 = VM("./assets", 64 shl 10, TheRealWorld(), arrayOf(TsvmBios), 8) + vm2.getIO().blockTransferPorts[0].attachDevice(TestDiskDrive(vm2, 0, File("assets/disk0"))) + initVMenv(vm2) + vms[1] = VMRunnerInfo(vm2, "Initial VM2") + + init() + } + + private fun init() { + changeActiveSession(0) + } + + private fun changeActiveSession(index: Int) { + currentVMselection = index + // TODO somehow implement the inputstream that cares about the currentVMselection + Gdx.input.inputProcessor = vms[currentVMselection]?.vm?.getIO() + } + + private fun initVMenv(vm: VM) { + vm.peripheralTable.getOrNull(1)?.peripheral?.dispose() + + val gpu = ReferenceGraphicsAdapter2("./assets", vm) + vm.peripheralTable[1] = PeripheralEntry(gpu, GraphicsAdapter.VRAM_SIZE, 16, 0) + + vm.getPrintStream = { gpu.getPrintStream() } + vm.getErrorStream = { gpu.getErrorStream() } + vm.getInputStream = { gpu.getInputStream() } + + vmRunners[vm.id] = VMRunnerFactory(vm.assetsDir, vm, "js") + coroutineJobs[vm.id] = GlobalScope.launch { vmRunners[vm.id]?.executeCommand(vm.roms[0]!!.readAll()) } + } + + private fun setCameraPosition(newX: Float, newY: Float) { + camera.position.set((-newX + AppLoader.WIDTH / 2), (-newY + AppLoader.HEIGHT / 2), 0f) // deliberate integer division + camera.update() + batch.setProjectionMatrix(camera.combined) + } + + private fun gdxClearAndSetBlend(r: Float, g: Float, b: Float, a: Float) { + Gdx.gl.glClearColor(r,g,b,a) + Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) + Gdx.gl.glEnable(GL20.GL_TEXTURE_2D) + Gdx.gl.glEnable(GL20.GL_BLEND) + } + + private fun updateFullscreenQuad(WIDTH: Int, HEIGHT: Int) { // NOT y-flipped quads! + fullscreenQuad.setVertices(floatArrayOf( + 0f, 0f, 0f, 1f, 1f, 1f, 1f, 0f, 1f, + WIDTH.toFloat(), 0f, 0f, 1f, 1f, 1f, 1f, 1f, 1f, + WIDTH.toFloat(), HEIGHT.toFloat(), 0f, 1f, 1f, 1f, 1f, 1f, 0f, + 0f, HEIGHT.toFloat(), 0f, 1f, 1f, 1f, 1f, 0f, 0f + )) + fullscreenQuad.setIndices(shortArrayOf(0, 1, 2, 2, 3, 0)) + } + + private var updateAkku = 0.0 + private var updateRate = 1f / 60f + + override fun render() { + gdxClearAndSetBlend(.094f, .094f, .094f, 0f) + setCameraPosition(0f, 0f) + + // update window title with contents of the 'built-in status display' + Gdx.graphics.setTitle("tsvm $EMDASH F: ${Gdx.graphics.framesPerSecond}") + + super.render() + + val dt = Gdx.graphics.rawDeltaTime + updateAkku += dt + + var i = 0L + while (updateAkku >= updateRate) { + updateGame(updateRate) + updateAkku -= updateRate + i += 1 + } + + renderGame(dt) + } + + private fun reboot(vm: VM) { + vmRunners[vm.id]!!.close() + coroutineJobs[vm.id]!!.cancel("reboot requested") + + vm.init() + initVMenv(vm) + } + + private fun updateGame(delta: Float) { + // update currently selected viewport + val mouseX = Gdx.input.x + val mouseY = Gdx.input.y + if (Gdx.input.justTouched()) { + val px = mouseX / windowWidth + val py = mouseY / windowHeight + val panel = py * panelsX + px + + if (panel < panelsX * panelsY - 1) { + changeActiveSession(panel) + } + } + + vms.forEachIndexed { index, it -> + if (it?.vm?.resetDown == true && index == currentVMselection) { reboot(it.vm) } + it?.vm?.update(delta) + } + } + + private val defaultGuiBackgroundColour = Color(0x444444ff) + + private fun renderGame(delta: Float) { + vms.forEachIndexed { index, vmInfo -> + drawVMtoCanvas(delta, batch, vmInfo?.vm, index % panelsX, index / panelsX) + + // draw Window frames and whatnot + val xoff = (index % panelsX) * windowWidth + val yoff = (index / panelsX) * windowHeight + batch.color = + if (index == currentVMselection) EmulatorGuiToolkit.Theme.COL_HIGHLIGHT else EmulatorGuiToolkit.Theme.COL_INACTIVE + batch.inUse { + batch.fillRect(xoff, yoff, windowWidth, 2) + batch.fillRect(xoff, yoff + windowHeight - 2, windowWidth, 2) + batch.fillRect(xoff, yoff, 2, windowHeight) + batch.fillRect(xoff + windowWidth - 2, yoff, 2, windowHeight) + } + } + } + + private fun drawVMtoCanvas(delta: Float, batch: SpriteBatch, vm: VM?, pposX: Int, pposY: Int) { + vm.let { vm -> + // assuming the reference adapter of 560x448 + val xoff = pposX * windowWidth.toFloat() + val yoff = pposY * windowHeight.toFloat() + + if (vm != null) { + (vm.peripheralTable.getOrNull(1)?.peripheral as? GraphicsAdapter).let { gpu -> + if (gpu != null) { + val clearCol = gpu.getBackgroundColour() + // clear the viewport by drawing coloured rectangle becausewhynot + batch.color = clearCol + batch.inUse { + batch.fillRect(pposX * windowWidth, pposY * windowHeight, windowWidth, windowHeight) + } + + gpu.render(delta, fbatch, xoff + 40f, yoff + 16f, false, null) + } + else { + // no graphics device available + fbatch.inUse { + fbatch.color = defaultGuiBackgroundColour + fbatch.fillRect(pposX * windowWidth, pposY * windowHeight, windowWidth, windowHeight) + // draw text + fbatch.color = EmulatorGuiToolkit.Theme.COL_INACTIVE + font.draw(fbatch, "no graphics device available", xoff + (windowWidth - 196) / 2, yoff + (windowHeight - 12) / 2) + } + } + } + } + else { + // no vm on the viewport + fbatch.inUse { + fbatch.color = defaultGuiBackgroundColour + fbatch.fillRect(pposX * windowWidth, pposY * windowHeight, windowWidth, windowHeight) + // draw text + fbatch.color = EmulatorGuiToolkit.Theme.COL_INACTIVE + font.draw(fbatch, "no vm on this viewport", xoff + (windowWidth - 154) / 2, yoff + (windowHeight - 12) / 2) + } + } + } + } + + private fun resizePanel(panelsX: Int, panelsY: Int) { + if (panelsX > 16 || panelsY > 16) throw IllegalArgumentException("Panel count too large: ($panelsX, $panelsY)") + if (panelsX * panelsY <= 0) throw IllegalArgumentException("Illegal panel count: ($panelsX, $panelsY)") + this.panelsX = panelsX + this.panelsY = panelsY + resize(windowWidth * panelsX, windowHeight * panelsY) + } + + override fun resize(width: Int, height: Int) { + super.resize(width, height) + + updateFullscreenQuad(width, height) + + camera.setToOrtho(true, width.toFloat(), height.toFloat()) + camera.update() + batch.projectionMatrix = camera.combined + fbatch.projectionMatrix = camera.combined + } + + override fun dispose() { + super.dispose() + sqtex.dispose() + batch.dispose() + fbatch.dispose() + fullscreenQuad.dispose() + coroutineJobs.values.forEach { it.cancel() } + vms.forEach { it?.vm?.dispose() } + } + + fun SpriteBatch.fillRect(x: Int, y: Int, w: Int, h: Int) { + this.draw(sqtex, x.toFloat(), y.toFloat(), w.toFloat(), h.toFloat()) + } + + fun SpriteBatch.inUse(f: (SpriteBatch) -> Unit) { + this.begin() + f(this) + this.end() + } +} + +object EmulatorGuiToolkit { + + object Theme { + val COL_INACTIVE = Color.LIGHT_GRAY + val COL_ACTIVE = Color(0xfff066_ff.toInt()) // yellow + val COL_HIGHLIGHT = Color(0x00f8ff_ff) // cyan + val COL_DISABLED = Color(0xaaaaaaff.toInt()) + } + +} \ No newline at end of file diff --git a/tsvm_executable/src/net/torvald/tsvm/sq.tga b/tsvm_executable/src/net/torvald/tsvm/sq.tga new file mode 100644 index 0000000..2ab6451 --- /dev/null +++ b/tsvm_executable/src/net/torvald/tsvm/sq.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0347db245faf4dcf4f95c4888f0555c8d5cd7ee884b4b84cbd496ed85256ab98 +size 34