mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 19:51:51 +09:00
video delta encoding wip
This commit is contained in:
@@ -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`)
|
||||
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`)
|
||||
@@ -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`)
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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)
|
||||
|
||||
101
assets/disk0/tvdos/moviedev/encodemovipf_delta.js
Normal file
101
assets/disk0/tvdos/moviedev/encodemovipf_delta.js
Normal file
@@ -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)
|
||||
@@ -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.
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user