mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 19:51:51 +09:00
audio adapter now takes ui8 bytes directly
This commit is contained in:
@@ -96,43 +96,9 @@ function readBytes(length) {
|
||||
return ptr
|
||||
}
|
||||
|
||||
/*let sampleSize = FILE_SIZE
|
||||
const FETCH_INTERVAL = 631578947
|
||||
let updateAkku = FETCH_INTERVAL
|
||||
let oldNanoTime = sys.nanoTime()
|
||||
|
||||
const BLOCK_SIZE = 37894
|
||||
|
||||
audio.setPcmMode(0)
|
||||
audio.setMasterVolume(0, 255)
|
||||
|
||||
while (sampleSize > 0) {
|
||||
let newNanoTime = sys.nanoTime()
|
||||
updateAkku += newNanoTime - oldNanoTime
|
||||
oldNanoTime = newNanoTime
|
||||
|
||||
if (updateAkku >= FETCH_INTERVAL) {
|
||||
println((FILE_SIZE - sampleSize) / FILE_SIZE * 100 + "%")
|
||||
updateAkku -= FETCH_INTERVAL
|
||||
|
||||
let readLength = (sampleSize < BLOCK_SIZE) ? sampleSize : BLOCK_SIZE
|
||||
let samples = readBytes(readLength)
|
||||
|
||||
audio.setUploadLength(0, readLength)
|
||||
audio.putPcmDataByPtr(samples, readLength, 0)
|
||||
audio.play(0)
|
||||
|
||||
sampleSize -= readLength
|
||||
sys.free(samples)
|
||||
}
|
||||
|
||||
sys.spin()
|
||||
}*/
|
||||
|
||||
|
||||
let sampleSize = FILE_SIZE
|
||||
const BLOCK_SIZE = 4096
|
||||
const QUEUEING_SIZE = 4
|
||||
const QUEUE_MAX = 4 // according to the spec
|
||||
|
||||
audio.resetParams(0)
|
||||
audio.purgeQueue(0)
|
||||
@@ -145,20 +111,23 @@ audio.setMasterVolume(0, 255)
|
||||
while (sampleSize > 0) {
|
||||
let queueSize = audio.getPosition(0)
|
||||
|
||||
serial.println(`[js] Trying to upload samples, queueSize = ${queueSize}`)
|
||||
// serial.println(`[js] Trying to upload samples, queueSize = ${queueSize}`)
|
||||
print(".")
|
||||
|
||||
if (queueSize == 0) {
|
||||
if (queueSize <= 1) {
|
||||
serial.println(`[js] Queue size: ${queueSize}; uploading ${QUEUE_MAX - queueSize} samples`)
|
||||
|
||||
println()
|
||||
println((FILE_SIZE - sampleSize) / FILE_SIZE * 100 + " %")
|
||||
|
||||
// upload four samples for lag-safely
|
||||
for (let repeat = QUEUEING_SIZE; repeat > 0; repeat--) {
|
||||
for (let repeat = QUEUE_MAX - queueSize; repeat > 0; repeat--) {
|
||||
let readLength = (sampleSize < BLOCK_SIZE) ? sampleSize : BLOCK_SIZE
|
||||
let samples = readBytes(readLength)
|
||||
|
||||
audio.putPcmDataByPtr(samples, readLength, 0)
|
||||
audio.uploadSamples(0, readLength)
|
||||
audio.setSampleUploadLength(0, readLength)
|
||||
audio.startSampleUpload(0)
|
||||
|
||||
sampleSize -= readLength
|
||||
sys.free(samples)
|
||||
@@ -169,6 +138,9 @@ while (sampleSize > 0) {
|
||||
audio.play(0)
|
||||
}
|
||||
|
||||
// audio.setMasterVolume(0, (Math.random()*255)|0)
|
||||
|
||||
|
||||
sys.sleep(10)
|
||||
}
|
||||
|
||||
|
||||
@@ -502,6 +502,27 @@ shell.coreutils = {
|
||||
}
|
||||
else return 1
|
||||
},
|
||||
mv: function(args) {
|
||||
if (args[2] === undefined || args[1] === undefined) {
|
||||
printerrln(`Usage: ${args[0].toUpperCase()} source_file destination_file`)
|
||||
return
|
||||
}
|
||||
let path = shell.resolvePathInput(args[1])
|
||||
let pathd = shell.resolvePathInput(args[2])
|
||||
let sourceFile = files.open(path.full)
|
||||
let destFile = files.open(pathd.full)
|
||||
|
||||
debugprintln(`[cp] source path: ${path.full}`)
|
||||
debugprintln(`[cp] dest path: ${pathd.full}`)
|
||||
|
||||
if (sourceFile.isDirectory || !sourceFile.exists) { printerrln(`${args[0].toUpperCase()} failed for '${sourceFile.fullPath}'`); return 1 } // if file is directory or failed to open, IO error code will be returned
|
||||
if (destFile.isDirectory) { printerrln(`${args[0].toUpperCase()} failed for '${destFile.fullPath}'`); return 1 } // if file is directory or failed to open, IO error code will be returned
|
||||
|
||||
destFile.bwrite(sourceFile.bread())
|
||||
|
||||
destFile.flush(); destFile.close()
|
||||
sourceFile.remove()
|
||||
},
|
||||
rem: function(args) {
|
||||
return 0
|
||||
},
|
||||
@@ -555,6 +576,7 @@ shell.coreutils.rm = shell.coreutils.del
|
||||
shell.coreutils.ls = shell.coreutils.dir
|
||||
shell.coreutils.time = shell.coreutils.date
|
||||
shell.coreutils.md = shell.coreutils.mkdir
|
||||
shell.coreutils.move = shell.coreutils.mv
|
||||
// end of command aliases
|
||||
Object.freeze(shell.coreutils)
|
||||
shell.stdio = {
|
||||
|
||||
@@ -573,7 +573,7 @@ Play Head Flags
|
||||
|
||||
NOTE: changing from PCM mode to Tracker mode or vice versa will also reset the parameters as described above
|
||||
Byte 2
|
||||
- PCM Mode: Sampling rate multiplier in 3.5 Unsigned Minifloat (0.03125x to 126x)
|
||||
- PCM Mode: Write non-zero value to start uploading; always 0 when read
|
||||
|
||||
Byte 3 (Tracker Mode)
|
||||
- BPM (24 to 280. Play Data will change this register)
|
||||
@@ -583,6 +583,8 @@ Play Head Flags
|
||||
Byte 3-4 (PCM Mode)
|
||||
- Signed Int16 Sampling rate difference from 30000 Hz
|
||||
|
||||
Uploaded PCM data will be stored onto the queue and the queue is only 4-entries long; any more uploads will be silently discarded.
|
||||
|
||||
|
||||
32768..65535 RW: Cue Sheet (2048 cues)
|
||||
Byte 1..15: pattern number for voice 1..15
|
||||
|
||||
@@ -16,7 +16,10 @@ class AudioJSR223Delegate(private val vm: VM) {
|
||||
fun setTrackerMode(playhead: Int) { getPlayhead(playhead)?.isPcmMode = false }
|
||||
fun isTrackerMode(playhead: Int) = getPlayhead(playhead)?.isPcmMode == false
|
||||
|
||||
fun setMasterVolume(playhead: Int, volume: Int) { getPlayhead(playhead)?.masterVolume = volume and 255 }
|
||||
fun setMasterVolume(playhead: Int, volume: Int) { getPlayhead(playhead)?.apply {
|
||||
masterVolume = volume and 255
|
||||
audioDevice.setVolume(masterVolume / 255f)
|
||||
} }
|
||||
fun getMasterVolume(playhead: Int) = getPlayhead(playhead)?.masterVolume
|
||||
|
||||
fun setMasterPan(playhead: Int, pan: Int) { getPlayhead(playhead)?.masterPan = pan and 255 }
|
||||
@@ -29,13 +32,12 @@ class AudioJSR223Delegate(private val vm: VM) {
|
||||
// fun setPosition(playhead: Int, pos: Int) { getPlayhead(playhead)?.position = pos and 65535 }
|
||||
fun getPosition(playhead: Int) = getPlayhead(playhead)?.position
|
||||
|
||||
fun uploadSamples(playhead: Int, length: Int) { getPlayhead(playhead)?.pcmUploadLength = length and 65535 }
|
||||
fun setSampleUploadLength(playhead: Int, length: Int) { getPlayhead(playhead)?.pcmUploadLength = length and 65535 }
|
||||
|
||||
fun setSamplingRate(playhead: Int, rate: Int) { getPlayhead(playhead)?.setSamplingRate(rate) }
|
||||
fun getSamplingRate(playhead: Int) = getPlayhead(playhead)?.getSamplingRate()
|
||||
|
||||
fun setSamplingRateMult(playhead: Int, mult: Float) { getPlayhead(playhead)?.samplingRateMult = ThreeFiveMiniUfloat(mult) }
|
||||
fun getSamplingRateMult(playhead: Int) = getPlayhead(playhead)?.samplingRateMult?.toFloat()
|
||||
fun startSampleUpload(playhead: Int) { getPlayhead(playhead)?.pcmUpload = true }
|
||||
|
||||
fun setBPM(playhead: Int, bpm: Int) { getPlayhead(playhead)?.bpm = (bpm - 24).and(255) + 24 }
|
||||
fun getBPM(playhead: Int) = getPlayhead(playhead)?.bpm
|
||||
|
||||
@@ -3,13 +3,15 @@ package net.torvald
|
||||
import sun.misc.Unsafe
|
||||
import java.io.PrintStream
|
||||
|
||||
class DanglingPointerException(msg: String) : NullPointerException(msg)
|
||||
class AddressOverflowException(msg: String) : IndexOutOfBoundsException(msg)
|
||||
|
||||
/**
|
||||
* Further read:
|
||||
* - http://www.docjar.com/docs/api/sun/misc/Unsafe.html
|
||||
*
|
||||
* Created by minjaesong on 2019-06-21.
|
||||
*/
|
||||
|
||||
internal object UnsafeHelper {
|
||||
val unsafe: Unsafe
|
||||
|
||||
@@ -87,8 +89,8 @@ internal class UnsafePtr(pointer: Long, allocSize: Long) {
|
||||
//// You may break the glass and use this tool when some fucking incomprehensible bugs ("vittujen vitun bugit")
|
||||
//// appear (e.g. getting garbage values when it fucking shouldn't)
|
||||
|
||||
assert(!destroyed) { throw NullPointerException("The pointer is already destroyed ($this)") }
|
||||
if (index !in 0 until size) throw IndexOutOfBoundsException("Index: $index; alloc size: $size; pointer: ${this}\n${Thread.currentThread().stackTrace.joinToString("\n", limit=10) { " $it" }}")
|
||||
if (destroyed) { throw DanglingPointerException("The pointer is already destroyed ($this)") }
|
||||
if (index !in 0 until size) throw AddressOverflowException("Index: $index; alloc size: $size; pointer: ${this}\n${Thread.currentThread().stackTrace.joinToString("\n", limit=10) { " $it" }}")
|
||||
}
|
||||
|
||||
operator fun get(index: Long): Byte {
|
||||
|
||||
@@ -28,69 +28,27 @@ class AudioAdapter(val vm: VM) : PeriBase {
|
||||
internal val sampleBin = UnsafeHelper.allocate(114687L)
|
||||
internal val instruments = Array(256) { TaudInst() }
|
||||
internal val playdata = Array(256) { Array(64) { TaudPlayData(0,0,0,0,0,0,0,0) } }
|
||||
internal val playheads = Array(4) { Playhead() }
|
||||
internal val playheads: Array<Playhead>
|
||||
internal val cueSheet = Array(2048) { PlayCue() }
|
||||
internal val pcmBin = UnsafeHelper.allocate(65536L)
|
||||
|
||||
private lateinit var audioDevices: Array<AudioDevice>
|
||||
// private var audioDevices: Array<AudioDevice>
|
||||
private val renderThreads = Array(4) { Thread(getRenderFun(it)) }
|
||||
private val writeQueueingThreads = Array(4) { Thread(getQueueingFun(it)) }
|
||||
// private val writeQueues = Array(4) { Queue<FloatArray>() }
|
||||
|
||||
/*private val alSources = Array(4) {
|
||||
val audioField = OpenALAudioDevice::class.java.getDeclaredField("audio")
|
||||
audioField.isAccessible = true
|
||||
val audio = audioField.get(audioDevices[it]) as OpenALLwjgl3Audio
|
||||
|
||||
val obtainSourceMethod = OpenALLwjgl3Audio::class.java.getDeclaredMethod("obtainSource", java.lang.Boolean.TYPE)
|
||||
obtainSourceMethod.isAccessible = true
|
||||
val alSource = obtainSourceMethod.invoke(audio, true) as Int
|
||||
|
||||
alSource
|
||||
}
|
||||
|
||||
private val alBuffers = Array(4) {
|
||||
val buffers = IntArray(3)
|
||||
AL11.alGenBuffers(buffers)
|
||||
buffers
|
||||
}
|
||||
|
||||
private fun freeAlSources() {
|
||||
audioDevices.forEachIndexed { index, adev ->
|
||||
val audioField = OpenALAudioDevice::class.java.getDeclaredField("audio")
|
||||
audioField.isAccessible = true
|
||||
val audio = audioField.get(adev) as OpenALLwjgl3Audio
|
||||
|
||||
val freeSourceMethod = OpenALLwjgl3Audio::class.java.getDeclaredMethod("freeSource", java.lang.Integer.TYPE)
|
||||
freeSourceMethod.isAccessible = true
|
||||
freeSourceMethod.invoke(audio, alSources[index])
|
||||
}
|
||||
}
|
||||
|
||||
private fun enqueuePacket(alSource: Int, alBuffer: Int, data: ByteBuffer) {
|
||||
AL11.alBufferData(alBuffer, AL11.AL_FORMAT_STEREO8, data, SAMPLING_RATE)
|
||||
AL11.alSourceQueueBuffers(alSource, alBuffer)
|
||||
|
||||
}*/
|
||||
|
||||
private val pcmCurrentPosInSamples = ShortArray(4)
|
||||
|
||||
private var pcmPlaybackWatchdogs = Array(4) { Thread {
|
||||
|
||||
} }
|
||||
|
||||
private fun getRenderFun(pheadNum: Int): () -> Unit = { while (true) {
|
||||
render(playheads[pheadNum], pheadNum)
|
||||
render(playheads[pheadNum])
|
||||
Thread.sleep(1)
|
||||
} }
|
||||
|
||||
private fun getQueueingFun(pheadNum: Int): () -> Unit = { while (true) {
|
||||
|
||||
playheads[pheadNum].let {
|
||||
if (it.pcmUploadLength > 0) {
|
||||
if (it.pcmQueue.size < 4 && it.pcmUpload && it.pcmUploadLength > 0) {
|
||||
printdbg("Downloading samples ${it.pcmUploadLength}")
|
||||
|
||||
val samples = FloatArray(it.pcmUploadLength) { pcmBin[it.toLong()].toUint().div(255f) * 2f - 1f }
|
||||
val samples = ByteArray(it.pcmUploadLength)
|
||||
UnsafeHelper.memcpyRaw(null, pcmBin.ptr, samples, UnsafeHelper.getArrayOffset(samples), it.pcmUploadLength.toLong())
|
||||
it.pcmQueue.addLast(samples)
|
||||
|
||||
it.pcmUploadLength = 0
|
||||
@@ -115,14 +73,21 @@ class AudioAdapter(val vm: VM) : PeriBase {
|
||||
|
||||
printdbg("buffer size: $deviceBufferSize x $deviceBufferCount")
|
||||
|
||||
audioDevices = Array(4) { OpenALBufferedAudioDevice(
|
||||
Gdx.audio as OpenALLwjgl3Audio,
|
||||
SAMPLING_RATE,
|
||||
false,
|
||||
deviceBufferSize,
|
||||
deviceBufferCount) {
|
||||
playheads = Array(4) {
|
||||
val adev = OpenALBufferedAudioDevice(
|
||||
Gdx.audio as OpenALLwjgl3Audio,
|
||||
SAMPLING_RATE,
|
||||
false,
|
||||
deviceBufferSize,
|
||||
deviceBufferCount
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
Playhead(index = it, audioDevice = adev)
|
||||
}
|
||||
|
||||
} }
|
||||
|
||||
|
||||
// printdbg("AudioAdapter latency: ${audioDevice.latency}")
|
||||
@@ -134,7 +99,7 @@ class AudioAdapter(val vm: VM) : PeriBase {
|
||||
/**
|
||||
* Put this function into a separate thread and keep track of the delta time by yourself
|
||||
*/
|
||||
private fun render(playhead: Playhead, pheadNum: Int) {
|
||||
private fun render(playhead: Playhead) {
|
||||
if (playhead.isPcmMode) {
|
||||
|
||||
val writeQueue = playhead.pcmQueue
|
||||
@@ -146,14 +111,12 @@ class AudioAdapter(val vm: VM) : PeriBase {
|
||||
val samples = writeQueue.removeFirst()
|
||||
playhead.position = writeQueue.size
|
||||
|
||||
printdbg("P${pheadNum+1} Vol ${playhead.masterVolume}; LpP ${playhead.pcmUploadLength}; start playback...")
|
||||
// printdbg("P${playhead.index+1} Vol ${playhead.masterVolume}; LpP ${playhead.pcmUploadLength}; start playback...")
|
||||
// printdbg(""+(0..42).joinToString { String.format("%.2f", samples[it]) })
|
||||
if (playhead.masterVolume == 0) printdbg("P${pheadNum+1} volume is zero!")
|
||||
|
||||
audioDevices[pheadNum].setVolume(playhead.masterVolume / 255f)
|
||||
audioDevices[pheadNum].writeSamples(samples, 0, samples.size)
|
||||
playhead.audioDevice.writeSamplesUI8(samples, 0, samples.size)
|
||||
|
||||
printdbg("P${pheadNum+1} go back to spinning")
|
||||
// printdbg("P${playhead.index+1} go back to spinning")
|
||||
|
||||
}
|
||||
else if (playhead.isPlaying) {
|
||||
@@ -215,8 +178,7 @@ class AudioAdapter(val vm: VM) : PeriBase {
|
||||
override fun dispose() {
|
||||
renderThreads.forEach { it.interrupt() }
|
||||
writeQueueingThreads.forEach { it.interrupt() }
|
||||
audioDevices.forEach { it.dispose() }
|
||||
// freeAlSources()
|
||||
playheads.forEach { it.dispose() }
|
||||
sampleBin.destroy()
|
||||
pcmBin.destroy()
|
||||
}
|
||||
@@ -270,6 +232,8 @@ class AudioAdapter(val vm: VM) : PeriBase {
|
||||
internal object PlayInstNop : PlayInstruction(0)
|
||||
|
||||
internal data class Playhead(
|
||||
val index: Int,
|
||||
|
||||
var position: Int = 0,
|
||||
var pcmUploadLength: Int = 0,
|
||||
var masterVolume: Int = 0,
|
||||
@@ -277,11 +241,13 @@ class AudioAdapter(val vm: VM) : PeriBase {
|
||||
// flags
|
||||
var isPcmMode: Boolean = false,
|
||||
var isPlaying: Boolean = false,
|
||||
var samplingRateMult: ThreeFiveMiniUfloat = ThreeFiveMiniUfloat(32),
|
||||
// var samplingRateMult: ThreeFiveMiniUfloat = ThreeFiveMiniUfloat(32),
|
||||
var bpm: Int = 120, // "stored" as 96
|
||||
var tickRate: Int = 6,
|
||||
var pcmUpload: Boolean = false,
|
||||
|
||||
var pcmQueue: Queue<FloatArray> = Queue<FloatArray>()
|
||||
var pcmQueue: Queue<ByteArray> = Queue<ByteArray>(),
|
||||
val audioDevice: OpenALBufferedAudioDevice
|
||||
) {
|
||||
fun read(index: Int): Byte = when (index) {
|
||||
0 -> position.toByte()
|
||||
@@ -291,31 +257,37 @@ class AudioAdapter(val vm: VM) : PeriBase {
|
||||
4 -> masterVolume.toByte()
|
||||
5 -> masterPan.toByte()
|
||||
6 -> (isPcmMode.toInt().shl(7) or isPlaying.toInt().shl(4)).toByte()
|
||||
7 -> samplingRateMult.index.toByte()
|
||||
7 -> 0
|
||||
8 -> (bpm - 24).toByte()
|
||||
9 -> tickRate.toByte()
|
||||
else -> throw InternalError("Bad offset $index")
|
||||
}
|
||||
|
||||
fun write(index: Int, byte: Int) = when (index) {
|
||||
0 -> if (!isPcmMode) { position = position.and(0xff00) or position } else {}
|
||||
1 -> if (!isPcmMode) { position = position.and(0x00ff) or position.shl(8) } else {}
|
||||
2 -> { pcmUploadLength = pcmUploadLength.and(0xff00) or pcmUploadLength }
|
||||
3 -> { pcmUploadLength = pcmUploadLength.and(0x00ff) or pcmUploadLength.shl(8) }
|
||||
4 -> { masterVolume = byte }
|
||||
5 -> { masterPan = byte }
|
||||
6 -> { byte.let {
|
||||
val oldPcmMode = isPcmMode
|
||||
isPcmMode = (it and 0b10000000) != 0
|
||||
isPlaying = (it and 0b00010000) != 0
|
||||
fun write(index: Int, byte: Int) {
|
||||
val byte = byte and 255
|
||||
when (index) {
|
||||
0 -> if (!isPcmMode) { position = position.and(0xff00) or position } else {}
|
||||
1 -> if (!isPcmMode) { position = position.and(0x00ff) or position.shl(8) } else {}
|
||||
2 -> { pcmUploadLength = pcmUploadLength.and(0xff00) or pcmUploadLength }
|
||||
3 -> { pcmUploadLength = pcmUploadLength.and(0x00ff) or pcmUploadLength.shl(8) }
|
||||
4 -> {
|
||||
masterVolume = byte
|
||||
audioDevice.setVolume(masterVolume / 255f)
|
||||
}
|
||||
5 -> { masterPan = byte }
|
||||
6 -> { byte.let {
|
||||
val oldPcmMode = isPcmMode
|
||||
isPcmMode = (it and 0b10000000) != 0
|
||||
isPlaying = (it and 0b00010000) != 0
|
||||
|
||||
if (it and 0b01000000 != 0 || oldPcmMode != isPcmMode) resetParams()
|
||||
if (it and 0b00100000 != 0) purgeQueue()
|
||||
} }
|
||||
7 -> { samplingRateMult = ThreeFiveMiniUfloat(byte) }
|
||||
8 -> { bpm = byte + 24 }
|
||||
9 -> { tickRate = byte }
|
||||
else -> throw InternalError("Bad offset $index")
|
||||
if (it and 0b01000000 != 0 || oldPcmMode != isPcmMode) resetParams()
|
||||
if (it and 0b00100000 != 0) purgeQueue()
|
||||
} }
|
||||
7 -> if (isPcmMode) { pcmUpload = true } else {}
|
||||
8 -> { bpm = byte + 24 }
|
||||
9 -> { tickRate = byte }
|
||||
else -> throw InternalError("Bad offset $index")
|
||||
}
|
||||
}
|
||||
|
||||
fun getSamplingRate() = 30000 - ((bpm - 24).and(255) or tickRate.and(255).shl(8)).toShort().toInt()
|
||||
@@ -338,6 +310,10 @@ class AudioAdapter(val vm: VM) : PeriBase {
|
||||
pcmUploadLength = 0
|
||||
}
|
||||
}
|
||||
|
||||
fun dispose() {
|
||||
audioDevice.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
internal data class TaudPlayData(
|
||||
|
||||
@@ -3,6 +3,8 @@ package net.torvald.tsvm.peripheral
|
||||
import com.badlogic.gdx.Gdx
|
||||
import com.badlogic.gdx.Input
|
||||
import com.badlogic.gdx.InputProcessor
|
||||
import net.torvald.AddressOverflowException
|
||||
import net.torvald.DanglingPointerException
|
||||
import net.torvald.UnsafeHelper
|
||||
import net.torvald.tsvm.CircularArray
|
||||
import net.torvald.tsvm.VM
|
||||
@@ -159,77 +161,96 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor {
|
||||
override fun mmio_write(addr: Long, byte: Byte) {
|
||||
val adi = addr.toInt()
|
||||
val bi = byte.toInt().and(255)
|
||||
when (addr) {
|
||||
37L -> keyboardBuffer.appendHead(byte)
|
||||
38L -> {
|
||||
keyboardInputRequested = (byte.isNonZero())
|
||||
if (keyboardInputRequested) keyboardBuffer.clear()
|
||||
try {
|
||||
when (addr) {
|
||||
37L -> keyboardBuffer.appendHead(byte)
|
||||
38L -> {
|
||||
keyboardInputRequested = (byte.isNonZero())
|
||||
if (keyboardInputRequested) keyboardBuffer.clear()
|
||||
}
|
||||
|
||||
39L -> rawInputFunctionLatched = (byte.isNonZero())
|
||||
in 40..47 -> keyEventBuffers[adi - 40] = byte
|
||||
68L -> {
|
||||
uptimeCounterLatched = byte.and(0b01).isNonZero()
|
||||
RTClatched = byte.and(0b10).isNonZero()
|
||||
}
|
||||
|
||||
88L -> vm.romMapping = bi
|
||||
89L -> {
|
||||
acpiShutoff = byte.and(-128).isNonZero()
|
||||
}
|
||||
|
||||
in 1024..2047 -> peripheralFast[addr - 1024] = byte
|
||||
|
||||
4076L -> blockTransferPorts[0].statusCode.set(bi)
|
||||
4077L -> blockTransferPorts[1].statusCode.set(bi)
|
||||
4078L -> blockTransferPorts[2].statusCode.set(bi)
|
||||
4079L -> blockTransferPorts[3].statusCode.set(bi)
|
||||
|
||||
4084L ->
|
||||
blockTransferPorts[0].blockSize.getAcquire().let {
|
||||
blockTransferPorts[0].blockSize.setRelease(it.and(0xFF00) or byte.toInt().and(255))
|
||||
}
|
||||
|
||||
4085L -> {
|
||||
blockTransferPorts[0].hasNext.set(byte < 0)
|
||||
blockTransferPorts[0].blockSize.getAcquire().let {
|
||||
blockTransferPorts[0].blockSize.setRelease(it.and(0x00FF) or byte.toInt().and(15))
|
||||
}
|
||||
}
|
||||
|
||||
4086L -> blockTransferPorts[1].blockSize.getAcquire().let {
|
||||
blockTransferPorts[1].blockSize.setRelease(it.and(0xFF00) or byte.toInt().and(255))
|
||||
}
|
||||
|
||||
4087L -> {
|
||||
blockTransferPorts[1].hasNext.set(byte < 0)
|
||||
blockTransferPorts[1].blockSize.getAcquire().let {
|
||||
blockTransferPorts[1].blockSize.setRelease(it.and(0x00FF) or byte.toInt().and(15))
|
||||
}
|
||||
}
|
||||
|
||||
4088L -> blockTransferPorts[2].blockSize.getAcquire().let {
|
||||
blockTransferPorts[2].blockSize.setRelease(it.and(0xFF00) or byte.toInt().and(255))
|
||||
}
|
||||
|
||||
4089L -> {
|
||||
blockTransferPorts[2].hasNext.set(byte < 0)
|
||||
blockTransferPorts[2].blockSize.getAcquire().let {
|
||||
blockTransferPorts[2].blockSize.setRelease(it.and(0x00FF) or byte.toInt().and(15))
|
||||
}
|
||||
}
|
||||
|
||||
4090L -> blockTransferPorts[3].blockSize.getAcquire().let {
|
||||
blockTransferPorts[3].blockSize.setRelease(it.and(0xFF00) or byte.toInt().and(255))
|
||||
}
|
||||
|
||||
4091L -> {
|
||||
blockTransferPorts[3].hasNext.set(byte < 0)
|
||||
blockTransferPorts[3].blockSize.getAcquire().let {
|
||||
blockTransferPorts[3].blockSize.setRelease(it.and(0x00FF) or byte.toInt().and(15))
|
||||
}
|
||||
}
|
||||
|
||||
in 4092..4095 -> setBlockTransferPortStatus(adi - 4092, byte)
|
||||
|
||||
in 4096..8191 -> blockTransferTx[0][addr - 4096] = byte
|
||||
in 8192..12287 -> blockTransferTx[1][addr - 8192] = byte
|
||||
in 12288..16383 -> blockTransferTx[2][addr - 12288] = byte
|
||||
in 16384..20479 -> blockTransferTx[3][addr - 16384] = byte
|
||||
|
||||
in 131072..262143 -> vm.peripheralTable[1].peripheral?.mmio_write(addr - 131072, byte)
|
||||
in 262144..393215 -> vm.peripheralTable[2].peripheral?.mmio_write(addr - 262144, byte)
|
||||
in 393216..524287 -> vm.peripheralTable[3].peripheral?.mmio_write(addr - 393216, byte)
|
||||
in 524288..655359 -> vm.peripheralTable[4].peripheral?.mmio_write(addr - 524288, byte)
|
||||
in 655360..786431 -> vm.peripheralTable[5].peripheral?.mmio_write(addr - 655360, byte)
|
||||
in 786432..917503 -> vm.peripheralTable[6].peripheral?.mmio_write(addr - 786432, byte)
|
||||
in 917504..1048575 -> vm.peripheralTable[7].peripheral?.mmio_write(addr - 917504, byte)
|
||||
}
|
||||
39L -> rawInputFunctionLatched = (byte.isNonZero())
|
||||
in 40..47 -> keyEventBuffers[adi - 40] = byte
|
||||
68L -> {
|
||||
uptimeCounterLatched = byte.and(0b01).isNonZero()
|
||||
RTClatched = byte.and(0b10).isNonZero()
|
||||
}
|
||||
|
||||
88L -> vm.romMapping = bi
|
||||
89L -> { acpiShutoff = byte.and(-128).isNonZero() }
|
||||
|
||||
in 1024..2047 -> peripheralFast[addr - 1024] = byte
|
||||
|
||||
4076L -> blockTransferPorts[0].statusCode.set(bi)
|
||||
4077L -> blockTransferPorts[1].statusCode.set(bi)
|
||||
4078L -> blockTransferPorts[2].statusCode.set(bi)
|
||||
4079L -> blockTransferPorts[3].statusCode.set(bi)
|
||||
|
||||
4084L ->
|
||||
blockTransferPorts[0].blockSize.getAcquire().let {
|
||||
blockTransferPorts[0].blockSize.setRelease(it.and(0xFF00) or byte.toInt().and(255)) }
|
||||
4085L -> {
|
||||
blockTransferPorts[0].hasNext.set(byte < 0)
|
||||
blockTransferPorts[0].blockSize.getAcquire().let {
|
||||
blockTransferPorts[0].blockSize.setRelease(it.and(0x00FF) or byte.toInt().and(15)) }
|
||||
}
|
||||
|
||||
4086L -> blockTransferPorts[1].blockSize.getAcquire().let {
|
||||
blockTransferPorts[1].blockSize.setRelease(it.and(0xFF00) or byte.toInt().and(255)) }
|
||||
4087L -> {
|
||||
blockTransferPorts[1].hasNext.set(byte < 0)
|
||||
blockTransferPorts[1].blockSize.getAcquire().let {
|
||||
blockTransferPorts[1].blockSize.setRelease(it.and(0x00FF) or byte.toInt().and(15)) }
|
||||
}
|
||||
|
||||
4088L -> blockTransferPorts[2].blockSize.getAcquire().let {
|
||||
blockTransferPorts[2].blockSize.setRelease(it.and(0xFF00) or byte.toInt().and(255)) }
|
||||
4089L -> {
|
||||
blockTransferPorts[2].hasNext.set(byte < 0)
|
||||
blockTransferPorts[2].blockSize.getAcquire().let {
|
||||
blockTransferPorts[2].blockSize.setRelease(it.and(0x00FF) or byte.toInt().and(15)) }
|
||||
}
|
||||
|
||||
4090L -> blockTransferPorts[3].blockSize.getAcquire().let {
|
||||
blockTransferPorts[3].blockSize.setRelease(it.and(0xFF00) or byte.toInt().and(255)) }
|
||||
4091L -> {
|
||||
blockTransferPorts[3].hasNext.set(byte < 0)
|
||||
blockTransferPorts[3].blockSize.getAcquire().let {
|
||||
blockTransferPorts[3].blockSize.setRelease(it.and(0x00FF) or byte.toInt().and(15)) }
|
||||
}
|
||||
|
||||
in 4092..4095 -> setBlockTransferPortStatus(adi - 4092, byte)
|
||||
|
||||
in 4096..8191 -> blockTransferTx[0][addr - 4096] = byte
|
||||
in 8192..12287 -> blockTransferTx[1][addr - 8192] = byte
|
||||
in 12288..16383 -> blockTransferTx[2][addr - 12288] = byte
|
||||
in 16384..20479 -> blockTransferTx[3][addr - 16384] = byte
|
||||
|
||||
in 131072..262143 -> vm.peripheralTable[1].peripheral?.mmio_write(addr - 131072, byte)
|
||||
in 262144..393215 -> vm.peripheralTable[2].peripheral?.mmio_write(addr - 262144, byte)
|
||||
in 393216..524287 -> vm.peripheralTable[3].peripheral?.mmio_write(addr - 393216, byte)
|
||||
in 524288..655359 -> vm.peripheralTable[4].peripheral?.mmio_write(addr - 524288, byte)
|
||||
in 655360..786431 -> vm.peripheralTable[5].peripheral?.mmio_write(addr - 655360, byte)
|
||||
in 786432..917503 -> vm.peripheralTable[6].peripheral?.mmio_write(addr - 786432, byte)
|
||||
in 917504..1048575 -> vm.peripheralTable[7].peripheral?.mmio_write(addr - 917504, byte)
|
||||
}
|
||||
catch (_: DanglingPointerException) {}
|
||||
catch (_: AddressOverflowException) {}
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.badlogic.gdx.backends.lwjgl3.audio.OpenALAudioDevice
|
||||
import com.badlogic.gdx.backends.lwjgl3.audio.OpenALLwjgl3Audio
|
||||
import com.badlogic.gdx.math.MathUtils
|
||||
import com.badlogic.gdx.utils.GdxRuntimeException
|
||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.toUint
|
||||
import org.lwjgl.BufferUtils
|
||||
import org.lwjgl.openal.AL10
|
||||
import org.lwjgl.openal.AL11
|
||||
@@ -64,6 +65,20 @@ class OpenALBufferedAudioDevice(
|
||||
tempBuffer = BufferUtils.createByteBuffer(bufferSize)
|
||||
}
|
||||
|
||||
fun writeSamplesUI8(samples: ByteArray, offset: Int, numSamples: Int) {
|
||||
if (bytes == null || bytes!!.size < numSamples * 2) bytes = ByteArray(numSamples * 2)
|
||||
val end = Math.min(offset + numSamples, samples.size)
|
||||
var i = offset
|
||||
var ii = 0
|
||||
while (i < end) {
|
||||
val sample = ui8toI16Hi[samples[i].toUint()]
|
||||
bytes!![ii++] = sample
|
||||
bytes!![ii++] = sample
|
||||
i++
|
||||
}
|
||||
writeSamples(bytes!!, 0, numSamples * 2)
|
||||
}
|
||||
|
||||
override fun writeSamples(samples: ShortArray, offset: Int, numSamples: Int) {
|
||||
if (bytes == null || bytes!!.size < numSamples * 2) bytes = ByteArray(numSamples * 2)
|
||||
val end = Math.min(offset + numSamples, samples.size)
|
||||
@@ -228,5 +243,7 @@ class OpenALBufferedAudioDevice(
|
||||
|
||||
companion object {
|
||||
private const val bytesPerSample = 2
|
||||
private val ui8toI16Hi = ByteArray(256) { (128 + it).toByte() }
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user