mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-11 15:24:05 +09:00
graphics adapter: render framebuffer is back to 32bpp to support direct colour mode
This commit is contained in:
@@ -253,6 +253,69 @@ MMIO
|
|||||||
current TTY background colour (useful for print() function)
|
current TTY background colour (useful for print() function)
|
||||||
11 RO
|
11 RO
|
||||||
Number of Banks, or VRAM size (1 = 256 kB, max 4)
|
Number of Banks, or VRAM size (1 = 256 kB, max 4)
|
||||||
|
12 RW
|
||||||
|
Graphics Mode
|
||||||
|
0: 560x448, 256 Colours, 1 layer
|
||||||
|
1: 280x224, 256 Colours, 4 layers
|
||||||
|
2: 280x224, 4096 Colours, 2 layers
|
||||||
|
3: 560x448, 256 Colours, 2 layers (if bank 2 is not installed, will fall back to mode 0)
|
||||||
|
4: 560x448, 4096 Colours, 1 layer (if bank 2 is not installed, will fall back to mode 0)
|
||||||
|
4096 is also known as "direct colour mode" (4096 colours * 16 transparency -> 65536 colours)
|
||||||
|
13 RW
|
||||||
|
Layer Arrangement
|
||||||
|
If 4 layers are used:
|
||||||
|
Num LO<->HI
|
||||||
|
0 1234
|
||||||
|
1 1243
|
||||||
|
2 1324
|
||||||
|
3 1342
|
||||||
|
4 1423
|
||||||
|
5 1432
|
||||||
|
6 2134
|
||||||
|
7 2143
|
||||||
|
8 2314
|
||||||
|
9 2341
|
||||||
|
10 2413
|
||||||
|
11 2431
|
||||||
|
12 3124
|
||||||
|
13 3142
|
||||||
|
14 3214
|
||||||
|
15 3241
|
||||||
|
16 3412
|
||||||
|
17 3421
|
||||||
|
18 4123
|
||||||
|
19 4132
|
||||||
|
20 4213
|
||||||
|
21 4231
|
||||||
|
22 4312
|
||||||
|
23 4321
|
||||||
|
If 2 layers are used:
|
||||||
|
Num LO<->HI
|
||||||
|
0 12
|
||||||
|
1 12
|
||||||
|
2 12
|
||||||
|
3 12
|
||||||
|
4 12
|
||||||
|
5 12
|
||||||
|
6 12
|
||||||
|
7 21
|
||||||
|
8 21
|
||||||
|
9 21
|
||||||
|
10 21
|
||||||
|
11 21
|
||||||
|
12 12
|
||||||
|
13 12
|
||||||
|
14 21
|
||||||
|
15 21
|
||||||
|
16 12
|
||||||
|
17 21
|
||||||
|
18 12
|
||||||
|
19 12
|
||||||
|
20 21
|
||||||
|
21 21
|
||||||
|
22 12
|
||||||
|
23 21
|
||||||
|
If 1 layer is used, this field will do nothing and always fall back to 0
|
||||||
|
|
||||||
Text-mode-font-ROM is immutable and does not belong to VRAM
|
Text-mode-font-ROM is immutable and does not belong to VRAM
|
||||||
Even in the text mode framebuffer is still being drawn onto the screen, and the texts are drawn on top of it
|
Even in the text mode framebuffer is still being drawn onto the screen, and the texts are drawn on top of it
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
|
|||||||
protected val TAB_SIZE = 8
|
protected val TAB_SIZE = 8
|
||||||
|
|
||||||
internal val framebuffer = Pixmap(WIDTH, HEIGHT, Pixmap.Format.Alpha)
|
internal val framebuffer = Pixmap(WIDTH, HEIGHT, Pixmap.Format.Alpha)
|
||||||
internal val framebuffer2 = Pixmap(WIDTH, HEIGHT, Pixmap.Format.Alpha)
|
internal val framebuffer2 = Pixmap(WIDTH, HEIGHT, Pixmap.Format.RGBA8888)
|
||||||
protected var rendertex = Texture(1, 1, Pixmap.Format.RGBA8888)
|
protected var rendertex = Texture(1, 1, Pixmap.Format.RGBA8888)
|
||||||
internal val paletteOfFloats = FloatArray(1024) {
|
internal val paletteOfFloats = FloatArray(1024) {
|
||||||
val rgba = DEFAULT_PALETTE[it / 4]
|
val rgba = DEFAULT_PALETTE[it / 4]
|
||||||
@@ -108,6 +108,9 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
|
|||||||
private val outFBOregion = Array(2) { TextureRegion(outFBOs[it].colorBufferTexture) }
|
private val outFBOregion = Array(2) { TextureRegion(outFBOs[it].colorBufferTexture) }
|
||||||
private val outFBObatch = SpriteBatch()
|
private val outFBObatch = SpriteBatch()
|
||||||
|
|
||||||
|
private var graphicsMode = 0
|
||||||
|
private var layerArrangement = 0
|
||||||
|
|
||||||
|
|
||||||
private val memTextCursorPosOffset = 0L
|
private val memTextCursorPosOffset = 0L
|
||||||
private val memTextForeOffset = 2L
|
private val memTextForeOffset = 2L
|
||||||
@@ -269,6 +272,8 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
|
|||||||
9L -> ttyFore.toByte()
|
9L -> ttyFore.toByte()
|
||||||
10L -> ttyBack.toByte()
|
10L -> ttyBack.toByte()
|
||||||
11L -> sgr.bankCount.toByte()
|
11L -> sgr.bankCount.toByte()
|
||||||
|
12L -> graphicsMode.toByte()
|
||||||
|
13L -> layerArrangement.toByte()
|
||||||
|
|
||||||
in 0 until VM.MMIO_SIZE -> -1
|
in 0 until VM.MMIO_SIZE -> -1
|
||||||
else -> null
|
else -> null
|
||||||
@@ -276,7 +281,16 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun mmio_write(addr: Long, byte: Byte) {
|
override fun mmio_write(addr: Long, byte: Byte) {
|
||||||
TODO("Not yet implemented")
|
val bi = byte.toUint()
|
||||||
|
when (addr) {
|
||||||
|
6L -> setTextmodeAttributes(byte)
|
||||||
|
7L -> setGraphicsAttributes(byte)
|
||||||
|
9L -> { ttyFore = bi }
|
||||||
|
10L -> { ttyBack = bi }
|
||||||
|
12L -> { graphicsMode = bi }
|
||||||
|
13L -> { layerArrangement = bi }
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun runCommand(opcode: Byte) {
|
private fun runCommand(opcode: Byte) {
|
||||||
@@ -713,6 +727,8 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
|
|||||||
unusedArea[1].toInt().and(15).toFloat() / 15f,
|
unusedArea[1].toInt().and(15).toFloat() / 15f,
|
||||||
unusedArea[2].toInt().and(15).toFloat() / 15f, 1f)
|
unusedArea[2].toInt().and(15).toFloat() / 15f, 1f)
|
||||||
|
|
||||||
|
private val isRefSize = (config.width == 560 && config.height == 448)
|
||||||
|
|
||||||
open fun render(delta: Float, uiBatch: SpriteBatch, xoff: Float, yoff: Float, flipY: Boolean = false, uiFBO: FrameBuffer? = null) {
|
open fun render(delta: Float, uiBatch: SpriteBatch, xoff: Float, yoff: Float, flipY: Boolean = false, uiFBO: FrameBuffer? = null) {
|
||||||
uiFBO?.end()
|
uiFBO?.end()
|
||||||
|
|
||||||
@@ -722,21 +738,64 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
|
|||||||
chrrom.pixels.position(0)
|
chrrom.pixels.position(0)
|
||||||
|
|
||||||
framebuffer2.setColor(-1);framebuffer2.fill()
|
framebuffer2.setColor(-1);framebuffer2.fill()
|
||||||
for (y in 0 until config.height) {
|
if (isRefSize && graphicsMode == 1) {
|
||||||
var xoff = unusedArea[20L + 2*y].toUint().shl(8) or unusedArea[20L + 2*y + 1].toUint()
|
val layerOrder = LAYERORDERS4[layerArrangement]
|
||||||
if (xoff.and(0x8000) != 0) xoff = xoff or 0xFFFF0000.toInt()
|
for (y in 0..223) {
|
||||||
val xs = (0+xoff).coerceIn(0,config.width-1) .. (config.width-1+xoff).coerceIn(0,config.width-1)
|
var xoff = unusedArea[20L + 2 * y].toUint().shl(8) or unusedArea[20L + 2 * y + 1].toUint()
|
||||||
|
if (xoff.and(0x8000) != 0) xoff = xoff or 0xFFFF0000.toInt()
|
||||||
|
val xs =
|
||||||
|
(0 + xoff).coerceIn(0, 279)..(279 + xoff).coerceIn(0, 279)
|
||||||
|
|
||||||
if (xoff in -(config.width-1)..config.width-1) {
|
if (xoff in -(280 - 1) until 280) {
|
||||||
for (x in xs) {
|
for (x in xs) {
|
||||||
// this only works because framebuffer is guaranteed to be 8bpp
|
val colour = layerOrder.map {
|
||||||
framebuffer2.pixels.put(y*config.width+x,
|
val colourIndex = framebuffer.pixels.get(280 * 224 * it + (y * 224 + x)).toUint()
|
||||||
framebuffer.pixels.get(y*config.width + (x - xoff)) // coerceIn not required as (x - xoff) never escapes 0..559
|
Color(paletteOfFloats[4*colourIndex], paletteOfFloats[4*colourIndex+1], paletteOfFloats[4*colourIndex+2], paletteOfFloats[4*colourIndex+3])
|
||||||
)
|
}.fold(Color(0)) { dest, src ->
|
||||||
|
// manually alpha compositing
|
||||||
|
// out_color = {src_color * src_alpha + dest_color * dest_alpha * (1-src_alpha)} / out_alpha
|
||||||
|
// see https://gamedev.stackexchange.com/a/115786
|
||||||
|
val outAlpha = (1f - (1f - dest.a) * (1f - src.a)).coerceIn(0.0001f, 1f)
|
||||||
|
|
||||||
|
// src.a + dest.a - src.a*dest.a)
|
||||||
|
Color(
|
||||||
|
(src.r * src.a + dest.r * dest.a * (1f - src.a)) / outAlpha,
|
||||||
|
(src.g * src.a + dest.g * dest.a * (1f - src.a)) / outAlpha,
|
||||||
|
(src.b * src.a + dest.b* dest.a * (1f - src.a)) / outAlpha,
|
||||||
|
outAlpha
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
framebuffer2.setColor(colour)
|
||||||
|
framebuffer2.drawPixel(x*2, y*2)
|
||||||
|
framebuffer2.drawPixel(x*2+1, y*2)
|
||||||
|
framebuffer2.drawPixel(x*2, y*2+1)
|
||||||
|
framebuffer2.drawPixel(x*2+1, y*2+1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
for (y in 0 until config.height) {
|
||||||
|
var xoff = unusedArea[20L + 2 * y].toUint().shl(8) or unusedArea[20L + 2 * y + 1].toUint()
|
||||||
|
if (xoff.and(0x8000) != 0) xoff = xoff or 0xFFFF0000.toInt()
|
||||||
|
val xs =
|
||||||
|
(0 + xoff).coerceIn(0, config.width - 1)..(config.width - 1 + xoff).coerceIn(0, config.width - 1)
|
||||||
|
|
||||||
|
if (xoff in -(config.width - 1) until config.width) {
|
||||||
|
for (x in xs) {
|
||||||
|
// this only works because framebuffer is guaranteed to be 8bpp
|
||||||
|
/*framebuffer2.pixels.put(
|
||||||
|
y * config.width + x,
|
||||||
|
framebuffer.pixels.get(y * config.width + (x - xoff)) // coerceIn not required as (x - xoff) never escapes 0..559
|
||||||
|
)*/
|
||||||
|
val colourIndex = framebuffer.pixels.get(y * config.width + (x - xoff)).toUint() // coerceIn not required as (x - xoff) never escapes 0..559
|
||||||
|
framebuffer2.setColor(paletteOfFloats[4*colourIndex], paletteOfFloats[4*colourIndex+1], paletteOfFloats[4*colourIndex+2], paletteOfFloats[4*colourIndex+3])
|
||||||
|
framebuffer2.drawPixel(x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
chrrom0.dispose()
|
chrrom0.dispose()
|
||||||
chrrom0 = Texture(chrrom)
|
chrrom0 = Texture(chrrom)
|
||||||
@@ -973,7 +1032,7 @@ uniform sampler2D u_texture;
|
|||||||
uniform vec4 pal[256];
|
uniform vec4 pal[256];
|
||||||
|
|
||||||
void main(void) {
|
void main(void) {
|
||||||
gl_FragColor = pal[int(texture2D(u_texture, v_texCoords).a * 255.0)];
|
gl_FragColor = texture2D(u_texture, v_texCoords);
|
||||||
}
|
}
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
@@ -989,7 +1048,8 @@ float intensitySteps = 4.0;
|
|||||||
uniform vec4 lcdBaseCol;
|
uniform vec4 lcdBaseCol;
|
||||||
|
|
||||||
void main(void) {
|
void main(void) {
|
||||||
vec4 palCol = pal[int(texture2D(u_texture, v_texCoords).a * 255.0)];
|
// vec4 palCol = pal[int(texture2D(u_texture, v_texCoords).a * 255.0)];
|
||||||
|
vec4 palCol = texture2D(u_texture, v_texCoords);
|
||||||
float lum = ceil((3.0 * palCol.r + 4.0 * palCol.g + palCol.b) / 8.0 * intensitySteps) / intensitySteps;
|
float lum = ceil((3.0 * palCol.r + 4.0 * palCol.g + palCol.b) / 8.0 * intensitySteps) / intensitySteps;
|
||||||
vec4 outIntensity = vec4(vec3(1.0 - lum), palCol.a);
|
vec4 outIntensity = vec4(vec3(1.0 - lum), palCol.a);
|
||||||
|
|
||||||
@@ -1010,8 +1070,8 @@ float intensitySteps = 4.0;
|
|||||||
uniform vec4 lcdBaseCol;
|
uniform vec4 lcdBaseCol;
|
||||||
|
|
||||||
void main(void) {
|
void main(void) {
|
||||||
vec4 palCol = pal[int(texture2D(u_texture, v_texCoords).a * 255.0)];
|
// vec4 palCol = pal[int(texture2D(u_texture, v_texCoords).a * 255.0)];
|
||||||
float lum = floor((3.0 * palCol.r + 4.0 * palCol.g + palCol.b) / 8.0 * intensitySteps) / intensitySteps;
|
vec4 palCol = texture2D(u_texture, v_texCoords); float lum = floor((3.0 * palCol.r + 4.0 * palCol.g + palCol.b) / 8.0 * intensitySteps) / intensitySteps;
|
||||||
vec4 outIntensity = vec4(vec3(lum), palCol.a);
|
vec4 outIntensity = vec4(vec3(lum), palCol.a);
|
||||||
|
|
||||||
// LCD output will invert the luminosity. That is, normally white colour will be black on PM-LCD.
|
// LCD output will invert the luminosity. That is, normally white colour will be black on PM-LCD.
|
||||||
@@ -1765,6 +1825,60 @@ void main() {
|
|||||||
it.and(255).div(255f)
|
it.and(255).div(255f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val LAYERORDERS4 = listOf( // [drawn first, second, third, fourth], zero-indexed
|
||||||
|
"1234",
|
||||||
|
"1243",
|
||||||
|
"1324",
|
||||||
|
"1342",
|
||||||
|
"1423",
|
||||||
|
"1432",
|
||||||
|
"2134",
|
||||||
|
"2143",
|
||||||
|
"2314",
|
||||||
|
"2341",
|
||||||
|
"2413",
|
||||||
|
"2431",
|
||||||
|
"3124",
|
||||||
|
"3142",
|
||||||
|
"3214",
|
||||||
|
"3241",
|
||||||
|
"3412",
|
||||||
|
"3421",
|
||||||
|
"4123",
|
||||||
|
"4132",
|
||||||
|
"4213",
|
||||||
|
"4231",
|
||||||
|
"4312",
|
||||||
|
"4321",
|
||||||
|
).map { s -> (0..3).map { s[it].toInt() - 61 } }
|
||||||
|
|
||||||
|
val LAYERORDERS2 = listOf( // [drawn first, second], zero-indexed
|
||||||
|
"12",
|
||||||
|
"12",
|
||||||
|
"12",
|
||||||
|
"12",
|
||||||
|
"12",
|
||||||
|
"12",
|
||||||
|
"12",
|
||||||
|
"21",
|
||||||
|
"21",
|
||||||
|
"21",
|
||||||
|
"21",
|
||||||
|
"21",
|
||||||
|
"12",
|
||||||
|
"12",
|
||||||
|
"21",
|
||||||
|
"21",
|
||||||
|
"12",
|
||||||
|
"21",
|
||||||
|
"12",
|
||||||
|
"12",
|
||||||
|
"21",
|
||||||
|
"21",
|
||||||
|
"12",
|
||||||
|
"21",
|
||||||
|
).map { s -> (0..1).map { s[it].toInt() - 61 } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user