From 31fa07579ef9478270ce1e5ab0e60d3b3387e438 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Thu, 2 Dec 2021 15:15:34 +0900 Subject: [PATCH] world radar --- assets/bios/pipcode.bas | 16 ++- .../tsvmperipheral/WorldRadar.kt | 130 ++++++++++++++++++ src/net/torvald/tsvm/VMGUI.kt | 3 + src/net/torvald/tsvm/peripheral/ExtDisp.kt | 4 + test_assets/test_terrain.png | Bin 0 -> 2060 bytes test_assets/test_terrain_out.png | Bin 0 -> 2092 bytes 6 files changed, 149 insertions(+), 4 deletions(-) create mode 100644 src/net/torvald/terrarum/modulecomputers/tsvmperipheral/WorldRadar.kt create mode 100644 test_assets/test_terrain.png create mode 100644 test_assets/test_terrain_out.png diff --git a/assets/bios/pipcode.bas b/assets/bios/pipcode.bas index 409e385..6212a49 100644 --- a/assets/bios/pipcode.bas +++ b/assets/bios/pipcode.bas @@ -1,4 +1,12 @@ -10 FOR K = 0 TO (160*140) -20 POKE(-1048576-K, INT(RND(1)*16)) -30 NEXT -40 GOTO 10 \ No newline at end of file +10 print("polling radar...") +20 s=cput(1,"POLL") +30 if s><0 then goto 900 +40 l=cget(1,0) +41 print("length: "+l+", pixels: "+l/3) +50 for i=0 to l step 3 +60 m=peek(i)*160+peek(i+1) +62 p=peek(i+2) +63 poke(-1048576-m,p) +70 next +899 end +900 print("Polling failed: "+s) diff --git a/src/net/torvald/terrarum/modulecomputers/tsvmperipheral/WorldRadar.kt b/src/net/torvald/terrarum/modulecomputers/tsvmperipheral/WorldRadar.kt new file mode 100644 index 0000000..37cd8ca --- /dev/null +++ b/src/net/torvald/terrarum/modulecomputers/tsvmperipheral/WorldRadar.kt @@ -0,0 +1,130 @@ +package net.torvald.terrarum.modulecomputers.tsvmperipheral + +import com.badlogic.gdx.files.FileHandle +import com.badlogic.gdx.graphics.Pixmap +import net.torvald.tsvm.VM +import net.torvald.tsvm.peripheral.BlockTransferInterface +import net.torvald.tsvm.peripheral.TestDiskDrive +import net.torvald.tsvm.peripheral.trimNull +import java.io.ByteArrayOutputStream + +/** + * Created by minjaesong on 2021-12-02. + */ +class WorldRadar(pngfile: FileHandle) : BlockTransferInterface(false, true) { + + private val W = 160 + private val H = 140 + + private val world = ByteArray(W*H) + + private val AIR = 0.toByte() + private val DIRT = 1.toByte() + private val GRASS = 2.toByte() + private val STONE = 16.toByte() + + private val AIR_OUT = 0.toByte() + private val GRASS_OUT = 2.toByte() + private val DIRT_OUT = 4.toByte() + private val STONE_OUT = 7.toByte() + + init { + statusCode = TestDiskDrive.STATE_CODE_STANDBY + + val worldTex = Pixmap(pngfile) + for (y in 0 until 140) { for (x in 0 until 160) { + val c = worldTex.getPixel(x, y) + world[y * W + x] = when (c) { + 0x46712dff -> GRASS + 0x9b9a9bff.toInt() -> STONE + 0x6a5130ff -> DIRT + else -> AIR + } + }} + + worldTex.dispose() + } + + private val messageComposeBuffer = ByteArrayOutputStream(BLOCK_SIZE) // always use this and don't alter blockSendBuffer please + private var blockSendBuffer = ByteArray(1) + private var blockSendCount = 0 + + private fun resetBuf() { + blockSendCount = 0 + messageComposeBuffer.reset() + } + + + override fun hasNext(): Boolean { + return (blockSendCount * BLOCK_SIZE < blockSendBuffer.size) + } + + override fun startSendImpl(recipient: BlockTransferInterface): Int { + if (blockSendCount == 0) { + blockSendBuffer = messageComposeBuffer.toByteArray() + } + + val sendSize = if (blockSendBuffer.size - (blockSendCount * BLOCK_SIZE) < BLOCK_SIZE) + blockSendBuffer.size % BLOCK_SIZE + else BLOCK_SIZE + + recipient.writeout(ByteArray(sendSize) { + blockSendBuffer[blockSendCount * BLOCK_SIZE + it] + }) + + blockSendCount += 1 + + return sendSize + } + + override fun writeoutImpl(inputData: ByteArray) { + val inputString = inputData.trimNull().toString(VM.CHARSET) + + // prepare draw commands + /* + * draw command format: + * + * + * + * marking rules: + * + * : exposed = has at least 1 nonsolid on 4 sides + * + * 1. exposed grass -> 2 + * 2. exposed dirt -> 4 + * 3. exposed stone -> 7 + * 4. stone exposed to dirt/grass -> 7 + */ + if (inputString.startsWith("POLL")) { + resetBuf() + val cmdbuf = HashMap(1024) + + for (y in 1 until H-1) { for (x in 1 until W-1) { + val yx = y.shl(8) or x + val i = y * W + x + val nearby = listOf(i-W,i-1,i+1,i+W).map { world[it] } // up, left, right, down + val block = world[i] + + if (block == GRASS && nearby.contains(AIR)) { + cmdbuf[yx] = GRASS_OUT + } + else if (block == DIRT && nearby.contains(AIR)) { + cmdbuf[yx] = DIRT_OUT + } + else if (block == STONE && (nearby.contains(AIR) || nearby.contains(GRASS) || nearby.contains(DIRT))) { + cmdbuf[yx] = STONE_OUT + } + }} + + cmdbuf.keys.sorted().forEach { key -> + val value = cmdbuf[key]!!.toInt() + val x = key % 256 + val y = key / 256 + messageComposeBuffer.write(y) + messageComposeBuffer.write(x) + messageComposeBuffer.write(value) + } + + } + } +} \ No newline at end of file diff --git a/src/net/torvald/tsvm/VMGUI.kt b/src/net/torvald/tsvm/VMGUI.kt index 5eab0b8..ea680fd 100644 --- a/src/net/torvald/tsvm/VMGUI.kt +++ b/src/net/torvald/tsvm/VMGUI.kt @@ -5,6 +5,7 @@ import com.badlogic.gdx.Gdx import com.badlogic.gdx.graphics.* import com.badlogic.gdx.graphics.g2d.SpriteBatch import kotlinx.coroutines.* +import net.torvald.terrarum.modulecomputers.tsvmperipheral.WorldRadar import net.torvald.tsvm.CompressorDelegate.GZIP_HEADER import net.torvald.tsvm.peripheral.* import java.io.File @@ -96,6 +97,8 @@ class VMGUI(val loaderInfo: EmulInstance, val viewportWidth: Int, val viewportHe vm.getInputStream = { System.`in` } } + vm.getIO().blockTransferPorts[1].attachDevice(WorldRadar(Gdx.files.internal("test_assets/test_terrain.png"))) + loaderInfo.extraPeripherals.forEach { (port, peri) -> val typeargs = peri.args.map { it.javaClass }.toTypedArray() diff --git a/src/net/torvald/tsvm/peripheral/ExtDisp.kt b/src/net/torvald/tsvm/peripheral/ExtDisp.kt index 78b03b6..c2893f5 100644 --- a/src/net/torvald/tsvm/peripheral/ExtDisp.kt +++ b/src/net/torvald/tsvm/peripheral/ExtDisp.kt @@ -97,6 +97,10 @@ class ExtDisp(val vm: VM, val width: Int, val height: Int) : PeriBase { in 0 until width * height -> { framebuffer.pixels.put(adi, byte) } + (width * height).toLong() -> { + framebuffer.setColor(bi.shl(24) or bi.shr(16) or bi.shl(8) or bi) + framebuffer.fill() + } in 0 until nextPowerOfTwo(width * height) -> { /* do nothing */ } else -> poke(addr % nextPowerOfTwo(width * height), byte) } diff --git a/test_assets/test_terrain.png b/test_assets/test_terrain.png new file mode 100644 index 0000000000000000000000000000000000000000..45e5cf48e0e3ab5c1b85db9de5b3da0832e0d0fc GIT binary patch literal 2060 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 WJF`gNY81}^0000