new peripheral; testing app improvements

This commit is contained in:
minjaesong
2021-12-01 15:22:24 +09:00
parent 0898e8478e
commit 065dfdac6f
12 changed files with 266 additions and 32 deletions

7
assets/bios/pipboot.js Normal file
View 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
View File

@@ -0,0 +1 @@
10 PRINT "HAI!"

View File

@@ -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) {

View File

@@ -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

View File

@@ -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) {

View 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];
}
"""
}
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -20,4 +20,6 @@ interface PeriBase {
fun dispose()
fun getVM(): VM
val typestring: String
}

View File

@@ -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

View File

@@ -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")

View File

@@ -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,