diff --git a/assets/disk0/tvdos/bin/playtav.js b/assets/disk0/tvdos/bin/playtav.js index cad0add..a63c389 100644 --- a/assets/disk0/tvdos/bin/playtav.js +++ b/assets/disk0/tvdos/bin/playtav.js @@ -112,7 +112,7 @@ if (fullFilePathStr.startsWith('$:/TAPE') || fullFilePathStr.startsWith('$:\\TAP con.clear() con.curs_set(0) -graphics.setGraphicsMode(4) // 4096-colour mode +graphics.setGraphicsMode(5) // 32-bit colour mode graphics.clearPixels(0) graphics.clearPixels2(0) diff --git a/tsvm_core/src/net/torvald/tsvm/GraphicsJSR223Delegate.kt b/tsvm_core/src/net/torvald/tsvm/GraphicsJSR223Delegate.kt index 2a6c5ce..75137e7 100644 --- a/tsvm_core/src/net/torvald/tsvm/GraphicsJSR223Delegate.kt +++ b/tsvm_core/src/net/torvald/tsvm/GraphicsJSR223Delegate.kt @@ -1454,6 +1454,7 @@ class GraphicsJSR223Delegate(private val vm: VM) { */ fun uploadRGBToFramebuffer(rgbAddr: Long, width: Int, height: Int, frameCount: Int, resizeToFull: Boolean) { val gpu = (vm.peripheralTable[1].peripheral as GraphicsAdapter) + val graphicsMode = gpu.graphicsMode val rgbAddrIncVec = if (rgbAddr >= 0) 1 else -1 @@ -1471,6 +1472,10 @@ class GraphicsJSR223Delegate(private val vm: VM) { val rgbBulkBuffer = ByteArray(chunkSize * 3) val rgChunk = ByteArray(chunkSize) val baChunk = ByteArray(chunkSize) + val rChunk = ByteArray(chunkSize) + val gChunk = ByteArray(chunkSize) + val bChunk = ByteArray(chunkSize) + val aChunk = ByteArray(chunkSize); aChunk.fill(-1) while (pixelsProcessed < totalNativePixels) { val pixelsInChunk = kotlin.math.min(chunkSize, totalNativePixels - pixelsProcessed) @@ -1490,24 +1495,53 @@ class GraphicsJSR223Delegate(private val vm: VM) { val g = rgbBulkBuffer[i*3 + 1].toUint() val b = rgbBulkBuffer[i*3 + 2].toUint() - // Apply Bayer dithering and convert to 4-bit - val r4 = ditherValue(r, videoX, videoY, frameCount) - val g4 = ditherValue(g, videoX, videoY, frameCount) - val b4 = ditherValue(b, videoX, videoY, frameCount) + if (graphicsMode == 4) { + // Apply Bayer dithering and convert to 4-bit + val r4 = ditherValue(r, videoX, videoY, frameCount) + val g4 = ditherValue(g, videoX, videoY, frameCount) + val b4 = ditherValue(b, videoX, videoY, frameCount) - // Pack RGB values and store in chunk arrays for batch processing - rgChunk[i] = ((r4 shl 4) or g4).toByte() - baChunk[i] = ((b4 shl 4) or 15).toByte() + // Pack RGB values and store in chunk arrays for batch processing + rgChunk[i] = ((r4 shl 4) or g4).toByte() + baChunk[i] = ((b4 shl 4) or 15).toByte() - // Write directly to framebuffer position - val nativePos = videoY * nativeWidth + videoX + } + else if (graphicsMode == 5) { + rChunk[i] = r.toByte() + gChunk[i] = g.toByte() + bChunk[i] = b.toByte() + } + } + val pixelIndex = pixelsProcessed + val videoY = pixelIndex / width + val videoX = pixelIndex % width + val nativePos = videoY * nativeWidth + videoX + if (graphicsMode == 4) { UnsafeHelper.memcpyRaw( - rgChunk, UnsafeHelper.getArrayOffset(rgChunk) + i, - null, gpu.framebuffer.ptr + nativePos, 1L + rgChunk, UnsafeHelper.getArrayOffset(rgChunk), + null, gpu.framebuffer.ptr + nativePos, pixelsInChunk.toLong() ) UnsafeHelper.memcpyRaw( - baChunk, UnsafeHelper.getArrayOffset(baChunk) + i, - null, gpu.framebuffer2!!.ptr + nativePos, 1L + baChunk, UnsafeHelper.getArrayOffset(baChunk), + null, gpu.framebuffer2!!.ptr + nativePos, pixelsInChunk.toLong() + ) + } + else if (graphicsMode == 5) { + UnsafeHelper.memcpyRaw( + rChunk, UnsafeHelper.getArrayOffset(rChunk), + null, gpu.framebuffer.ptr + nativePos, pixelsInChunk.toLong() + ) + UnsafeHelper.memcpyRaw( + gChunk, UnsafeHelper.getArrayOffset(gChunk), + null, gpu.framebuffer2!!.ptr + nativePos, pixelsInChunk.toLong() + ) + UnsafeHelper.memcpyRaw( + bChunk, UnsafeHelper.getArrayOffset(bChunk), + null, gpu.framebuffer3!!.ptr + nativePos, pixelsInChunk.toLong() + ) + UnsafeHelper.memcpyRaw( + aChunk, UnsafeHelper.getArrayOffset(aChunk), + null, gpu.framebuffer4!!.ptr + nativePos, pixelsInChunk.toLong() ) } @@ -1520,10 +1554,14 @@ class GraphicsJSR223Delegate(private val vm: VM) { val scaleY = height.toFloat() / nativeHeight.toFloat() // Process native pixels in 8KB chunks - val chunkSize = 8192 + val chunkSize = 32768 val rgChunk = ByteArray(chunkSize) val baChunk = ByteArray(chunkSize) - + val rChunk = ByteArray(chunkSize) + val gChunk = ByteArray(chunkSize) + val bChunk = ByteArray(chunkSize) + val aChunk = ByteArray(chunkSize); aChunk.fill(-1) + var pixelsProcessed = 0 while (pixelsProcessed < totalNativePixels) { @@ -1545,23 +1583,54 @@ class GraphicsJSR223Delegate(private val vm: VM) { val g = rgb[1] val b = rgb[2] - // Apply Bayer dithering and convert to 4-bit using native coordinates - val r4 = ditherValue(r, nativeX, nativeY, frameCount) - val g4 = ditherValue(g, nativeX, nativeY, frameCount) - val b4 = ditherValue(b, nativeX, nativeY, frameCount) + if (graphicsMode == 4) { + // Apply Bayer dithering and convert to 4-bit using native coordinates + val r4 = ditherValue(r, nativeX, nativeY, frameCount) + val g4 = ditherValue(g, nativeX, nativeY, frameCount) + val b4 = ditherValue(b, nativeX, nativeY, frameCount) - // Pack and store in chunk buffers - rgChunk[i] = ((r4 shl 4) or g4).toByte() - baChunk[i] = ((b4 shl 4) or 15).toByte() + // Pack and store in chunk buffers + rgChunk[i] = ((r4 shl 4) or g4).toByte() + baChunk[i] = ((b4 shl 4) or 15).toByte() + } + else if (graphicsMode == 5) { + rChunk[i] = r.toByte() + gChunk[i] = g.toByte() + bChunk[i] = b.toByte() + } + } + val pixelIndex = pixelsProcessed + val videoY = pixelIndex / width + val videoX = pixelIndex % width + val nativePos = videoY * nativeWidth + videoX + if (graphicsMode == 4) { + UnsafeHelper.memcpyRaw( + rgChunk, UnsafeHelper.getArrayOffset(rgChunk), + null, gpu.framebuffer.ptr + nativePos, pixelsInChunk.toLong() + ) + UnsafeHelper.memcpyRaw( + baChunk, UnsafeHelper.getArrayOffset(baChunk), + null, gpu.framebuffer2!!.ptr + nativePos, pixelsInChunk.toLong() + ) + } + else if (graphicsMode == 5) { + UnsafeHelper.memcpyRaw( + rChunk, UnsafeHelper.getArrayOffset(rChunk), + null, gpu.framebuffer.ptr + nativePos, pixelsInChunk.toLong() + ) + UnsafeHelper.memcpyRaw( + gChunk, UnsafeHelper.getArrayOffset(gChunk), + null, gpu.framebuffer2!!.ptr + nativePos, pixelsInChunk.toLong() + ) + UnsafeHelper.memcpyRaw( + bChunk, UnsafeHelper.getArrayOffset(bChunk), + null, gpu.framebuffer3!!.ptr + nativePos, pixelsInChunk.toLong() + ) + UnsafeHelper.memcpyRaw( + aChunk, UnsafeHelper.getArrayOffset(aChunk), + null, gpu.framebuffer4!!.ptr + nativePos, pixelsInChunk.toLong() + ) } - - // Write pixels to their sequential positions in framebuffer - UnsafeHelper.memcpyRaw( - rgChunk, UnsafeHelper.getArrayOffset(rgChunk), - null, gpu.framebuffer.ptr + pixelsProcessed, pixelsInChunk.toLong()) - UnsafeHelper.memcpyRaw( - baChunk, UnsafeHelper.getArrayOffset(baChunk), - null, gpu.framebuffer2!!.ptr + pixelsProcessed, pixelsInChunk.toLong()) pixelsProcessed += pixelsInChunk } diff --git a/tsvm_core/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt b/tsvm_core/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt index e5729f7..b43e571 100644 --- a/tsvm_core/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt +++ b/tsvm_core/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt @@ -50,7 +50,7 @@ data class SuperGraphicsAddonConfig( val bankCount: Int = 1 ) -class ReferenceGraphicsAdapter(assetsRoot: String, vm: VM) : GraphicsAdapter(assetsRoot, vm, GraphicsAdapter.DEFAULT_CONFIG_COLOR_CRT, SuperGraphicsAddonConfig(2)) +class ReferenceGraphicsAdapter(assetsRoot: String, vm: VM) : GraphicsAdapter(assetsRoot, vm, GraphicsAdapter.DEFAULT_CONFIG_COLOR_CRT, SuperGraphicsAddonConfig(4)) class ReferenceGraphicsAdapter2(assetsRoot: String, vm: VM) : RemoteGraphicsAdapter(assetsRoot, vm, GraphicsAdapter.DEFAULT_CONFIG_COLOR_CRT, SuperGraphicsAddonConfig(2)) class ReferenceLikeLCD(assetsRoot: String, vm: VM) : GraphicsAdapter(assetsRoot, vm, GraphicsAdapter.DEFAULT_CONFIG_PMLCD) @@ -76,6 +76,9 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi internal val framebuffer = UnsafeHelper.allocate(WIDTH.toLong() * HEIGHT, this)//Pixmap(WIDTH, HEIGHT, Pixmap.Format.Alpha) internal val framebuffer2 = if (sgr.bankCount >= 2) UnsafeHelper.allocate(WIDTH.toLong() * HEIGHT, this) else null + internal val framebuffer3 = if (sgr.bankCount >= 3) UnsafeHelper.allocate(WIDTH.toLong() * HEIGHT, this) else null + internal val framebuffer4 = if (sgr.bankCount >= 4) UnsafeHelper.allocate(WIDTH.toLong() * HEIGHT, this) else null + internal val framebufferOut = Pixmap(WIDTH, HEIGHT, Pixmap.Format.RGBA8888) protected var rendertex = Texture(1, 1, Pixmap.Format.RGBA8888) internal val paletteOfFloats = FloatArray(1024) { @@ -130,7 +133,7 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi private val outFBOregion = Array(2) { TextureRegion(outFBOs[it].colorBufferTexture) } private val outFBObatch = SpriteBatch(1000, DefaultGL32Shaders.createSpriteBatchShader()) - private var graphicsMode = 0 + var graphicsMode = 0 private var layerArrangement = 0 @@ -227,7 +230,19 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi override fun peek(addr: Long): Byte? { val adi = addr.toInt() - if (framebuffer2 != null && addr >= 262144) { + if (framebuffer4 != null && addr >= 524288) { + return when (addr - 524288) { + in 0 until 250880 -> framebuffer4[addr - 524288] + else -> null + } + } + else if (framebuffer3 != null && addr >= 393216) { + return when (addr - 393216) { + in 0 until 250880 -> framebuffer3[addr - 393216] + else -> null + } + } + else if (framebuffer2 != null && addr >= 262144) { return when (addr - 262144) { in 0 until 250880 -> framebuffer2[addr - 262144] else -> null @@ -275,6 +290,24 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi override fun poke(addr: Long, byte: Byte) { val adi = addr.toInt() val bi = byte.toInt().and(255) + if (framebuffer4 != null) { + when (addr - 524288) { + in 0 until 250880 -> { + lastUsedColour = byte + framebuffer4[addr - 524288] = byte + return + } + } + } + if (framebuffer3 != null) { + when (addr - 393216) { + in 0 until 250880 -> { + lastUsedColour = byte + framebuffer3[addr - 393216] = byte + return + } + } + } if (framebuffer2 != null) { when (addr - 262144) { in 0 until 250880 -> { @@ -899,6 +932,8 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi // textShader.tryDispose() framebuffer.destroy() framebuffer2?.destroy() + framebuffer3?.destroy() + framebuffer4?.destroy() framebufferOut.tryDispose() rendertex.tryDispose() textArea.destroy() @@ -942,7 +977,27 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi chrrom.pixels.position(0) framebufferOut.setColor(-1);framebufferOut.fill() - if (graphicsMode == 4 && framebuffer2 != null) { + if (graphicsMode == 5 && framebuffer4 != null && framebuffer3 != null && framebuffer2 != null) { + for (y in 0 until HEIGHT) { + var xoff = scanlineOffsets[2L * y].toUint() or scanlineOffsets[2L * y + 1].toUint().shl(8) + if (xoff.and(0x8000) != 0) xoff = xoff or 0xFFFF0000.toInt() + val xs = (0 + xoff).coerceIn(0, WIDTH - 1)..(WIDTH - 1 + xoff).coerceIn(0, WIDTH - 1) + + if (xoff in -(WIDTH - 1) until WIDTH) { + for (x in xs) { + val r = framebuffer[y.toLong() * WIDTH + (x - xoff)].toUint() // coerceIn not required as (x - xoff) never escapes 0..559 + val g = framebuffer2[y.toLong() * WIDTH + (x - xoff)].toUint() // coerceIn not required as (x - xoff) never escapes 0..559 + val b = framebuffer3[y.toLong() * WIDTH + (x - xoff)].toUint() // coerceIn not required as (x - xoff) never escapes 0..559 + val a = framebuffer4[y.toLong() * WIDTH + (x - xoff)].toUint() // coerceIn not required as (x - xoff) never escapes 0..559 + framebufferOut.setColor( + r.shl(24) or g.shl(16) or b.shl(8) or a + ) + framebufferOut.drawPixel(x, y) + } + } + } + } + else if (graphicsMode == 4 && framebuffer2 != null) { for (y in 0 until HEIGHT) { var xoff = scanlineOffsets[2L * y].toUint() or scanlineOffsets[2L * y + 1].toUint().shl(8) if (xoff.and(0x8000) != 0) xoff = xoff or 0xFFFF0000.toInt() @@ -968,7 +1023,7 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi } } else if (graphicsMode == 3 && framebuffer2 != null) { - val layerOrder = (if (graphicsMode == 1) LAYERORDERS4 else LAYERORDERS2)[layerArrangement] + val layerOrder = LAYERORDERS2[layerArrangement] val fb1 = if (layerOrder[0] == 0) framebuffer else framebuffer2 val fb2 = if (layerOrder[0] == 0) framebuffer2 else framebuffer