mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 19:51:51 +09:00
the multiview emulator thingamajig
This commit is contained in:
@@ -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)
|
||||
*/
|
||||
}
|
||||
@@ -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<Int, Color>()
|
||||
}
|
||||
BIN
tsvm_executable/src/net/torvald/terrarum/imagefont/font.tga
LFS
Normal file
BIN
tsvm_executable/src/net/torvald/terrarum/imagefont/font.tga
LFS
Normal file
Binary file not shown.
@@ -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<TextureRegion>
|
||||
|
||||
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<TextureRegion>(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<TextureRegion>(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()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
37
tsvm_executable/src/net/torvald/tsvm/TsvmEmulator.java
Normal file
37
tsvm_executable/src/net/torvald/tsvm/TsvmEmulator.java
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
295
tsvm_executable/src/net/torvald/tsvm/VMEmuExecutable.kt
Normal file
295
tsvm_executable/src/net/torvald/tsvm/VMEmuExecutable.kt
Normal file
@@ -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<VMRunnerInfo>(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<Int, VMRunner>() // <VM's identifier, VMRunner>
|
||||
var coroutineJobs = HashMap<Int, Job>() // <VM's identifier, Job>
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
}
|
||||
BIN
tsvm_executable/src/net/torvald/tsvm/sq.tga
LFS
Normal file
BIN
tsvm_executable/src/net/torvald/tsvm/sq.tga
LFS
Normal file
Binary file not shown.
Reference in New Issue
Block a user