fixing black screen bug

This commit is contained in:
minjaesong
2020-04-24 10:20:14 +09:00
parent 280b4148d9
commit 344c35ed9b
11 changed files with 224 additions and 84 deletions

View File

@@ -0,0 +1,28 @@
package net.torvald.tsvm
import net.torvald.tsvm.peripheral.GraphicsAdapter
class GraphicsJSR223Delegate(val vm: VM) {
private fun getFirstGPU(): GraphicsAdapter? {
return vm.peripheralTable[vm.findPeribyType("gpu") ?: return null].peripheral as? GraphicsAdapter
}
fun resetPalette() {
getFirstGPU()?.poke(250883L, 1)
}
/**
* @param index which palette number to modify, 0-255
* @param r g - b - a - RGBA value, 0-15
*/
fun setPalette(index: Int, r: Int, g: Int, b: Int, a: Int = 16) {
getFirstGPU()?.let {
it.paletteOfFloats[index * 4] = (r and 15) / 15f
it.paletteOfFloats[index * 4 + 1] = (g and 15) / 15f
it.paletteOfFloats[index * 4 + 2] = (b and 15) / 15f
it.paletteOfFloats[index * 4 + 3] = (a and 15) / 15f
}
}
}

View File

@@ -115,7 +115,7 @@ class VM(
}
}
fun poke(addr: Long, value: Byte) {
internal fun poke(addr: Long, value: Byte) {
val (memspace, offset) = translateAddr(addr)
if (memspace == null)
Firmware.errorIllegalAccess(addr)
@@ -125,7 +125,7 @@ class VM(
(memspace as PeriBase).poke(offset, value)
}
fun peek(addr:Long): Byte? {
internal fun peek(addr:Long): Byte? {
val (memspace, offset) = translateAddr(addr)
return if (memspace == null)
null

View File

@@ -70,21 +70,12 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter()
private var latch = true
private fun updateGame(delta: Float) {
// black screening workaround
if (latch) {
latch = false
//paintTestPalette()
val peripheralSlot = vm.findPeribyType(VM.PERITYPE_GRAPHICS)!!
val hwoff = VM.HW_RESERVE_SIZE * peripheralSlot
for (i in 250880 until 250972) {
vm.poke(-(i + 1) - hwoff, 0)
}
}
}
fun poke(addr: Long, value: Byte) = vm.poke(addr, value)
private fun paintTestPalette() {
}
private val gpuTestPaletteKt = """
@@ -101,30 +92,25 @@ fun inthash(x: Int): Int {
var rng = (Math.floor(Math.random() * 2147483647) + 1).toInt()
bindings.forEach {
println(it)
}
println(zzz)
while (true) {
val tstart: Long = System.nanoTime()
for (y1 in 0..359) {
for (x1 in 0 until w) {
val palnum = 20 * (y1 / 30) + (x1 / 28)
vm.poke(-(y1 * w + x1 + 1L) - hwoff, inthash(palnum + rng).toByte())
vm.poke(-(y1 * w + x1 + 1) - hwoff, inthash(palnum + rng))
}
}
for (y2 in 360 until h) {
for (x2 in 0 until w) {
val palnum = 240 + x2 / 35
vm.poke(-(y2 * w + x2 + 1L) - hwoff, palnum.toByte())
vm.poke(-(y2 * w + x2 + 1) - hwoff, palnum)
}
}
for (k in 0..2239) {
vm.poke(-(254912L + k + 1) - hwoff, -2) // white
vm.poke(-(254912L + 2240 + k + 1) - hwoff, -1) // transparent
vm.poke(-(254912L + 2240 * 2 + k + 1) - hwoff, Math.round(Math.random() * 255).toByte())
for (k in 0 until 2560) {
vm.poke(-(253952 + k + 1) - hwoff, -2) // white
vm.poke(-(253952 + 2560 + k + 1) - hwoff, -1) // transparent
vm.poke(-(253952 + 2560 * 2 + k + 1) - hwoff, Math.round(Math.random() * 255).toInt())
}
rng = inthash(rng)
@@ -133,6 +119,42 @@ while (true) {
}
""".trimIndent()
private val gpuTestPaletteKt2 = """
val w = 560
val h = 448
val hwoff = 1048576
fun inthash(x: Int): Int {
var x = (x.shr(16) xor x) * 0x45d9f3b
x = (x.shr(16) xor x) * 0x45d9f3b
x = (x.shr(16) xor x)
return x
}
var rng = ((Math.random() * 2147483647) + 1).toInt()
while (true) {
for (y1 in 0..359) {
for (x1 in 0 until w) {
val palnum = 20 * (y1 / 30) + (x1 / 28)
vm.poke(-(y1 * w + x1 + 1) - hwoff, palnum)//inthash(palnum + rng))
}
}
for (y2 in 360 until h) {
for (x2 in 0 until w) {
val palnum = 240 + x2 / 35
vm.poke(-(y2 * w + x2 + 1) - hwoff, palnum)
}
}
for (k in 0 until 255) {
graphics.setPalette(k, (Math.random() * 15).toInt(), (Math.random() * 15).toInt(), (Math.random() * 15).toInt())
}
println("arst")
}
""".trimIndent()
private val gpuTestPalette = """
local vm = require("rawmem")
local bit = require("bit32")
@@ -150,7 +172,7 @@ end
local rng = math.floor(math.random() * 2147483647)
while true do
local tstart = vm.nanotime()
local tstart = vm.nanoTime()
for y = 0, 359 do
for x = 0, w - 1 do
@@ -167,14 +189,14 @@ while true do
end
for k = 0, 2239 do
vm.poke(-(254912 + k + 1) - hwoff, 254)
vm.poke(-(254912 + 2240 + k + 1) - hwoff, 255)
vm.poke(-(254912 + 2240*2 + k + 1) - hwoff, math.floor(math.random() * 255.0))
vm.poke(-(253952 + k + 1) - hwoff, 254)
vm.poke(-(253952 + 2560 + k + 1) - hwoff, 255)
vm.poke(-(253952 + 2560*2 + k + 1) - hwoff, math.floor(math.random() * 255.0))
end
rng = inthash(rng)
local tend = vm.nanotime()
local tend = vm.nanoTime()
print("Apparent FPS: "..tostring(1000000000.0 / (tend - tstart)))
end
@@ -186,7 +208,7 @@ var h = 448
var hwoff = 1048576
print(typeof print) //function
print(typeof poke.invoke) //function
print(typeof vm.poke) //function
function inthash(x) {
x = ((x >> 16) ^ x) * 0x45d9f3b
@@ -199,31 +221,31 @@ var rng = Math.floor(Math.random() * 2147483647) + 1
while (true) {
var tstart = nanotime.invoke()
var tstart = vm.nanoTime()
for (var y = 0; y < 360; y++) {
for (var x = 0; x < w; x++) {
var palnum = 20 * Math.floor(y / 30) + Math.floor(x / 28)
poke.invoke(-(y * w + x + 1) - hwoff, inthash(palnum + rng))
vm.poke(-(y * w + x + 1) - hwoff, inthash(palnum + rng))
}
}
for (var y = 360; y < h; y++) {
for (var x = 0; x < w; x++) {
var palnum = 240 + Math.floor(x / 35)
poke.invoke(-(y * w + x + 1) - hwoff, palnum)
vm.poke(-(y * w + x + 1) - hwoff, palnum)
}
}
for (var k = 0; k < 2240; k++) {
poke.invoke(-(254912 + k + 1) - hwoff, -2) // white
poke.invoke(-(254912 + 2240 + k + 1) - hwoff, -1) // transparent
poke.invoke(-(254912 + 2240*2 + k + 1) - hwoff, Math.round(Math.random() * 255))
for (var k = 0; k < 2560; k++) {
vm.poke(-(253952 + k + 1) - hwoff, -2) // white
vm.poke(-(253952 + 2560 + k + 1) - hwoff, -1) // transparent
vm.poke(-(253952 + 2560*2 + k + 1) - hwoff, Math.round(Math.random() * 255))
}
rng = inthash(rng)
var tend = nanotime.invoke()
var tend = vm.nanoTime()
print("Apparent FPS: " + (1000000000 / (tend - tstart)))
}
@@ -250,7 +272,7 @@ int rng = Math.floor(Math.random() * 2147483647) + 1;
while (true) {
long tstart = nanotime.invoke();
long tstart = nanoTime.invoke();
for (int y1 = 0; y1 < 360; y1++) {
for (int x1 = 0; x1 < w; x1++) {
@@ -266,15 +288,15 @@ while (true) {
}
}
for (int k = 0; k < 2240; k++) {
poke.invoke(-(254912 + k + 1) - hwoff, -2); // white
poke.invoke(-(254912 + 2240 + k + 1) - hwoff, -1); // transparent
poke.invoke(-(254912 + 2240*2 + k + 1) - hwoff, Math.round(Math.random() * 255));
for (int k = 0; k < 2560; k++) {
poke.invoke(-(253952 + k + 1) - hwoff, -2); // white
poke.invoke(-(253952 + 2560 + k + 1) - hwoff, -1); // transparent
poke.invoke(-(253952 + 2560*2 + k + 1) - hwoff, Math.round(Math.random() * 255));
}
rng = inthash(rng);
long tend = nanotime.invoke();
long tend = nanoTime.invoke();
System.out.println("Apparent FPS: " + (1000000000.0 / (tend - tstart)));
}
@@ -301,7 +323,7 @@ rng = random.randint(1, 2147483647)
while True:
tstart = nanotime.invoke()
tstart = nanoTime.invoke()
for y1 in range(0, 360):
for x1 in range(0, w):
@@ -313,14 +335,14 @@ while True:
palnum = 240 + int(x2 / 35)
poke.invoke(-(y2 * w + x2 + 1) - hwoff, palnum)
for k in range(0, 2240):
poke.invoke(-(254912 + k + 1) - hwoff, -2)
poke.invoke(-(254912 + 2240 + k + 1) - hwoff, -1)
poke.invoke(-(254912 + 2240*2 + k + 1) - hwoff, random.randint(0, 255))
for k in range(0, 2560):
poke.invoke(-(253952 + k + 1) - hwoff, -2)
poke.invoke(-(253952 + 2560 + k + 1) - hwoff, -1)
poke.invoke(-(253952 + 2560*2 + k + 1) - hwoff, random.randint(0, 255))
rng = inthash(rng)
tend = nanotime.invoke()
tend = nanoTime.invoke()
print("Apparent FPS: " + str(1000000000.0 / (tend - tstart)))

View File

@@ -0,0 +1,22 @@
package net.torvald.tsvm
import net.torvald.tsvm.peripheral.GraphicsAdapter
/**
* Pass the instance of the class to the ScriptEngine's binding, preferably under the namespace of "vm"
*/
class VMJSR223Delegate(val vm: VM) {
fun poke(addr: Int, value: Int) = vm.poke(addr.toLong(), value.toByte())
fun peek(addr: Int) = vm.peek(addr.toLong())
fun nanoTime() = System.nanoTime()
fun dmagload(from: Int, to: Int, length: Int) {
val periid = vm.findPeribyType("gpu")
if (periid == null)
throw IllegalStateException("GPU not found")
else {
(vm.peripheralTable[periid].peripheral as GraphicsAdapter).bulkLoad(vm, from.toLong(), to.toLong(), length.toLong())
}
}
}

View File

@@ -1,10 +0,0 @@
package net.torvald.tsvm
class VMKotlinAdapter(val vm: VM) {
/*fun getClassLoader(): ClassLoader {
val cl = object : ClassLoader() {
}
}*/
}

View File

@@ -52,10 +52,10 @@ object VMRunnerFactory {
private val bind = context.getBindings(ScriptContext.ENGINE_SCOPE)
init {
bind.put("zzz", 42)
bind.put("vm", vm) // TODO use delegator class to access peripheral (do not expose VM itself)
bind.put("poke", { a: Long, b: Byte -> vm.poke(a, b) }) // kts: lambda does not work...
bind.put("nanotime", { System.nanoTime() })
bind.put("vm", VMJSR223Delegate(vm)) // TODO use delegator class to access peripheral (do not expose VM itself)
bind.put("graphics", GraphicsJSR223Delegate(vm))
//bind.put("poke", { a: Long, b: Byte -> vm.poke(a, b) }) // kts: lambda does not work...
//bind.put("nanotime", { System.nanoTime() })
}
override fun executeCommand(command: String) {

View File

@@ -1,15 +1,14 @@
package net.torvald.tsvm.peripheral
import com.badlogic.gdx.Gdx
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 net.torvald.UnsafeHelper
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import net.torvald.tsvm.AppLoader
import net.torvald.tsvm.VM
import net.torvald.tsvm.kB
import sun.nio.ch.DirectBuffer
import kotlin.experimental.and
class GraphicsAdapter : PeriBase {
@@ -21,7 +20,7 @@ class GraphicsAdapter : PeriBase {
val channel = it % 4
rgba.shr((3 - channel) * 8).and(255) / 255f
}
private val chrrom0 = Texture("./EGA8x14.png")
private val chrrom0 = Texture("./FontROM7x14.png")
private val faketex: Texture
private val spriteAndTextArea = UnsafeHelper.allocate(10660L)
@@ -34,11 +33,11 @@ class GraphicsAdapter : PeriBase {
private var graphicsUseSprites = false
private var lastUsedColour = (-1).toByte()
private var currentChrRom = 0
private var chrWidth = 8f
private var chrWidth = 7f
private var chrHeight = 14f
private var ttyFore = 254
private var ttyBack = 255
private var ttyFore: Int = 254 // cannot be Byte
private var ttyBack: Int = 255 // cannot be Byte
private val textForePixmap = Pixmap(TEXT_COLS, TEXT_ROWS, Pixmap.Format.RGBA8888)
private val textBackPixmap = Pixmap(TEXT_COLS, TEXT_ROWS, Pixmap.Format.RGBA8888)
@@ -48,7 +47,13 @@ class GraphicsAdapter : PeriBase {
private var textBackTex = Texture(textBackPixmap)
private var textTex = Texture(textPixmap)
private fun getTtyCursorPos() = spriteAndTextArea.getShort(3938L) % TEXT_COLS to spriteAndTextArea.getShort(3938L) / TEXT_COLS
private val memTextCursorPosOffset = 2978L
private val memTextForeOffset = 2980L
private val memTextBackOffset = 2980L + 2560
private val memTextOffset = 2980L + 2560 + 2560
private fun getTtyCursorPos() = spriteAndTextArea.getShort(memTextCursorPosOffset) % TEXT_COLS to spriteAndTextArea.getShort(3938L) / TEXT_COLS
private fun toTtyTextOffset(x: Int, y: Int) = y * TEXT_COLS + x
init {
framebuffer.blending = Pixmap.Blending.None
@@ -62,7 +67,10 @@ class GraphicsAdapter : PeriBase {
faketex = Texture(pm)
pm.dispose()
spriteAndTextArea.fillWith(0)
// initialise with NONZERO value; value zero corresponds with opaque black, and it will paint the whole screen black
// when in text mode, and that's undesired behaviour
// -1 is preferred because it points to the colour CLEAR, and it's constant.
spriteAndTextArea.fillWith(-1)
}
override fun peek(addr: Long): Byte? {
@@ -88,6 +96,10 @@ class GraphicsAdapter : PeriBase {
lastUsedColour = byte
framebuffer.drawPixel(adi % WIDTH, adi / WIDTH, bi.shl(24))
}
250883L -> {
unusedArea[adi - 250880] = byte
runCommand(byte)
}
in 250880 until 250972 -> unusedArea[adi - 250880] = byte
in 250972 until 261632 -> spriteAndTextArea[addr - 250972] = byte
in 261632 until 262144 -> pokePalette(adi - 261632, byte)
@@ -132,6 +144,64 @@ class GraphicsAdapter : PeriBase {
TODO("Not yet implemented")
}
private fun runCommand(opcode: Byte) {
val arg1 = unusedArea[4].toInt().and(255)
val arg2 = unusedArea[5].toInt().and(255)
when (opcode.toInt()) {
1 -> {
for (it in 0 until 1024) {
val rgba = DEFAULT_PALETTE[it / 4]
val channel = it % 4
rgba.shr((3 - channel) * 8).and(255) / 255f
}
}
2 -> {
framebuffer.setColor(
paletteOfFloats[arg1 * 4],
paletteOfFloats[arg1 * 4 + 1],
paletteOfFloats[arg1 * 4 + 2],
paletteOfFloats[arg1 * 4 + 3]
)
framebuffer.fill()
}
}
}
/**
* @param from memory address (pointer) on the VM's user memory. Because of how the VM is coded, only the user space is eligible for move.
* @param to memory "offset" in Graphics Adapter's memory space, starts from zero.
* @param length how many bytes should be moved
*/
fun bulkLoad(vm: VM, from: Long, to: Long, length: Long) {
UnsafeHelper.unsafe.copyMemory(null, vm.usermem.ptr + from, (framebuffer.pixels as DirectBuffer).address(), to, length)
}
private fun putChar(x: Int, y: Int, text: Byte, foreColour: Byte = ttyFore.toByte(), backColour: Byte = ttyBack.toByte()) {
val textOff = toTtyTextOffset(x, y)
spriteAndTextArea[memTextForeOffset + textOff] = foreColour
spriteAndTextArea[memTextBackOffset + textOff] = backColour
spriteAndTextArea[memTextOffset + textOff] = text
}
private fun advanceCursor() {
spriteAndTextArea.setShort(
memTextCursorPosOffset,
((spriteAndTextArea.getShort(memTextCursorPosOffset) + 1) % (TEXT_COLS * TEXT_ROWS)).toShort()
)
}
// how TTY should work with all those ASCII control characters
fun print(char: Byte) {
val (cx, cy) = getTtyCursorPos()
when (char) {
in 0x20..0x7E, in 0x80..0xFF -> {
putChar(cx, cy, char)
advanceCursor()
}
}
}
override fun dispose() {
framebuffer.dispose()
rendertex.dispose()
@@ -178,7 +248,6 @@ class GraphicsAdapter : PeriBase {
// draw framebuffer
batch.draw(rendertex, x, y)
// draw texts or sprites
batch.color = Color.WHITE
@@ -190,9 +259,9 @@ class GraphicsAdapter : PeriBase {
for (y in 0 until TEXT_ROWS) {
for (x in 0 until TEXT_COLS) {
val addr = y.toLong() * TEXT_COLS + x
val char = spriteAndTextArea[3940 + 2240 + 2240 + addr].toInt().and(255)
val back = spriteAndTextArea[3940 + 2240 + addr].toInt().and(255)
val fore = spriteAndTextArea[3940 + addr].toInt().and(255)
val char = spriteAndTextArea[memTextOffset + addr].toInt().and(255)
val back = spriteAndTextArea[memTextBackOffset + addr].toInt().and(255)
val fore = spriteAndTextArea[memTextForeOffset + addr].toInt().and(255)
textPixmap.setColor(Color(0f, 0f, char / 255f, 1f))
textPixmap.drawPixel(x, y)
@@ -232,7 +301,7 @@ class GraphicsAdapter : PeriBase {
batch.shader = null
if (textCursorIsOn) {
/*if (textCursorIsOn) {
batch.color = Color(
paletteOfFloats[4 * ttyFore],
paletteOfFloats[4 * ttyFore + 1],
@@ -241,7 +310,7 @@ class GraphicsAdapter : PeriBase {
)
val (cursorx, cursory) = getTtyCursorPos()
batch.draw(faketex, cursorx * chrWidth, (TEXT_ROWS - cursory - 1) * chrHeight, chrWidth, chrHeight)
}
}*/
}
else {
// draw sprites
@@ -293,7 +362,7 @@ class GraphicsAdapter : PeriBase {
companion object {
const val WIDTH = 560
const val HEIGHT = 448
const val TEXT_COLS = 70
const val TEXT_COLS = 80
const val TEXT_ROWS = 32
val VRAM_SIZE = 256.kB()