From 4c4f24be375a9352001ccd42fd6b6de634c28dcf Mon Sep 17 00:00:00 2001 From: minjaesong Date: Tue, 15 Apr 2025 21:38:33 +0900 Subject: [PATCH] video delta encoding wip --- assets/disk0/home/hdk/compile.js | 10 +- assets/disk0/home/hdk/decompile.js | 1 - assets/disk0/home/hdk/link.js | 5 +- assets/disk0/tvdos/moviedev/encodemovipf.js | 11 +- .../tvdos/moviedev/encodemovipf_delta.js | 101 ++++++++ terranmon.txt | 30 +++ .../torvald/tsvm/GraphicsJSR223Delegate.kt | 225 +++++++++++++++++- tsvm_core/src/net/torvald/tsvm/JS_INIT.js | 10 + tsvm_core/src/net/torvald/tsvm/VM.kt | 96 +++++++- .../tsvm/peripheral/GraphicsAdapter.kt | 3 +- .../src/net/torvald/tsvm/AppLoader.java | 3 +- 11 files changed, 466 insertions(+), 29 deletions(-) create mode 100644 assets/disk0/tvdos/moviedev/encodemovipf_delta.js diff --git a/assets/disk0/home/hdk/compile.js b/assets/disk0/home/hdk/compile.js index 20bba87..840688e 100644 --- a/assets/disk0/home/hdk/compile.js +++ b/assets/disk0/home/hdk/compile.js @@ -1,9 +1,9 @@ if (exec_args[1] === undefined) { - println("Usage: compile myfile") + println("Usage: compile myfile.js") println("The compiled file will be myfile.bin") return 1 } - -_G.shell.execute(`gzip -c ${exec_args[1]} | writeto ${exec_args[1]}.gz`) -_G.shell.execute(`enc ${exec_args[1]}.gz ${exec_args[1]}.bin`) -_G.shell.execute(`rm ${exec_args[1]}.gz`) \ No newline at end of file +const filenameWithoutExt = exec_args[1].substringBeforeLast(".") +_G.shell.execute(`gzip -c ${exec_args[1]} | writeto ${filenameWithoutExt}.gz`) +_G.shell.execute(`enc ${filenameWithoutExt}.gz ${filenameWithoutExt}.bin`) +_G.shell.execute(`rm ${filenameWithoutExt}.gz`) \ No newline at end of file diff --git a/assets/disk0/home/hdk/decompile.js b/assets/disk0/home/hdk/decompile.js index ee7e234..0d9f177 100644 --- a/assets/disk0/home/hdk/decompile.js +++ b/assets/disk0/home/hdk/decompile.js @@ -3,7 +3,6 @@ if (exec_args[1] === undefined) { println("The compiled file will be myfile.bin.js") return 1 } - _G.shell.execute(`enc ${exec_args[1]} ${exec_args[1]}.gz`) _G.shell.execute(`gzip -c -d ${exec_args[1]}.gz | writeto ${exec_args[1]}.js`) _G.shell.execute(`rm ${exec_args[1]}.gz`) \ No newline at end of file diff --git a/assets/disk0/home/hdk/link.js b/assets/disk0/home/hdk/link.js index 28a1e3f..2c84291 100644 --- a/assets/disk0/home/hdk/link.js +++ b/assets/disk0/home/hdk/link.js @@ -8,7 +8,10 @@ if (exec_args[1] === undefined || exec_args[2] === undefined) { let infilePath = _G.shell.resolvePathInput(exec_args[2]).full let infile = files.open(infilePath) -let outfile = files.open(infilePath + ".out") + +if (!infile.exists) throw Error("No such file: " + infilePath) + +let outfile = files.open(infilePath.substringBeforeLast(".") + ".out") let outMode = exec_args[1].toLowerCase() let type = { diff --git a/assets/disk0/tvdos/moviedev/encodemovipf.js b/assets/disk0/tvdos/moviedev/encodemovipf.js index e730ac2..a280c98 100644 --- a/assets/disk0/tvdos/moviedev/encodemovipf.js +++ b/assets/disk0/tvdos/moviedev/encodemovipf.js @@ -7,11 +7,11 @@ let PATHFUN = (i) => `/ddol/${(''+i).padStart(5,'0')}.png` const FBUF_SIZE = WIDTH * HEIGHT -let infile = sys.malloc(512000) // somewhat arbitrary -let imagearea = sys.malloc(FBUF_SIZE*3) -let decodearea = sys.malloc(FBUF_SIZE) -let ipfarea = sys.malloc(FBUF_SIZE) -let gzippedImage = sys.malloc(512000) // somewhat arbitrary +let infile = sys.malloc(512000) // allocate somewhat arbitrary amount of memory +let imagearea = sys.malloc(FBUF_SIZE*3) // allocate exact amount of memory +let decodearea = sys.malloc(FBUF_SIZE) // allocate exact amount of memory +let ipfarea = sys.malloc(FBUF_SIZE) // allocate exact amount of memory +let gzippedImage = sys.malloc(512000) // allocate somewhat arbitrary amount of memory let outfilename = exec_args[1] @@ -69,6 +69,7 @@ for (let f = 1; f <= TOTAL_FRAMES; f++) { print(` ${gzlen} bytes\n`) } +// free all the memory that has been allocated sys.free(infile) sys.free(imagearea) sys.free(decodearea) diff --git a/assets/disk0/tvdos/moviedev/encodemovipf_delta.js b/assets/disk0/tvdos/moviedev/encodemovipf_delta.js new file mode 100644 index 0000000..7fda637 --- /dev/null +++ b/assets/disk0/tvdos/moviedev/encodemovipf_delta.js @@ -0,0 +1,101 @@ +// some manual config shits +let TOTAL_FRAMES = 3813 +let FPS = 30 +let WIDTH = 560 +let HEIGHT = 448 +let PATHFUN = (i) => `/ddol/${(''+i).padStart(5,'0')}.png` + +if (WIDTH % 4 != 0 || HEIGHT % 4 != 0) { + printerrln(`Frame dimension is not multiple of 4 (${WIDTH}x${HEIGHT})`) + return 5 +} + +const FBUF_SIZE = WIDTH * HEIGHT +let infile = sys.malloc(512000) // somewhat arbitrary +let imagearea = sys.malloc(FBUF_SIZE*3) +let decodearea = sys.malloc(FBUF_SIZE) +let ipfarea1 = sys.malloc(FBUF_SIZE) +let ipfarea2 = sys.malloc(FBUF_SIZE) +let ipfDelta = sys.malloc(FBUF_SIZE) +let gzippedImage = sys.malloc(512000) // somewhat arbitrary + +let outfilename = exec_args[1] + +if (!outfilename) return 1 + +function appendToOutfile(bytes) { + filesystem.open("A", outfilename, "A") + filesystem.writeBytes("A", bytes) +} + +function appendToOutfilePtr(ptr, len) { + filesystem.open("A", outfilename, "A") + dma.ramToCom(ptr, 0, len) +} + +// write header to the file +let headerBytes = [ + 0x1F, 0x54, 0x53, 0x56, 0x4D, 0x4D, 0x4F, 0x56, // magic + WIDTH & 255, (WIDTH >> 8) & 255, // width + HEIGHT & 255, (HEIGHT >> 8) & 255, // height + FPS & 255, (FPS >> 8) & 255, // FPS + TOTAL_FRAMES & 255, (TOTAL_FRAMES >> 8) & 255, (TOTAL_FRAMES >> 16) & 255, (TOTAL_FRAMES >> 24) & 255, // frame count + 0x04, 0x00, // type 4 frames (force no-alpha) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // reserved +] + +filesystem.open("A", outfilename, "W") +filesystem.writeBytes("A", headerBytes) + +let ipfAreaOld = ipfarea2 +let ipfAreaNew = ipfarea1 + + +for (let f = 1; f <= TOTAL_FRAMES; f++) { + let fname = PATHFUN(f) + filesystem.open("A", fname, "R") + let fileLen = filesystem.getFileLen("A") + dma.comToRam(0, 0, infile, fileLen) + + let [_1, _2, channels, _3] = graphics.decodeImageTo(infile, fileLen, imagearea) + + const val IPF_BLOCK_SIZE = (channels == 3) ? 12 : 20; + + print(`Frame ${f}/${TOTAL_FRAMES} (Ch: ${channels}) ->`) + +// graphics.imageToDisplayableFormat(imagearea, decodearea, 560, 448, 3, 1) + graphics.encodeIpf1(imagearea, ipfAreaNew, WIDTH, HEIGHT, channels, false, 0) + + // get the difference map + let patchEncodedSize = graphics.encodeIpf1d(ipfAreaOld, ipfAreaNew, ipfDelta, WIDTH, HEIGHT, 0.90) + + // decide whether or not the patch encoding should be used + let gzlen = gzip.compFromTo( + (patchEncodedSize) ? ipfDelta : ipfAreaNew, + patchEncodedSize || FBUF_SIZE, + gzippedImage + ) + let frameSize = [ + (gzlen >>> 0) & 255, + (gzlen >>> 8) & 255, + (gzlen >>> 16) & 255, + (gzlen >>> 24) & 255 + ] + appendToOutfile(frameSize) + appendToOutfilePtr(gzippedImage, gzlen) + + print(` ${gzlen} bytes\n`) + + // swap two pointers + let t = ipfAreaOld + ipfAreaOld = ipfAreaNew + ipfAreaNew = t +} + +sys.free(infile) +sys.free(imagearea) +sys.free(decodearea) +sys.free(ipfarea1) +sys.free(ipfarea2) +sys.free(ipfDelta) +sys.free(gzippedImage) \ No newline at end of file diff --git a/terranmon.txt b/terranmon.txt index e2e3b97..b9b2b46 100644 --- a/terranmon.txt +++ b/terranmon.txt @@ -601,6 +601,36 @@ iPF2: which packs into: [ 30 | 30 | FA | FA ] (because little endian) +iPF1-delta (for video encoding): + +Delta encoded frames contain "insutructions" for delta-encoding the existing frame. +Or, a collection of [OPCODE | PAYLOAD] pairs + +Opcode: +0x00 : Skip N blocks + payload: (varint) number of 4x4 blocks +0x10 : Patch + payload: (12 bytes) encoded delta block +0x20 : Repeat + payload: (varint) repeat last delta N times +0xF0 : End of delta stream + payload: none + +Sample stream: + [SKIP 10] [PATCH A] [REPEAT 3] [SKIP 5] [PATCH B] [END] + +Delta block format: + + Each PATCH delta payload is still: + 8 bytes of Luma (4-bit deltas for 16 pixels) + 2 bytes of Co deltas (4× 4-bit deltas) + 2 bytes of Cg deltas (4× 4-bit deltas) + Total: 12 bytes per PATCH. + + These are always relative to the same-position block in the previous frame. + + + - Progressive Blocks Ordered string of words (word size varies by the colour mode) are stored here. If progressive mode is enabled, words are stored in the order that accomodates it. diff --git a/tsvm_core/src/net/torvald/tsvm/GraphicsJSR223Delegate.kt b/tsvm_core/src/net/torvald/tsvm/GraphicsJSR223Delegate.kt index 941f574..da4de69 100644 --- a/tsvm_core/src/net/torvald/tsvm/GraphicsJSR223Delegate.kt +++ b/tsvm_core/src/net/torvald/tsvm/GraphicsJSR223Delegate.kt @@ -704,6 +704,113 @@ class GraphicsJSR223Delegate(private val vm: VM) { }} } + /** + * @return non-zero if delta-encoded, 0 if delta encoding is worthless + */ + fun encodeIpf1d( + prevIPFptr: Int, // full iPF picture frame for t minus one + newIPFptr: Int, // full iPF picture frame for t equals zero + currentFrame: Int, // where to write delta-encoded payloads to. Not touched if delta-encoding is worthless + width: Int, height: Int, + inefficiencyThreshold: Double = 0.90 + ): Int { + val frameSize = width * height + + val BLOCK_SIZE = 12 + val totalBlocks = frameSize / BLOCK_SIZE + + var skipCount = 0 + var repeatCount = 0 + val temp = ByteArray(frameSize * 2) // Overallocate + var tempPtr = 0 + + var lastDelta = ByteArray(BLOCK_SIZE) + + fun readBlock(ptr: Int): ByteArray { + return ByteArray(BLOCK_SIZE) { i -> vm.peek(ptr.toLong() + i)!!.toByte() } + } + + // MSB is a "continuation flag"; varint decoding terminates when it sees byte with no MSB set + fun writeVarInt(buf: ByteArray, start: Int, value: Int): Int { + var v = value + var i = 0 + while (v >= 0x80) { + buf[start + i] = ((v and 0x7F) or 0x80).toByte() + v = v ushr 7 + i++ + } + buf[start + i] = v.toByte() + return i + 1 + } + + fun flushSkips() { + if (skipCount > 0) { + temp[tempPtr++] = 0x00 + tempPtr += writeVarInt(temp, tempPtr, skipCount) + skipCount = 0 + } + } + + fun flushRepeats() { + if (repeatCount > 0) { + temp[tempPtr++] = 0x20 + tempPtr += writeVarInt(temp, tempPtr, repeatCount) + repeatCount = 0 + } + } + + for (blockIndex in 0 until totalBlocks) { + val offset = blockIndex * BLOCK_SIZE + val prevBlock = readBlock(prevIPFptr + offset) + val currBlock = readBlock(newIPFptr + offset) + + val diff = isSignificantlyDifferent(prevBlock, currBlock) + + if (!diff) { + if (repeatCount > 0) flushRepeats() + skipCount++ + } else if (lastDelta.contentEquals(currBlock)) { + flushSkips() + repeatCount++ + } else { + flushSkips() + flushRepeats() + temp[tempPtr++] = 0x10 + currBlock.copyInto(temp, tempPtr) + tempPtr += BLOCK_SIZE + lastDelta = currBlock + } + } + + flushSkips() + flushRepeats() + temp[tempPtr++] = 0xF0.toByte() + + if (tempPtr >= (frameSize * inefficiencyThreshold).toInt()) { + return 0 // delta is inefficient, do not write + } + + // Write delta to memory + if (currentFrame >= 0) { + UnsafeHelper.memcpyRaw(temp, UnsafeHelper.getArrayOffset(temp), null, vm.usermem.ptr + currentFrame, tempPtr.toLong()) + } + else { + for (i in 0 until tempPtr) { + vm.poke(currentFrame.toLong() + i, temp[i]) + } + } + + return tempPtr + } + + private fun isSignificantlyDifferent(a: ByteArray, b: ByteArray, threshold: Int = 5): Boolean { + var total = 0 + for (i in a.indices) { + total += kotlin.math.abs((a[i].toInt() and 0xFF) - (b[i].toInt() and 0xFF)) + } + return total > threshold + } + fun encodeIpf2(srcPtr: Int, destPtr: Int, width: Int, height: Int, channels: Int, hasAlpha: Boolean, pattern: Int) { var writeCount = 0L @@ -868,7 +975,6 @@ class GraphicsJSR223Delegate(private val vm: VM) { } fun decodeIpf1(srcPtr: Int, destRG: Int, destBA: Int, width: Int, height: Int, hasAlpha: Boolean) { - val gpu = getFirstGPU() val sign = if (destRG >= 0) 1 else -1 if (destRG * destBA < 0) throw IllegalArgumentException("Both destination memories must be on the same domain (both being Usermem or HWmem)") val sptr = srcPtr.toLong() @@ -876,9 +982,7 @@ class GraphicsJSR223Delegate(private val vm: VM) { val dptr2 = destBA.toLong() var readCount = 0 fun readShort() = - vm.peek(sptr + readCount++)!!.toUint() or vm.peek(sptr + readCount++)!!.toUint().shl(8).also { - gpu?.applyDelay() - } + vm.peek(sptr + readCount++)!!.toUint() or vm.peek(sptr + readCount++)!!.toUint().shl(8) for (blockY in 0 until ceil(height / 4f)) { @@ -938,8 +1042,110 @@ class GraphicsJSR223Delegate(private val vm: VM) { }} } + fun applyIpf1d(ipf1DeltaPtr: Int, destRG: Int, destBA: Int, width: Int, height: Int) { + val blocksPerRow = (width + 3) / 4 + val totalBlocks = ((width + 3) / 4) * ((height + 3) / 4) + + val sign = if (destRG >= 0) 1 else -1 + if (destRG * destBA < 0) throw IllegalArgumentException("Both destination memories must be on the same domain") + + var ptr = ipf1DeltaPtr.toLong() + var blockIndex = 0 + + fun readByte(): Int = (vm.peek(ptr++)!!.toInt() and 0xFF) + fun readShort(): Int { + val low = readByte() + val high = readByte() + return low or (high shl 8) + } + + fun readVarInt(): Int { + var value = 0 + var shift = 0 + while (true) { + val byte = readByte() + value = value or ((byte and 0x7F) shl shift) + if ((byte and 0x80) == 0) break + shift += 7 + } + return value + } + + while (true) { + val opcode = readByte() + when (opcode) { + 0x00 -> { // Skip blocks + val count = readVarInt() + blockIndex += count + } + 0x10 -> { // Write literal patch + if (blockIndex >= totalBlocks) break + + val co = readShort() + val cg = readShort() + val y1 = readShort() + val y2 = readShort() + val y3 = readShort() + val y4 = readShort() + + val rg = IntArray(16) + val ba = IntArray(16) + + var px = ycocgToRGB(co and 15, cg and 15, y1, 65535) + rg[0] = px[0]; ba[0] = px[1] + rg[1] = px[2]; ba[1] = px[3] + rg[4] = px[4]; ba[4] = px[5] + rg[5] = px[6]; ba[5] = px[7] + + px = ycocgToRGB((co shr 4) and 15, (cg shr 4) and 15, y2, 65535) + rg[2] = px[0]; ba[2] = px[1] + rg[3] = px[2]; ba[3] = px[3] + rg[6] = px[4]; ba[6] = px[5] + rg[7] = px[6]; ba[7] = px[7] + + px = ycocgToRGB((co shr 8) and 15, (cg shr 8) and 15, y3, 65535) + rg[8] = px[0]; ba[8] = px[1] + rg[9] = px[2]; ba[9] = px[3] + rg[12] = px[4]; ba[12] = px[5] + rg[13] = px[6]; ba[13] = px[7] + + px = ycocgToRGB((co shr 12) and 15, (cg shr 12) and 15, y4, 65535) + rg[10] = px[0]; ba[10] = px[1] + rg[11] = px[2]; ba[11] = px[3] + rg[14] = px[4]; ba[14] = px[5] + rg[15] = px[6]; ba[15] = px[7] + + val blockX = blockIndex % blocksPerRow + val blockY = blockIndex / blocksPerRow + + for (py in 0..3) { + for (pxi in 0..3) { + val ox = blockX * 4 + pxi + val oy = blockY * 4 + py + if (ox < width && oy < height) { + val offset = oy * 560 + ox + val i = py * 4 + pxi + vm.poke((destRG + offset * sign).toLong(), rg[i].toByte()) + vm.poke((destBA + offset * sign).toLong(), ba[i].toByte()) + } + } + } + + blockIndex++ + } + 0x20 -> { // Repeat last literal + val repeatCount = readVarInt() + // Just skip applying. We assume previous patch was already applied visually. + blockIndex += repeatCount + } + 0xF0 -> return // End of stream + else -> error("Unknown delta opcode: ${opcode.toString(16)}") + } + } + } + + fun decodeIpf2(srcPtr: Int, destRG: Int, destBA: Int, width: Int, height: Int, hasAlpha: Boolean) { - val gpu = getFirstGPU() val sign = if (destRG >= 0) 1 else -1 if (destRG * destBA < 0) throw IllegalArgumentException("Both destination memories must be on the same domain (both being Usermem or HWmem)") val sptr = srcPtr.toLong() @@ -947,14 +1153,9 @@ class GraphicsJSR223Delegate(private val vm: VM) { val dptr2 = destBA.toLong() var readCount = 0 fun readShort() = - vm.peek(sptr + readCount++)!!.toUint() or vm.peek(sptr + readCount++)!!.toUint().shl(8).also { - gpu?.applyDelay() - } + vm.peek(sptr + readCount++)!!.toUint() or vm.peek(sptr + readCount++)!!.toUint().shl(8) fun readInt() = - vm.peek(sptr + readCount++)!!.toUint() or vm.peek(sptr + readCount++)!!.toUint().shl(8) or vm.peek(sptr + readCount++)!!.toUint().shl(16) or vm.peek(sptr + readCount++)!!.toUint().shl(24).also { - gpu?.applyDelay() - gpu?.applyDelay() - } + vm.peek(sptr + readCount++)!!.toUint() or vm.peek(sptr + readCount++)!!.toUint().shl(8) or vm.peek(sptr + readCount++)!!.toUint().shl(16) or vm.peek(sptr + readCount++)!!.toUint().shl(24) for (blockY in 0 until ceil(height / 4f)) { diff --git a/tsvm_core/src/net/torvald/tsvm/JS_INIT.js b/tsvm_core/src/net/torvald/tsvm/JS_INIT.js index 286e17a..a9caea7 100644 --- a/tsvm_core/src/net/torvald/tsvm/JS_INIT.js +++ b/tsvm_core/src/net/torvald/tsvm/JS_INIT.js @@ -346,6 +346,16 @@ String.prototype.tail = function() { String.prototype.init = function() { return this.substring(0, this.length - 1) } +String.prototype.substringBeforeLast = function(delimiter) { + if (!this || this.length === 0) return "" + if (!delimiter || delimiter.length === 0) return "" + + const lastIndex = this.lastIndexOf(delimiter) + + if (lastIndex === -1) return this + + return this.substring(0, lastIndex) +} Array.prototype.shuffle = function() { let counter = this.length; diff --git a/tsvm_core/src/net/torvald/tsvm/VM.kt b/tsvm_core/src/net/torvald/tsvm/VM.kt index ca9bbf8..df39501 100644 --- a/tsvm_core/src/net/torvald/tsvm/VM.kt +++ b/tsvm_core/src/net/torvald/tsvm/VM.kt @@ -3,9 +3,7 @@ package net.torvald.tsvm import net.torvald.UnsafeHelper import net.torvald.UnsafePtr import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.toHex -import net.torvald.tsvm.peripheral.IOSpace -import net.torvald.tsvm.peripheral.PeriBase -import net.torvald.tsvm.peripheral.VMProgramRom +import net.torvald.tsvm.peripheral.* import java.io.InputStream import java.io.OutputStream import java.nio.charset.Charset @@ -275,6 +273,98 @@ class VM( mallocSizes[blockStart] = allocBlocks } + fun memcpy(from: Int, to: Int, len: Int) { + val from = from.toLong() + val to = to.toLong() + val len = len.toLong() + + val fromVector = if (from >= 0) 1 else -1 + val toVector = if (to >= 0) 1 else -1 + val fromDev = getDev(from, len, false) + val toDev = getDev(to, len, true) + +// println("from = $from, to = $to") +// println("fromDev = $fromDev, toDev = $toDev") + + if (fromDev != null && toDev != null) + UnsafeHelper.memcpy(fromDev, toDev, len) + else if (fromDev == null && toDev != null) { + val buf = UnsafeHelper.allocate(len, this) + for (i in 0 until len) buf[i] = peek(from + i*fromVector)!! + UnsafeHelper.memcpy(buf.ptr, toDev, len) + buf.destroy() + } + else if (fromDev != null) { + for (i in 0 until len) poke(to + i*toVector, UnsafeHelper.unsafe.getByte(fromDev + i)) + } + else { + for (i in 0 until len) poke(to + i*toVector, peek(from + i*fromVector)!!) + } + } + + private fun relPtrInDev(from: Long, len: Long, start: Int, end: Int) = + (from in start..end && (from + len) in start..end) + + private fun getDev(from: Long, len: Long, isDest: Boolean): Long? { + return if (from >= 0) usermem.ptr + from + // MMIO area + else if (from in -1048576..-1 && (from - len) in -1048577..-1) { + val fromIndex = (-from-1) / 131072 + val dev = peripheralTable[fromIndex.toInt()].peripheral ?: return null + val fromRel = (-from-1) % 131072 + if (fromRel + len > 131072) return null + + return if (dev is IOSpace) { + if (relPtrInDev(fromRel, len, 1024, 2047)) dev.peripheralFast.ptr + fromRel - 1024 + else if (relPtrInDev(fromRel, len, 4096, 8191)) (if (isDest) dev.blockTransferTx[0] else dev.blockTransferRx[0]).ptr + fromRel - 4096 + else if (relPtrInDev(fromRel, len, 8192, 12287)) (if (isDest) dev.blockTransferTx[1] else dev.blockTransferRx[1]).ptr + fromRel - 8192 + else if (relPtrInDev(fromRel, len, 12288, 16383)) (if (isDest) dev.blockTransferTx[2] else dev.blockTransferRx[2]).ptr + fromRel - 12288 + else if (relPtrInDev(fromRel, len, 16384, 20479)) (if (isDest) dev.blockTransferTx[3] else dev.blockTransferRx[3]).ptr + fromRel - 16384 + else null + } + else if (dev is AudioAdapter) { + if (relPtrInDev(fromRel, len, 64, 2367)) dev.mediaDecodedBin.ptr + fromRel - 64 + else if (relPtrInDev(fromRel, len, 2368, 4096)) dev.mediaFrameBin.ptr + fromRel - 2368 + else null + } + else if (dev is GraphicsAdapter) { + if (relPtrInDev(fromRel, len, 1024, 2047)) dev.scanlineOffsets.ptr + fromRel - 1024 + else if (relPtrInDev(fromRel, len, 2048, 4095)) dev.mappedFontRom.ptr + fromRel - 2048 + else if (relPtrInDev(fromRel, len, 65536, 131071)) dev.instArea.ptr + fromRel - 65536 + else null + } + else null + } + // memory area + else { + val fromIndex = (-from-1) / 1048576 + val dev = peripheralTable[fromIndex.toInt()].peripheral ?: return null + val fromRel = (-from-1) % 1048576 + if (fromRel + len > 1048576) return null + + return if (dev is AudioAdapter) { + if (relPtrInDev(fromRel, len, 0, 114687)) dev.sampleBin.ptr + fromRel - 0 + else null + } + else if (dev is GraphicsAdapter) { + if (relPtrInDev(fromRel, len, 0, 250879)) dev.framebuffer.ptr + fromRel - 0 + else if (relPtrInDev(fromRel, len, 250880, 251903)) dev.unusedArea.ptr + fromRel - 250880 + else if (relPtrInDev(fromRel, len, 253950, 261631)) dev.textArea.ptr + fromRel - 253950 + else if (relPtrInDev(fromRel, len, 262144, 513023)) dev.framebuffer2?.ptr?.plus(fromRel)?.minus(253950) + else null + } + else if (dev is RamBank) { + if (relPtrInDev(fromRel, len, 0, 524287)) + dev.mem.ptr + 524288*dev.map0 + fromRel + else if (relPtrInDev(fromRel, len, 524288, 131071)) + dev.mem.ptr + 524288*dev.map1 + fromRel - 524288 + else + null + } + else null + } + } + internal data class VMNativePtr(val address: Int, val size: Int) } diff --git a/tsvm_core/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt b/tsvm_core/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt index 2624667..2ae6be7 100644 --- a/tsvm_core/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt +++ b/tsvm_core/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt @@ -42,7 +42,7 @@ data class AdapterConfig( val paletteShader: String = DRAW_SHADER_FRAG, val drawScale: Float = 1f, val scaleFiltered: Boolean = false, - val baudRate: Double = 16_384_000.0,//57600.0, + val baudRate: Double = 115200.0,//16_384_000.0,//57600.0, val bitsPerChar: Int = 10 // start bit + 8 data bits + stop bit ) @@ -260,6 +260,7 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi protected var slpcnt = 0L open fun applyDelay() { + applyDelay0() } protected fun applyDelay0() { diff --git a/tsvm_executable/src/net/torvald/tsvm/AppLoader.java b/tsvm_executable/src/net/torvald/tsvm/AppLoader.java index 82cab24..f707fb4 100644 --- a/tsvm_executable/src/net/torvald/tsvm/AppLoader.java +++ b/tsvm_executable/src/net/torvald/tsvm/AppLoader.java @@ -56,7 +56,8 @@ public class AppLoader { defaultPeripherals.add(new Pair(3, new PeripheralEntry2("net.torvald.tsvm.peripheral.AudioAdapter", vm))); - EmulInstance reference = new EmulInstance(vm, "net.torvald.tsvm.peripheral.ReferenceGraphicsAdapter2", diskPath, 560, 448, defaultPeripherals); + EmulInstance reference = new EmulInstance(vm, "net.torvald.tsvm.peripheral.ReferenceGraphicsAdapter", diskPath, 560, 448, defaultPeripherals); + EmulInstance referenceRemote = new EmulInstance(vm, "net.torvald.tsvm.peripheral.RemoteGraphicsAdapter", diskPath, 560, 448, defaultPeripherals); EmulInstance reference2 = new EmulInstance(vm, "net.torvald.tsvm.peripheral.ReferenceLikeLCD", diskPath, 560, 448, defaultPeripherals); EmulInstance term = new EmulInstance(vm, "net.torvald.tsvm.peripheral.Term", diskPath, 720, 480); EmulInstance portable = new EmulInstance(vm, "net.torvald.tsvm.peripheral.CLCDDisplay", diskPath, 1080, 436);