mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 19:51:51 +09:00
new peripheral; testing app improvements
This commit is contained in:
7
assets/bios/pipboot.js
Normal file
7
assets/bios/pipboot.js
Normal file
@@ -0,0 +1,7 @@
|
||||
println("Hello, Personal Information Processor!")
|
||||
|
||||
while (1) {
|
||||
for (let i = 0; i <= 160*140; i++) {
|
||||
sys.poke(-1048576 - i, Math.round(Math.random()*15));
|
||||
}
|
||||
}
|
||||
1
assets/bios/pipcode.bas
Normal file
1
assets/bios/pipcode.bas
Normal file
@@ -0,0 +1 @@
|
||||
10 PRINT "HAI!"
|
||||
@@ -4,6 +4,8 @@ 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 {
|
||||
@@ -33,16 +35,26 @@ public class AppLoader {
|
||||
// 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});
|
||||
|
||||
// uncomment to target the TerranBASIC runner
|
||||
// VM vm = new VM(64 << 10, new TheRealWorld(), new VMProgramRom[]{TBASRelBios.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, 447);
|
||||
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);
|
||||
|
||||
new Lwjgl3Application(new VMGUI(reference, WIDTH, HEIGHT), appConfig);
|
||||
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);
|
||||
}
|
||||
|
||||
public static ShaderProgram loadShaderFromFile(String vert, String frag) {
|
||||
|
||||
@@ -59,7 +59,6 @@ class VM(
|
||||
|
||||
fun init() {
|
||||
peripheralTable[0] = PeripheralEntry(
|
||||
"io",
|
||||
IOSpace(this),
|
||||
HW_RESERVE_SIZE,
|
||||
MMIO_SIZE.toInt() - 256,
|
||||
@@ -196,12 +195,21 @@ class VM(
|
||||
internal data class VMNativePtr(val address: Int, val size: Int)
|
||||
}
|
||||
|
||||
data class PeripheralEntry(
|
||||
val type: String = "null",
|
||||
class PeripheralEntry(
|
||||
val peripheral: PeriBase? = null,
|
||||
val memsize: Long = 0,
|
||||
val mmioSize: Int = 0,
|
||||
val interruptCount: Int = 0 // max: 4
|
||||
val interruptCount: Int = 0, // max: 4
|
||||
) {
|
||||
val type = peripheral?.typestring
|
||||
}
|
||||
|
||||
class PeripheralEntry2(
|
||||
val memsize: Long = 0,
|
||||
val mmioSize: Int = 0,
|
||||
val interruptCount: Int = 0, // max: 4
|
||||
val peripheralClassname: String,
|
||||
vararg val args: Any
|
||||
)
|
||||
|
||||
fun Int.kB() = this * 1024L
|
||||
|
||||
@@ -11,14 +11,28 @@ import java.io.File
|
||||
|
||||
fun ByteArray.startsWith(other: ByteArray) = this.sliceArray(other.indices).contentEquals(other)
|
||||
|
||||
|
||||
data class EmulInstance(
|
||||
class EmulInstance(
|
||||
val vm: VM,
|
||||
val display: String,
|
||||
val display: String?,
|
||||
val diskPath: String = "assets/disk0",
|
||||
val drawWidth: Int,
|
||||
val drawHeight: 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() {
|
||||
|
||||
@@ -27,7 +41,7 @@ class VMGUI(val loaderInfo: EmulInstance, val viewportWidth: Int, val viewportHe
|
||||
lateinit var batch: SpriteBatch
|
||||
lateinit var camera: OrthographicCamera
|
||||
|
||||
lateinit var gpu: GraphicsAdapter
|
||||
var gpu: GraphicsAdapter? = null
|
||||
lateinit var vmRunner: VMRunner
|
||||
lateinit var coroutineJob: Job
|
||||
lateinit var memvwr: Memvwr
|
||||
@@ -57,27 +71,48 @@ class VMGUI(val loaderInfo: EmulInstance, val viewportWidth: Int, val viewportHe
|
||||
}
|
||||
|
||||
private fun init() {
|
||||
val loadedClass = Class.forName(loaderInfo.display)
|
||||
val loadedClassConstructor = loadedClass.getConstructor(vm::class.java)
|
||||
val loadedClassInstance = loadedClassConstructor.newInstance(vm)
|
||||
gpu = (loadedClassInstance as GraphicsAdapter)
|
||||
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.getIO().blockTransferPorts[0].attachDevice(TestDiskDrive(vm, 0, File(loaderInfo.diskPath)))
|
||||
|
||||
vm.peripheralTable[1] = PeripheralEntry(
|
||||
VM.PERITYPE_GPU_AND_TERM,
|
||||
gpu,
|
||||
GraphicsAdapter.VRAM_SIZE,
|
||||
16,
|
||||
0
|
||||
)
|
||||
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` }
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
vm.getPrintStream = { gpu.getPrintStream() }
|
||||
vm.getErrorStream = { gpu.getErrorStream() }
|
||||
vm.getInputStream = { gpu.getInputStream() }
|
||||
|
||||
if (usememvwr) memvwr = Memvwr(vm)
|
||||
|
||||
|
||||
@@ -138,11 +173,21 @@ class VMGUI(val loaderInfo: EmulInstance, val viewportWidth: Int, val viewportHe
|
||||
|
||||
fun poke(addr: Long, value: Byte) = vm.poke(addr, value)
|
||||
|
||||
private val defaultGuiBackgroundColour = Color(0x444444ff)
|
||||
|
||||
private fun renderGame(delta: Float) {
|
||||
val clearCol = gpu.getBackgroundColour()
|
||||
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())
|
||||
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) {
|
||||
|
||||
151
src/net/torvald/tsvm/peripheral/ExtDisp.kt
Normal file
151
src/net/torvald/tsvm/peripheral/ExtDisp.kt
Normal file
@@ -0,0 +1,151 @@
|
||||
package net.torvald.tsvm.peripheral
|
||||
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.graphics.Pixmap
|
||||
import com.badlogic.gdx.graphics.Texture
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import com.badlogic.gdx.math.Matrix4
|
||||
import net.torvald.tsvm.AppLoader
|
||||
import net.torvald.tsvm.VM
|
||||
|
||||
/**
|
||||
* External Display that is always visible through its own UI ingame.
|
||||
*
|
||||
* Created by minjaesong on 2021-12-01.
|
||||
*/
|
||||
class ExtDisp(val vm: VM, val width: Int, val height: Int) : PeriBase {
|
||||
|
||||
constructor(vm: VM, w: java.lang.Integer, h: java.lang.Integer) : this(
|
||||
vm, w.toInt(), h.toInt()
|
||||
)
|
||||
|
||||
override val typestring = "oled"
|
||||
|
||||
override fun getVM(): VM {
|
||||
return vm
|
||||
}
|
||||
|
||||
internal val framebuffer = Pixmap(width, height, Pixmap.Format.Alpha)
|
||||
private val outFBObatch = SpriteBatch()
|
||||
|
||||
protected val drawShader = AppLoader.loadShaderInline(GraphicsAdapter.DRAW_SHADER_VERT, OLED_PAL_SHADER)
|
||||
|
||||
init {
|
||||
// no orthographic camera, must be "raw" Matrix4
|
||||
val m = Matrix4()
|
||||
m.setToOrtho2D(0f, 0f, width.toFloat(), height.toFloat())
|
||||
outFBObatch.projectionMatrix = m
|
||||
|
||||
framebuffer.blending = Pixmap.Blending.None
|
||||
framebuffer.setColor(0)
|
||||
framebuffer.fill()
|
||||
}
|
||||
|
||||
private lateinit var tex: Texture
|
||||
|
||||
open fun render(uiBatch: SpriteBatch, xoff: Float, yoff: Float) {
|
||||
framebuffer.pixels.position(0)
|
||||
|
||||
tex = Texture(framebuffer)
|
||||
|
||||
uiBatch.inUse {
|
||||
uiBatch.color = Color.WHITE
|
||||
uiBatch.shader = drawShader
|
||||
uiBatch.draw(tex, xoff, yoff)
|
||||
}
|
||||
|
||||
tex.dispose()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next power of two of the given number.
|
||||
*
|
||||
* E.g. for an input 100, this returns 128.
|
||||
* Returns 1 for all numbers <= 1.
|
||||
*
|
||||
* @param number The number to obtain the POT for.
|
||||
* @return The next power of two.
|
||||
*/
|
||||
private fun nextPowerOfTwo(number: Int): Int {
|
||||
var number = number
|
||||
number--
|
||||
number = number or (number shr 1)
|
||||
number = number or (number shr 2)
|
||||
number = number or (number shr 4)
|
||||
number = number or (number shr 8)
|
||||
number = number or (number shr 16)
|
||||
number++
|
||||
number += if (number == 0) 1 else 0
|
||||
return number
|
||||
}
|
||||
|
||||
override fun peek(addr: Long): Byte? {
|
||||
val adi = addr.toInt()
|
||||
return when (addr) {
|
||||
in 0 until width * height -> {
|
||||
framebuffer.pixels.get(adi)
|
||||
}
|
||||
in 0 until nextPowerOfTwo(width * height) -> { null }
|
||||
else -> peek(addr % nextPowerOfTwo(width * height))
|
||||
}
|
||||
}
|
||||
|
||||
override fun poke(addr: Long, byte: Byte) {
|
||||
val adi = addr.toInt()
|
||||
val bi = byte.toInt().and(255)
|
||||
when (addr) {
|
||||
in 0 until width * height -> {
|
||||
framebuffer.pixels.put(adi, byte)
|
||||
}
|
||||
in 0 until nextPowerOfTwo(width * height) -> { /* do nothing */ }
|
||||
else -> poke(addr % nextPowerOfTwo(width * height), byte)
|
||||
}
|
||||
}
|
||||
|
||||
override fun mmio_read(addr: Long): Byte? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun mmio_write(addr: Long, byte: Byte) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
framebuffer.dispose()
|
||||
try { tex.dispose() } catch (e: Throwable) {}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
val OLED_PAL_SHADER = """
|
||||
#version 130
|
||||
|
||||
varying vec4 v_color;
|
||||
varying vec2 v_texCoords;
|
||||
uniform sampler2D u_texture;
|
||||
vec4 pal[16] = vec4[](
|
||||
vec4(0.0,0.0,0.0,1.0),
|
||||
vec4(0.0,0.1765,0.6667,1.0),
|
||||
vec4(0.0,0.6667,0.0,1.0),
|
||||
vec4(0.0,0.7255,0.6667,1.0),
|
||||
vec4(0.6667,0.0,0.0,1.0),
|
||||
vec4(0.6667,0.1765,0.6667,1.0),
|
||||
vec4(0.6667,0.6667,0.0,1.0),
|
||||
vec4(0.6667,0.6667,0.6667,1.0),
|
||||
|
||||
vec4(0.0,0.0,0.0,1.0),
|
||||
vec4(0.0,0.2667,1.0,1.0),
|
||||
vec4(0.0,1.0,0.0,1.0),
|
||||
vec4(0.0,1.0,1.0,1.0),
|
||||
vec4(1.0,0.0,0.0,1.0),
|
||||
vec4(1.0,0.2667,1.0,1.0),
|
||||
vec4(1.0,1.0,0.0,1.0),
|
||||
vec4(1.0,1.0,1.0,1.0)
|
||||
);
|
||||
|
||||
void main(void) {
|
||||
gl_FragColor = pal[int(texture2D(u_texture, v_texCoords).a * 255.0) % 16];
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,8 @@ class ReferenceLikeLCD(vm: VM) : GraphicsAdapter(vm, GraphicsAdapter.DEFAULT_CON
|
||||
open class GraphicsAdapter(val vm: VM, val config: AdapterConfig, val sgr: SuperGraphicsAddonConfig = SuperGraphicsAddonConfig()) :
|
||||
GlassTty(config.textRows, config.textCols), PeriBase {
|
||||
|
||||
override val typestring = VM.PERITYPE_GPU_AND_TERM
|
||||
|
||||
override fun getVM(): VM {
|
||||
return vm
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ import kotlin.experimental.and
|
||||
|
||||
class IOSpace(val vm: VM) : PeriBase, InputProcessor {
|
||||
|
||||
override val typestring = "io"
|
||||
|
||||
override fun getVM(): VM {
|
||||
return vm
|
||||
}
|
||||
|
||||
@@ -20,4 +20,6 @@ interface PeriBase {
|
||||
fun dispose()
|
||||
|
||||
fun getVM(): VM
|
||||
|
||||
val typestring: String
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import java.io.OutputStream
|
||||
|
||||
class TTY(val vm: VM) : GlassTty(TEXT_ROWS, TEXT_COLS), PeriBase {
|
||||
|
||||
override val typestring = VM.PERITYPE_GPU_AND_TERM
|
||||
|
||||
companion object {
|
||||
const val TEXT_ROWS = 25
|
||||
const val TEXT_COLS = 80
|
||||
|
||||
@@ -33,4 +33,7 @@ object TandemBios : VMProgramRom("./assets/bios/tandemport.js")
|
||||
object TsvmBios : VMProgramRom("./assets/bios/tsvmbios.js")
|
||||
object BasicRom : VMProgramRom("./assets/bios/basic.bin")
|
||||
object TBASRelBios : VMProgramRom("./assets/bios/tbasdist.js")
|
||||
object WPBios : VMProgramRom("./assets/bios/wp.js")
|
||||
object WPBios : VMProgramRom("./assets/bios/wp.js")
|
||||
|
||||
object PipBios : VMProgramRom("./assets/bios/pipboot.js")
|
||||
object PipROM : VMProgramRom("./assets/bios/pipcode.bas")
|
||||
@@ -32,7 +32,6 @@ class V2kRunTest : ApplicationAdapter() {
|
||||
gpu = GraphicsAdapter(vm, GraphicsAdapter.DEFAULT_CONFIG_COLOR_CRT)
|
||||
|
||||
vm.peripheralTable[1] = PeripheralEntry(
|
||||
VM.PERITYPE_GPU_AND_TERM,
|
||||
gpu,
|
||||
GraphicsAdapter.VRAM_SIZE,
|
||||
16,
|
||||
|
||||
Reference in New Issue
Block a user