mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-10 21:21:51 +09:00
codes split into modules: tsvm_core, tsvm_executable, TerranBASICexecutable
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
package net.torvald.terrarum.modulecomputers.tsvmperipheral
|
||||
|
||||
import com.badlogic.gdx.files.FileHandle
|
||||
import com.badlogic.gdx.graphics.Pixmap
|
||||
import net.torvald.tsvm.VM
|
||||
import net.torvald.tsvm.peripheral.BlockTransferInterface
|
||||
import net.torvald.tsvm.peripheral.TestDiskDrive
|
||||
import net.torvald.tsvm.peripheral.trimNull
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2021-12-02.
|
||||
*/
|
||||
class WorldRadar(pngfile: FileHandle) : BlockTransferInterface(false, true) {
|
||||
|
||||
private val W = 162
|
||||
private val H = 142
|
||||
|
||||
private val world = ByteArray(256*256)
|
||||
|
||||
private val AIR = 0.toByte()
|
||||
private val DIRT = 1.toByte()
|
||||
private val GRASS = 2.toByte()
|
||||
private val STONE = 16.toByte()
|
||||
|
||||
private val AIR_OUT = 0.toByte()
|
||||
private val GRASS_OUT = 2.toByte()
|
||||
private val DIRT_OUT = 4.toByte()
|
||||
private val STONE_OUT = 7.toByte()
|
||||
|
||||
init {
|
||||
statusCode = TestDiskDrive.STATE_CODE_STANDBY
|
||||
|
||||
val worldTex = Pixmap(pngfile)
|
||||
for (y in 0 until worldTex.height) { for (x in 0 until worldTex.width) {
|
||||
val c = worldTex.getPixel(x, y)
|
||||
world[y * worldTex.width + x] = when (c) {
|
||||
0x46712dff -> GRASS
|
||||
0x9b9a9bff.toInt() -> STONE
|
||||
0x6a5130ff -> DIRT
|
||||
else -> AIR
|
||||
}
|
||||
}}
|
||||
|
||||
worldTex.dispose()
|
||||
}
|
||||
|
||||
private val messageComposeBuffer = ByteArrayOutputStream(BLOCK_SIZE) // always use this and don't alter blockSendBuffer please
|
||||
private var blockSendBuffer = ByteArray(1)
|
||||
private var blockSendCount = 0
|
||||
|
||||
private fun resetBuf() {
|
||||
blockSendCount = 0
|
||||
messageComposeBuffer.reset()
|
||||
}
|
||||
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return (blockSendCount * BLOCK_SIZE < blockSendBuffer.size)
|
||||
}
|
||||
|
||||
override fun startSendImpl(recipient: BlockTransferInterface): Int {
|
||||
if (blockSendCount == 0) {
|
||||
blockSendBuffer = messageComposeBuffer.toByteArray()
|
||||
}
|
||||
|
||||
val sendSize = if (blockSendBuffer.size - (blockSendCount * BLOCK_SIZE) < BLOCK_SIZE)
|
||||
blockSendBuffer.size % BLOCK_SIZE
|
||||
else BLOCK_SIZE
|
||||
|
||||
recipient.writeout(ByteArray(sendSize) {
|
||||
blockSendBuffer[blockSendCount * BLOCK_SIZE + it]
|
||||
})
|
||||
|
||||
blockSendCount += 1
|
||||
|
||||
return sendSize
|
||||
}
|
||||
|
||||
private var oldCmdbuf = HashMap<Int,Byte>(1024)
|
||||
|
||||
override fun writeoutImpl(inputData: ByteArray) {
|
||||
val inputString = inputData.trimNull().toString(VM.CHARSET)
|
||||
|
||||
// prepare draw commands
|
||||
/*
|
||||
* draw command format:
|
||||
*
|
||||
* <Y> <X> <COL>
|
||||
*
|
||||
* marking rules:
|
||||
*
|
||||
* : exposed = has at least 1 nonsolid on 4 sides
|
||||
*
|
||||
* 1. exposed grass -> 2
|
||||
* 2. exposed dirt -> 4
|
||||
* 3. exposed stone -> 7
|
||||
* 4. stone exposed to dirt/grass -> 7
|
||||
*/
|
||||
if (inputString.startsWith("POLL")) {
|
||||
resetBuf()
|
||||
val cmdbuf = HashMap<Int,Byte>(1024)
|
||||
|
||||
for (y in 1..H-2) { for (x in 1..W-2) {
|
||||
val yx = (y-1).shl(8) or x
|
||||
val i = y * W + x
|
||||
val nearby = listOf(i-W,i-1,i+1,i+W).map { world[it] } // up, left, right, down
|
||||
val block = world[i]
|
||||
|
||||
if (block == GRASS && nearby.contains(AIR)) {
|
||||
cmdbuf[yx] = GRASS_OUT
|
||||
}
|
||||
else if (block == DIRT && nearby.contains(AIR)) {
|
||||
cmdbuf[yx] = DIRT_OUT
|
||||
}
|
||||
else if (block == STONE && (nearby.contains(AIR) || nearby.contains(GRASS) || nearby.contains(DIRT))) {
|
||||
cmdbuf[yx] = STONE_OUT
|
||||
}
|
||||
}}
|
||||
|
||||
(oldCmdbuf.keys union cmdbuf.keys).sorted().forEach { key ->
|
||||
val value = (cmdbuf[key] ?: AIR_OUT).toInt()
|
||||
val x = key % 256
|
||||
val y = key / 256
|
||||
messageComposeBuffer.write(y)
|
||||
messageComposeBuffer.write(x)
|
||||
messageComposeBuffer.write(value)
|
||||
}
|
||||
|
||||
oldCmdbuf = cmdbuf
|
||||
}
|
||||
}
|
||||
}
|
||||
54
tsvm_executable/src/net/torvald/tsvm/AppLoader.java
Normal file
54
tsvm_executable/src/net/torvald/tsvm/AppLoader.java
Normal file
@@ -0,0 +1,54 @@
|
||||
package net.torvald.tsvm;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application;
|
||||
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration;
|
||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
|
||||
import kotlin.Pair;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import net.torvald.tsvm.peripheral.*;
|
||||
|
||||
public class AppLoader {
|
||||
|
||||
public static String appTitle = "tsvm";
|
||||
public static Lwjgl3ApplicationConfiguration appConfig;
|
||||
|
||||
public static int WIDTH = 640;//810;//720;
|
||||
public static int HEIGHT = 480;//360;//480;
|
||||
|
||||
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);
|
||||
|
||||
|
||||
// VM vm = new VM(64 << 10, new TheRealWorld(), new VMProgramRom[]{BasicBios.INSTANCE, BasicRom.INSTANCE});
|
||||
// VM vm = new VM(64 << 10, new TheRealWorld(), new VMProgramRom[]{OEMBios.INSTANCE, BasicRom.INSTANCE});
|
||||
// VM vm = new VM(64 << 10, new TheRealWorld(), new VMProgramRom[]{TandemBios.INSTANCE, BasicRom.INSTANCE});
|
||||
// VM vm = new VM(128 << 10, new TheRealWorld(), new VMProgramRom[]{BasicBios.INSTANCE, WPBios.INSTANCE});
|
||||
VM vm = new VM(2048 << 10, new TheRealWorld(), new VMProgramRom[]{TsvmBios.INSTANCE});
|
||||
VM pipvm = new VM(4096, new TheRealWorld(), new VMProgramRom[]{PipBios.INSTANCE, PipROM.INSTANCE});
|
||||
|
||||
EmulInstance reference = new EmulInstance(vm, "net.torvald.tsvm.peripheral.ReferenceGraphicsAdapter", "assets/disk0", 560, 448);
|
||||
EmulInstance reference2 = new EmulInstance(vm, "net.torvald.tsvm.peripheral.ReferenceLikeLCD", "assets/disk0", 560, 448);
|
||||
EmulInstance term = new EmulInstance(vm, "net.torvald.tsvm.peripheral.Term", "assets/disk0", 720, 480);
|
||||
EmulInstance portable = new EmulInstance(vm, "net.torvald.tsvm.peripheral.CharacterLCDdisplay", "assets/disk0", 628, 302);
|
||||
EmulInstance wp = new EmulInstance(vm, "net.torvald.tsvm.peripheral.WpTerm", "assets/wpdisk", 810, 360);
|
||||
EmulInstance pip = new EmulInstance(pipvm, null, "assets/disk0", 640, 480, CollectionsKt.listOf(new Pair(1, new PeripheralEntry2(
|
||||
32768L,
|
||||
1,
|
||||
0,
|
||||
"net.torvald.tsvm.peripheral.ExtDisp",
|
||||
pipvm, 160, 140
|
||||
))));
|
||||
|
||||
new Lwjgl3Application(new VMGUI(pip, WIDTH, HEIGHT), appConfig);
|
||||
}
|
||||
}
|
||||
127
tsvm_executable/src/net/torvald/tsvm/Memvwr.kt
Normal file
127
tsvm_executable/src/net/torvald/tsvm/Memvwr.kt
Normal file
@@ -0,0 +1,127 @@
|
||||
package net.torvald.tsvm
|
||||
|
||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.toUint
|
||||
import net.torvald.tsvm.peripheral.IOSpace
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.Dimension
|
||||
import java.awt.Font
|
||||
import javax.swing.JFrame
|
||||
import javax.swing.JTextArea
|
||||
import javax.swing.WindowConstants
|
||||
|
||||
class Memvwr(val vm: VM) : JFrame() {
|
||||
|
||||
val memArea = JTextArea()
|
||||
var columns = 16
|
||||
|
||||
fun composeVwrText() {
|
||||
val sb = StringBuilder()
|
||||
val io = vm.peripheralTable[0].peripheral as IOSpace
|
||||
|
||||
sb.append("== MMIO ==\n")
|
||||
|
||||
sb.append("Keyboard buffer: ")
|
||||
for (i in 0L..31L) {
|
||||
sb.append(io.peek(i)!!.toUByte().toString(16).padStart(2, '0').toUpperCase())
|
||||
sb.append(' ')
|
||||
}
|
||||
sb.append('\n')
|
||||
|
||||
sb.append("Keyboard/Mouse input latched: ")
|
||||
sb.append(io.peek(39L) != 0.toByte())
|
||||
sb.append('\n')
|
||||
sb.append("Mouse pos: ")
|
||||
sb.append((io.peek(32L)!!.toUint() or (io.peek(33L)!!.toUint() shl 8)).toShort())
|
||||
sb.append(", ")
|
||||
sb.append((io.peek(34L)!!.toUint() or (io.peek(35L)!!.toUint() shl 8)).toShort())
|
||||
sb.append(" (mouse down: ")
|
||||
sb.append(io.peek(36L) != 0.toByte())
|
||||
sb.append(")\n")
|
||||
sb.append("Keys pressed: ")
|
||||
for (i in 40L..47L) {
|
||||
sb.append(io.peek(i)!!.toUByte().toString(16).padStart(2, '0').toUpperCase())
|
||||
sb.append(' ')
|
||||
}
|
||||
sb.append('\n')
|
||||
|
||||
sb.append("TTY Keyboard read: ")
|
||||
sb.append(io.peek(38L) != 0.toByte())
|
||||
sb.append('\n')
|
||||
|
||||
sb.append("Counter latched: ")
|
||||
sb.append(io.peek(68L)!!.toString(2).padStart(8, '0'))
|
||||
sb.append('\n')
|
||||
|
||||
sb.append("\nBlock transfer status:\n")
|
||||
for (port in 0..3) {
|
||||
val status = io.peek(4084L + 2 * port)!!.toUint() or (io.peek(4085L + 2 * port)!!.toUint() shl 8)
|
||||
|
||||
sb.append("== Port ${port + 1}\n")
|
||||
sb.append(" hasNext: ${(status and 0x8000) != 0}\n")
|
||||
sb.append(" size of the block: ${if (status and 0xFFF == 0) 4096 else status and 0xFFF}\n")
|
||||
}
|
||||
|
||||
sb.append("\nBlock transfer control:\n")
|
||||
for (port in 0..3) {
|
||||
val status = io.peek(4092L + port)!!
|
||||
|
||||
sb.append("== Port ${port + 1}: ${status.toString(2).padStart(8, '0')}\n")
|
||||
}
|
||||
|
||||
sb.append("\n== First 4 kbytes of User RAM ==\n")
|
||||
sb.append("ADRESS : 0 1 2 3| 4 5 6 7| 8 9 A B| C D E F\n")
|
||||
for (i in 0L..4095L) {
|
||||
if (i % columns == 0L) {
|
||||
sb.append(i.toString(16).toUpperCase().padStart(6, '0')) // mem addr
|
||||
sb.append(" : ") // separator
|
||||
}
|
||||
|
||||
|
||||
sb.append(vm.peek(i)!!.toUint().toString(16).toUpperCase().padStart(2, '0'))
|
||||
if (i % 16L in longArrayOf(3L, 7L, 11L)) {
|
||||
sb.append('|') // mem value
|
||||
}
|
||||
else {
|
||||
sb.append(' ') // mem value
|
||||
}
|
||||
|
||||
// ASCII viewer
|
||||
if (i % columns == 15L) {
|
||||
sb.append("| ")
|
||||
|
||||
for (x in -15..0) {
|
||||
val mem = vm.peek(i + x)!!.toUint()
|
||||
|
||||
if (mem < 32) {
|
||||
sb.append('.')
|
||||
}
|
||||
else {
|
||||
sb.append(mem.toChar())
|
||||
}
|
||||
|
||||
if (x + 15 in intArrayOf(3, 7, 11))
|
||||
sb.append(' ')
|
||||
}
|
||||
|
||||
sb.append("|\n")
|
||||
}
|
||||
}
|
||||
|
||||
memArea.text = sb.toString()
|
||||
}
|
||||
|
||||
fun update() {
|
||||
composeVwrText()
|
||||
}
|
||||
|
||||
init {
|
||||
memArea.font = Font("Monospaced", Font.PLAIN, 12)
|
||||
memArea.highlighter = null
|
||||
|
||||
this.layout = BorderLayout()
|
||||
this.isVisible = true
|
||||
this.add(javax.swing.JScrollPane(memArea), BorderLayout.CENTER)
|
||||
this.defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE
|
||||
this.size = Dimension(820, 960)
|
||||
}
|
||||
}
|
||||
280
tsvm_executable/src/net/torvald/tsvm/VMGUI.kt
Normal file
280
tsvm_executable/src/net/torvald/tsvm/VMGUI.kt
Normal file
@@ -0,0 +1,280 @@
|
||||
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.*
|
||||
import net.torvald.terrarum.modulecomputers.tsvmperipheral.WorldRadar
|
||||
import net.torvald.tsvm.peripheral.*
|
||||
import java.io.File
|
||||
|
||||
class EmulInstance(
|
||||
val vm: VM,
|
||||
val display: String?,
|
||||
val diskPath: String = "assets/disk0",
|
||||
val drawWidth: Int,
|
||||
val drawHeight: Int,
|
||||
) {
|
||||
|
||||
var extraPeripherals: List<Pair<Int, PeripheralEntry2>> = listOf(); private set
|
||||
|
||||
constructor(
|
||||
vm: VM,
|
||||
display: String?,
|
||||
diskPath: String = "assets/disk0",
|
||||
drawWidth: Int,
|
||||
drawHeight: Int,
|
||||
extraPeripherals: List<Pair<Int, PeripheralEntry2>>
|
||||
) : this(vm, display, diskPath, drawWidth, drawHeight) {
|
||||
this.extraPeripherals = extraPeripherals
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class VMGUI(val loaderInfo: EmulInstance, val viewportWidth: Int, val viewportHeight: Int) : ApplicationAdapter() {
|
||||
|
||||
val vm = loaderInfo.vm
|
||||
|
||||
lateinit var batch: SpriteBatch
|
||||
lateinit var camera: OrthographicCamera
|
||||
|
||||
var gpu: GraphicsAdapter? = null
|
||||
lateinit var vmRunner: VMRunner
|
||||
lateinit var coroutineJob: Job
|
||||
lateinit var memvwr: Memvwr
|
||||
lateinit var fullscreenQuad: Mesh
|
||||
|
||||
val usememvwr = false
|
||||
|
||||
override fun create() {
|
||||
super.create()
|
||||
|
||||
fullscreenQuad = Mesh(
|
||||
true, 4, 6,
|
||||
VertexAttribute.Position(),
|
||||
VertexAttribute.ColorUnpacked(),
|
||||
VertexAttribute.TexCoords(0)
|
||||
)
|
||||
updateFullscreenQuad(AppLoader.WIDTH, AppLoader.HEIGHT)
|
||||
|
||||
batch = SpriteBatch()
|
||||
camera = OrthographicCamera(AppLoader.WIDTH.toFloat(), AppLoader.HEIGHT.toFloat())
|
||||
camera.setToOrtho(false)
|
||||
camera.update()
|
||||
batch.projectionMatrix = camera.combined
|
||||
|
||||
|
||||
init()
|
||||
}
|
||||
|
||||
private fun init() {
|
||||
if (loaderInfo.display != null) {
|
||||
val loadedClass = Class.forName(loaderInfo.display)
|
||||
val loadedClassConstructor = loadedClass.getConstructor(vm::class.java)
|
||||
val loadedClassInstance = loadedClassConstructor.newInstance(vm)
|
||||
gpu = (loadedClassInstance as GraphicsAdapter)
|
||||
|
||||
vm.getIO().blockTransferPorts[0].attachDevice(TestDiskDrive(vm, 0, File(loaderInfo.diskPath)))
|
||||
|
||||
vm.peripheralTable[1] = PeripheralEntry(
|
||||
gpu,
|
||||
GraphicsAdapter.VRAM_SIZE,
|
||||
16,
|
||||
0
|
||||
)
|
||||
|
||||
vm.getPrintStream = { gpu!!.getPrintStream() }
|
||||
vm.getErrorStream = { gpu!!.getErrorStream() }
|
||||
vm.getInputStream = { gpu!!.getInputStream() }
|
||||
}
|
||||
else {
|
||||
vm.getPrintStream = { System.out }
|
||||
vm.getErrorStream = { System.err }
|
||||
vm.getInputStream = { System.`in` }
|
||||
}
|
||||
|
||||
vm.getIO().blockTransferPorts[1].attachDevice(
|
||||
WorldRadar(
|
||||
Gdx.files.internal(
|
||||
"test_assets/test_terrain.png"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
loaderInfo.extraPeripherals.forEach { (port, peri) ->
|
||||
val typeargs = peri.args.map { it.javaClass }.toTypedArray()
|
||||
|
||||
val loadedClass = Class.forName(peri.peripheralClassname)
|
||||
val loadedClassConstructor = loadedClass.getConstructor(*typeargs)
|
||||
val loadedClassInstance = loadedClassConstructor.newInstance(*peri.args)
|
||||
|
||||
vm.peripheralTable[port] = PeripheralEntry(
|
||||
loadedClassInstance as PeriBase,
|
||||
peri.memsize,
|
||||
peri.mmioSize,
|
||||
peri.interruptCount
|
||||
)
|
||||
}
|
||||
|
||||
Gdx.input.inputProcessor = vm.getIO()
|
||||
|
||||
if (usememvwr) memvwr = Memvwr(vm)
|
||||
|
||||
|
||||
vmRunner = VMRunnerFactory(vm, "js")
|
||||
coroutineJob = GlobalScope.launch {
|
||||
vmRunner.executeCommand(vm.roms[0]!!.readAll())
|
||||
}
|
||||
}
|
||||
|
||||
private var rebootRequested = false
|
||||
|
||||
private fun reboot() {
|
||||
vmRunner.close()
|
||||
coroutineJob.cancel("reboot requested")
|
||||
|
||||
vm.init()
|
||||
init()
|
||||
}
|
||||
|
||||
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'
|
||||
val msg = (1024L until 1048L).map { cp437toUni[vm.getIO().mmio_read(it)!!.toInt().and(255)] }.joinToString("").trim()
|
||||
Gdx.graphics.setTitle("$msg $EMDASH F: ${Gdx.graphics.framesPerSecond}")
|
||||
|
||||
if (usememvwr) memvwr.update()
|
||||
|
||||
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 updateGame(delta: Float) {
|
||||
if (!vm.resetDown && rebootRequested) {
|
||||
reboot()
|
||||
rebootRequested = false
|
||||
}
|
||||
|
||||
vm.update(delta)
|
||||
|
||||
if (vm.resetDown) rebootRequested = true
|
||||
}
|
||||
|
||||
fun poke(addr: Long, value: Byte) = vm.poke(addr, value)
|
||||
|
||||
private val defaultGuiBackgroundColour = Color(0x444444ff)
|
||||
|
||||
private fun renderGame(delta: Float) {
|
||||
val clearCol = gpu?.getBackgroundColour() ?: defaultGuiBackgroundColour
|
||||
Gdx.gl.glClearColor(clearCol.r, clearCol.g, clearCol.b, clearCol.a)
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
|
||||
gpu?.render(delta, batch, (viewportWidth - loaderInfo.drawWidth).div(2).toFloat(), (viewportHeight - loaderInfo.drawHeight).div(2).toFloat())
|
||||
|
||||
vm.findPeribyType("oled")?.let {
|
||||
val disp = it.peripheral as ExtDisp
|
||||
|
||||
disp.render(batch,
|
||||
(viewportWidth - loaderInfo.drawWidth).div(2).toFloat() + (gpu?.config?.width ?: 0),
|
||||
(viewportHeight - loaderInfo.drawHeight).div(2).toFloat())
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
super.dispose()
|
||||
batch.dispose()
|
||||
fullscreenQuad.dispose()
|
||||
coroutineJob.cancel()
|
||||
vm.dispose()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val cp437toUni = hashMapOf<Int, Char>(
|
||||
0 to 32.toChar(),
|
||||
1 to 0x263A.toChar(),
|
||||
2 to 0x263B.toChar(),
|
||||
3 to 0x2665.toChar(),
|
||||
4 to 0x2666.toChar(),
|
||||
5 to 0x2663.toChar(),
|
||||
6 to 0x2660.toChar(),
|
||||
7 to 0x2022.toChar(),
|
||||
8 to 0x25D8.toChar(),
|
||||
9 to 0x25CB.toChar(),
|
||||
10 to 0x25D9.toChar(),
|
||||
11 to 0x2642.toChar(),
|
||||
12 to 0x2640.toChar(),
|
||||
13 to 0x266A.toChar(),
|
||||
14 to 0x266B.toChar(),
|
||||
15 to 0x00A4.toChar(),
|
||||
|
||||
16 to 0x25BA.toChar(),
|
||||
17 to 0x25C4.toChar(),
|
||||
18 to 0x2195.toChar(),
|
||||
19 to 0x203C.toChar(),
|
||||
20 to 0x00B6.toChar(),
|
||||
21 to 0x00A7.toChar(),
|
||||
22 to 0x25AC.toChar(),
|
||||
23 to 0x21A8.toChar(),
|
||||
24 to 0x2191.toChar(),
|
||||
25 to 0x2193.toChar(),
|
||||
26 to 0x2192.toChar(),
|
||||
27 to 0x2190.toChar(),
|
||||
28 to 0x221F.toChar(),
|
||||
29 to 0x2194.toChar(),
|
||||
30 to 0x25B2.toChar(),
|
||||
31 to 0x25BC.toChar(),
|
||||
|
||||
127 to 0x2302.toChar(),
|
||||
|
||||
158 to 0x2610.toChar(),
|
||||
159 to 0x2611.toChar()
|
||||
)
|
||||
|
||||
init {
|
||||
for (k in 32..126) {
|
||||
cp437toUni[k] = k.toChar()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const val EMDASH = 0x2014.toChar()
|
||||
Reference in New Issue
Block a user