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

BIN
FontROM7x14.kra Normal file

Binary file not shown.

BIN
FontROM7x14.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
smpte_bars.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

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()

View File

@@ -44,7 +44,16 @@ Endianness: little
From the start of the memory space:
250880 bytes
Framebuffer
92 bytes
3 bytes
*reserved for future use*
1 byte
command (writing to this memory address changes the status)
1: reset palette to default
2: fill framebuffer with given colour (arg1)
2 bytes
argument for "command" (arg1: Byte, arg2: Byte)
write to this address FIRST and then write to "command" to execute the command
86 bytes
*Unused*
IF graphics_mode THEN
(41 sprites : 260 bytes each -> 10660 bytes)
@@ -56,15 +65,15 @@ IF graphics_mode THEN
256 bytes
16x16 texture for the sprite
ELSE
3938 bytes
2978 bytes
*Unused*
2 bytes
Cursor position in: (y*32 + x)
2240 bytes
2560 bytes
Text foreground colours
2240 bytes
2560 bytes
Text background colours
2240 bytes
2560 bytes
Text buffer of 70x32 (8x14 character size, and yes: actual character data is on the bottom)
FI
512 bytes