diff --git a/assets/disk0/tvdos/bin/playmp2.js b/assets/disk0/tvdos/bin/playmp2.js index a23ba38..36555e2 100644 --- a/assets/disk0/tvdos/bin/playmp2.js +++ b/assets/disk0/tvdos/bin/playmp2.js @@ -1,3 +1,7 @@ +const SND_BASE_ADDR = audio.getBaseAddr() + +if (!SND_BASE_ADDR) return 10 + const pcm = require("pcm") const interactive = exec_args[2] && exec_args[2].toLowerCase() == "/i" function printdbg(s) { if (0) serial.println(s) } @@ -75,7 +79,7 @@ let bytes_left = FILE_SIZE let decodedLength = 0 -//serial.println(`Frame size: ${FRAME_SIZE}`) +serial.println(`Frame size: ${FRAME_SIZE}`) @@ -102,7 +106,8 @@ function decodeEvent(frameSize, len) { } - decodeAndResample(samplePtrL, samplePtrR, decodePtr, len) +// decodeAndResample(samplePtrL, samplePtrR, decodePtr, len) + audio.putPcmDataByPtr(decodePtr, len, 0) audio.setSampleUploadLength(0, len) audio.startSampleUpload(0) @@ -168,13 +173,10 @@ audio.setMasterVolume(0, 255) audio.play(0) -let mp2context = audio.mp2Init() +//let mp2context = audio.mp2Init() +audio.mp2Init() // decode frame -let frame = sys.malloc(FRAME_SIZE) -let samplePtrL = sys.malloc(2304) // 16b samples -let samplePtrR = sys.malloc(2304) // 16b samples -let decodePtr = sys.malloc(2304) // 8b samples let t1 = sys.nanoTime() let bufRealTimeLen = 36 let stopPlay = false @@ -191,14 +193,23 @@ try { printPlayBar() - filebuf.readBytes(FRAME_SIZE, frame) - let [frameSize, samples] = audio.mp2DecodeFrame(mp2context, frame, true, samplePtrL, samplePtrR) - if (frameSize) { -// println(samples) - // play using decodedLR - decodeEvent(frameSize, samples) - FRAME_SIZE = frameSize // JUST IN CASE when a vbr mp2 is not filtered and played thru + + filebuf.readBytes(FRAME_SIZE, SND_BASE_ADDR - 2368) + audio.mp2Decode() + sys.waitForMemChg(SND_BASE_ADDR - 41, 255, 255) + + if (audio.getPosition(0) >= QUEUE_MAX) { + while (audio.getPosition(0) >= (QUEUE_MAX >>> 1)) { + printdbg(`Queue full, waiting until the queue has some space (${audio.getPosition(0)}/${QUEUE_MAX})`) + sys.sleep(bufRealTimeLen) + } } + audio.putPcmDataByPtr(SND_BASE_ADDR - 64, 2304, 0) + audio.setSampleUploadLength(0, 2304) + audio.startSampleUpload(0) + sys.sleep(10) + + bytes_left -= FRAME_SIZE decodedLength += FRAME_SIZE @@ -209,10 +220,6 @@ catch (e) { errorlevel = 1 } finally { - sys.free(frame) - sys.free(decodePtr) - sys.free(samplePtrL) - sys.free(samplePtrR) } return errorlevel \ No newline at end of file diff --git a/assets/disk0/tvdos/include/seqread.js b/assets/disk0/tvdos/include/seqread.js index 96b5971..28799e8 100644 --- a/assets/disk0/tvdos/include/seqread.js +++ b/assets/disk0/tvdos/include/seqread.js @@ -49,6 +49,8 @@ function readBytes(length, ptrToDecode) { let ptr = (ptrToDecode === undefined) ? sys.malloc(length) : ptrToDecode let requiredBlocks = Math.floor((readCount + length) / 4096) - Math.floor(readCount / 4096) + let destVector = (ptrToDecode >= 0) ? 1 : -1 + let completedReads = 0 // serial.println(`readBytes(${length}); readCount = ${readCount}`) @@ -74,7 +76,7 @@ function readBytes(length, ptrToDecode) { // serial.println(`Pulled a block (${thisBlockLen}); readCount = ${readCount}, completedReads = ${completedReads}, remaining = ${remaining}`) // copy from read buffer to designated position - sys.memcpy(-4097 - port*4096, ptr + completedReads, remaining) + sys.memcpy(-4097 - port*4096, ptr + completedReads*destVector, remaining) // increment readCount properly readCount += remaining @@ -90,7 +92,7 @@ function readBytes(length, ptrToDecode) { // serial.println(`Reusing a block (${thisBlockLen}); readCount = ${readCount}, completedReads = ${completedReads}`) // copy from read buffer to designated position - sys.memcpy(-4097 - port*4096 - padding, ptr + completedReads, thisBlockLen) + sys.memcpy(-4097 - port*4096 - padding, ptr + completedReads*destVector, thisBlockLen) // increment readCount properly readCount += thisBlockLen diff --git a/terranmon.txt b/terranmon.txt index 36e40c9..5ff24a8 100644 --- a/terranmon.txt +++ b/terranmon.txt @@ -419,7 +419,7 @@ Packet Types - 32 | 0 | 1 48 | 2 | 3 56 | 4 | 5 - 64 | 6 | 7 + 64 | 6 | 7 (libtwolame does not allow bitrate lower than this on 32 kHz) 80 | 8 | 9 96 | 10 | 11 112 | 12 | 13 @@ -612,7 +612,17 @@ Sound Adapter MMIO ... auto-fill to Play head #4 -32 ??: ??? +40 WO: Media Decoder Control + Write 16 to initialise the MP2 context (call this before the decoding of NEW music) + Write 1 to decode the frame as MP2 + + When called with byte 17, initialisation will precede before the decoding + +41 RO: Media Decoder Status + Non-zero value indicates the decoder is busy + +64..2367 RW: MP2 Decoded Samples (unsigned 8-bit stereo) +2368..4095 RW: MP2 Frame to be decoded Sound Hardware Info - Sampling rate: 32000 Hz diff --git a/tsvm_core/src/net/torvald/tsvm/AudioJSR223Delegate.kt b/tsvm_core/src/net/torvald/tsvm/AudioJSR223Delegate.kt index b7ee0ca..2b76747 100644 --- a/tsvm_core/src/net/torvald/tsvm/AudioJSR223Delegate.kt +++ b/tsvm_core/src/net/torvald/tsvm/AudioJSR223Delegate.kt @@ -1,9 +1,7 @@ package net.torvald.tsvm -import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.toUint import net.torvald.tsvm.peripheral.AudioAdapter -import org.graalvm.polyglot.Value -import kotlin.math.cos +import net.torvald.tsvm.peripheral.MP2Env /** * Created by minjaesong on 2022-12-31. @@ -78,6 +76,15 @@ class AudioJSR223Delegate(private val vm: VM) { +// fun mp2Init() = getFirstSnd()?.mp2Env?.initialise() + fun mp2GetInitialFrameSize(bytes: IntArray) = getFirstSnd()?.mp2Env?.getInitialFrameSize(bytes) +// fun mp2DecodeFrame(mp2: MP2Env.MP2, framePtr: Long?, pcm: Boolean, outL: Long, outR: Long) = getFirstSnd()?.mp2Env?.decodeFrame(mp2, framePtr, pcm, outL, outR) + + fun getBaseAddr(): Int? = getFirstSnd()?.let { return it.vm.findPeriSlotNum(it)?.times(-131072)?.minus(1) } + fun mp2Init() = getFirstSnd()?.mmio_write(40L, 16) + fun mp2Decode() = getFirstSnd()?.mmio_write(40L, 1) + fun mp2InitThenDecode() = getFirstSnd()?.mmio_write(40L, 17) + /* @@ -105,7 +112,7 @@ class AudioJSR223Delegate(private val vm: VM) { SOFTWARE. */ - private val synthNWin = Array(64) { i -> FloatArray(32) { j -> cos(((16 + i) * (2 * j + 1)) * (Math.PI / 64.0)).toFloat() } } + /*private val synthNWin = Array(64) { i -> FloatArray(32) { j -> cos(((16 + i) * (2 * j + 1)) * (Math.PI / 64.0)).toFloat() } } private val synthDtbl = floatArrayOf( 0.000000000f, -0.000015259f, -0.000015259f, -0.000015259f, -0.000015259f, -0.000015259f, -0.000015259f, -0.000030518f, @@ -425,551 +432,7 @@ class AudioJSR223Delegate(private val vm: VM) { } } } - } - - - - - - - - - /* - mp2dec.js JavaScript MPEG-1 Audio Layer II decoder - Copyright (C) 2011 Liam Wilson - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see http://www.gnu.org/licenses/. - */ - /* Note this is a port of kjmp2 by Martin J. Fiedler: */ - - /****************************************************************************** - ** kjmp2 -- a minimal MPEG-1 Audio Layer II decoder library ** - ******************************************************************************* - ** Copyright (C) 2006 Martin J. Fiedler martin.fiedler@gmx.net ** - ** ** - ** This software is provided 'as-is', without any express or implied ** - ** warranty. In no event will the authors be held liable for any damages ** - ** arising from the use of this software. ** - ** ** - ** Permission is granted to anyone to use this software for any purpose, ** - ** including commercial applications, and to alter it and redistribute it ** - ** freely, subject to the following restrictions: ** - ** 1. The origin of this software must not be misrepresented; you must not ** - ** claim that you wrote the original software. If you use this software ** - ** in a product, an acknowledgment in the product documentation would ** - ** be appreciated but is not required. ** - ** 2. Altered source versions must be plainly marked as such, and must not ** - ** be misrepresented as being the original software. ** - ** 3. This notice may not be removed or altered from any source ** - ** distribution. ** - ******************************************************************************/ - - private var mp2_frame: Long? = null; // ptr - private var STEREO=0; - // #define JOINT_STEREO 1 - private var JOINT_STEREO=1; - // #define DUAL_CHANNEL 2 - private var DUAL_CHANNEL=2; - // #define MONO 3 - private var MONO=3; - private val mp2_sample_rates = arrayOf(44100, 48000, 32000, 0); - private val mp2_bitrates = arrayOf(32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384); - private val mp2_scf_value = arrayOf( - 0x02000000, 0x01965FEA, 0x01428A30, 0x01000000, - 0x00CB2FF5, 0x00A14518, 0x00800000, 0x006597FB, - 0x0050A28C, 0x00400000, 0x0032CBFD, 0x00285146, - 0x00200000, 0x001965FF, 0x001428A3, 0x00100000, - 0x000CB2FF, 0x000A1451, 0x00080000, 0x00065980, - 0x00050A29, 0x00040000, 0x00032CC0, 0x00028514, - 0x00020000, 0x00019660, 0x0001428A, 0x00010000, - 0x0000CB30, 0x0000A145, 0x00008000, 0x00006598, - 0x000050A3, 0x00004000, 0x000032CC, 0x00002851, - 0x00002000, 0x00001966, 0x00001429, 0x00001000, - 0x00000CB3, 0x00000A14, 0x00000800, 0x00000659, - 0x0000050A, 0x00000400, 0x0000032D, 0x00000285, - 0x00000200, 0x00000196, 0x00000143, 0x00000100, - 0x000000CB, 0x000000A1, 0x00000080, 0x00000066, - 0x00000051, 0x00000040, 0x00000033, 0x00000028, - 0x00000020, 0x00000019, 0x00000014, 0); - private val mp2_N = Array(64) { i -> IntArray(32) { j -> - Math.floor(256.0 * Math.cos((16 + i) * ((j shl 1) + 1) * 0.0490873852123405)).toInt() - } } - private val mp2_U = IntArray(512) - private val mp2_D = arrayOf( - 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,-0x00001, - -0x00001,-0x00001,-0x00001,-0x00002,-0x00002,-0x00003,-0x00003,-0x00004, - -0x00004,-0x00005,-0x00006,-0x00006,-0x00007,-0x00008,-0x00009,-0x0000A, - -0x0000C,-0x0000D,-0x0000F,-0x00010,-0x00012,-0x00014,-0x00017,-0x00019, - -0x0001C,-0x0001E,-0x00022,-0x00025,-0x00028,-0x0002C,-0x00030,-0x00034, - -0x00039,-0x0003E,-0x00043,-0x00048,-0x0004E,-0x00054,-0x0005A,-0x00060, - -0x00067,-0x0006E,-0x00074,-0x0007C,-0x00083,-0x0008A,-0x00092,-0x00099, - -0x000A0,-0x000A8,-0x000AF,-0x000B6,-0x000BD,-0x000C3,-0x000C9,-0x000CF, - 0x000D5, 0x000DA, 0x000DE, 0x000E1, 0x000E3, 0x000E4, 0x000E4, 0x000E3, - 0x000E0, 0x000DD, 0x000D7, 0x000D0, 0x000C8, 0x000BD, 0x000B1, 0x000A3, - 0x00092, 0x0007F, 0x0006A, 0x00053, 0x00039, 0x0001D,-0x00001,-0x00023, - -0x00047,-0x0006E,-0x00098,-0x000C4,-0x000F3,-0x00125,-0x0015A,-0x00190, - -0x001CA,-0x00206,-0x00244,-0x00284,-0x002C6,-0x0030A,-0x0034F,-0x00396, - -0x003DE,-0x00427,-0x00470,-0x004B9,-0x00502,-0x0054B,-0x00593,-0x005D9, - -0x0061E,-0x00661,-0x006A1,-0x006DE,-0x00718,-0x0074D,-0x0077E,-0x007A9, - -0x007D0,-0x007EF,-0x00808,-0x0081A,-0x00824,-0x00826,-0x0081F,-0x0080E, - 0x007F5, 0x007D0, 0x007A0, 0x00765, 0x0071E, 0x006CB, 0x0066C, 0x005FF, - 0x00586, 0x00500, 0x0046B, 0x003CA, 0x0031A, 0x0025D, 0x00192, 0x000B9, - -0x0002C,-0x0011F,-0x00220,-0x0032D,-0x00446,-0x0056B,-0x0069B,-0x007D5, - -0x00919,-0x00A66,-0x00BBB,-0x00D16,-0x00E78,-0x00FDE,-0x01148,-0x012B3, - -0x01420,-0x0158C,-0x016F6,-0x0185C,-0x019BC,-0x01B16,-0x01C66,-0x01DAC, - -0x01EE5,-0x02010,-0x0212A,-0x02232,-0x02325,-0x02402,-0x024C7,-0x02570, - -0x025FE,-0x0266D,-0x026BB,-0x026E6,-0x026ED,-0x026CE,-0x02686,-0x02615, - -0x02577,-0x024AC,-0x023B2,-0x02287,-0x0212B,-0x01F9B,-0x01DD7,-0x01BDD, - 0x019AE, 0x01747, 0x014A8, 0x011D1, 0x00EC0, 0x00B77, 0x007F5, 0x0043A, - 0x00046,-0x003E5,-0x00849,-0x00CE3,-0x011B4,-0x016B9,-0x01BF1,-0x0215B, - -0x026F6,-0x02CBE,-0x032B3,-0x038D3,-0x03F1A,-0x04586,-0x04C15,-0x052C4, - -0x05990,-0x06075,-0x06771,-0x06E80,-0x0759F,-0x07CCA,-0x083FE,-0x08B37, - -0x09270,-0x099A7,-0x0A0D7,-0x0A7FD,-0x0AF14,-0x0B618,-0x0BD05,-0x0C3D8, - -0x0CA8C,-0x0D11D,-0x0D789,-0x0DDC9,-0x0E3DC,-0x0E9BD,-0x0EF68,-0x0F4DB, - -0x0FA12,-0x0FF09,-0x103BD,-0x1082C,-0x10C53,-0x1102E,-0x113BD,-0x116FB, - -0x119E8,-0x11C82,-0x11EC6,-0x120B3,-0x12248,-0x12385,-0x12467,-0x124EF, - 0x1251E, 0x124F0, 0x12468, 0x12386, 0x12249, 0x120B4, 0x11EC7, 0x11C83, - 0x119E9, 0x116FC, 0x113BE, 0x1102F, 0x10C54, 0x1082D, 0x103BE, 0x0FF0A, - 0x0FA13, 0x0F4DC, 0x0EF69, 0x0E9BE, 0x0E3DD, 0x0DDCA, 0x0D78A, 0x0D11E, - 0x0CA8D, 0x0C3D9, 0x0BD06, 0x0B619, 0x0AF15, 0x0A7FE, 0x0A0D8, 0x099A8, - 0x09271, 0x08B38, 0x083FF, 0x07CCB, 0x075A0, 0x06E81, 0x06772, 0x06076, - 0x05991, 0x052C5, 0x04C16, 0x04587, 0x03F1B, 0x038D4, 0x032B4, 0x02CBF, - 0x026F7, 0x0215C, 0x01BF2, 0x016BA, 0x011B5, 0x00CE4, 0x0084A, 0x003E6, - -0x00045,-0x00439,-0x007F4,-0x00B76,-0x00EBF,-0x011D0,-0x014A7,-0x01746, - 0x019AE, 0x01BDE, 0x01DD8, 0x01F9C, 0x0212C, 0x02288, 0x023B3, 0x024AD, - 0x02578, 0x02616, 0x02687, 0x026CF, 0x026EE, 0x026E7, 0x026BC, 0x0266E, - 0x025FF, 0x02571, 0x024C8, 0x02403, 0x02326, 0x02233, 0x0212B, 0x02011, - 0x01EE6, 0x01DAD, 0x01C67, 0x01B17, 0x019BD, 0x0185D, 0x016F7, 0x0158D, - 0x01421, 0x012B4, 0x01149, 0x00FDF, 0x00E79, 0x00D17, 0x00BBC, 0x00A67, - 0x0091A, 0x007D6, 0x0069C, 0x0056C, 0x00447, 0x0032E, 0x00221, 0x00120, - 0x0002D,-0x000B8,-0x00191,-0x0025C,-0x00319,-0x003C9,-0x0046A,-0x004FF, - -0x00585,-0x005FE,-0x0066B,-0x006CA,-0x0071D,-0x00764,-0x0079F,-0x007CF, - 0x007F5, 0x0080F, 0x00820, 0x00827, 0x00825, 0x0081B, 0x00809, 0x007F0, - 0x007D1, 0x007AA, 0x0077F, 0x0074E, 0x00719, 0x006DF, 0x006A2, 0x00662, - 0x0061F, 0x005DA, 0x00594, 0x0054C, 0x00503, 0x004BA, 0x00471, 0x00428, - 0x003DF, 0x00397, 0x00350, 0x0030B, 0x002C7, 0x00285, 0x00245, 0x00207, - 0x001CB, 0x00191, 0x0015B, 0x00126, 0x000F4, 0x000C5, 0x00099, 0x0006F, - 0x00048, 0x00024, 0x00002,-0x0001C,-0x00038,-0x00052,-0x00069,-0x0007E, - -0x00091,-0x000A2,-0x000B0,-0x000BC,-0x000C7,-0x000CF,-0x000D6,-0x000DC, - -0x000DF,-0x000E2,-0x000E3,-0x000E3,-0x000E2,-0x000E0,-0x000DD,-0x000D9, - 0x000D5, 0x000D0, 0x000CA, 0x000C4, 0x000BE, 0x000B7, 0x000B0, 0x000A9, - 0x000A1, 0x0009A, 0x00093, 0x0008B, 0x00084, 0x0007D, 0x00075, 0x0006F, - 0x00068, 0x00061, 0x0005B, 0x00055, 0x0004F, 0x00049, 0x00044, 0x0003F, - 0x0003A, 0x00035, 0x00031, 0x0002D, 0x00029, 0x00026, 0x00023, 0x0001F, - 0x0001D, 0x0001A, 0x00018, 0x00015, 0x00013, 0x00011, 0x00010, 0x0000E, - 0x0000D, 0x0000B, 0x0000A, 0x00009, 0x00008, 0x00007, 0x00007, 0x00006, - 0x00005, 0x00005, 0x00004, 0x00004, 0x00003, 0x00003, 0x00002, 0x00002, - 0x00002, 0x00002, 0x00001, 0x00001, 0x00001, 0x00001, 0x00001, 0x00001); - private val mp2_quant_lut_step1= arrayOf( - arrayOf(0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2 ), - arrayOf(0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2 )); - private val mp2_QUANT_TAB_A = 27 or 64 // Table 3-B.2a: high-rate, sblimit = 27 - private val mp2_QUANT_TAB_B = 30 or 64 // Table 3-B.2b: high-rate, sblimit = 30 - private val mp2_QUANT_TAB_C = 8 // Table 3-B.2c: low-rate, sblimit = 8 - private val mp2_QUANT_TAB_D = 12 // Table 3-B.2d: low-rate, sblimit = 12 - private val mp2_quant_lut_step2 = arrayOf( - arrayOf(mp2_QUANT_TAB_C, mp2_QUANT_TAB_C, mp2_QUANT_TAB_D), - arrayOf(mp2_QUANT_TAB_A, mp2_QUANT_TAB_A, mp2_QUANT_TAB_A), - arrayOf(mp2_QUANT_TAB_B, mp2_QUANT_TAB_A, mp2_QUANT_TAB_B)); - private val mp2_quant_lut_step3 = arrayOf( - arrayOf(0x44,0x44, - 0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34 - ), - arrayOf(0x43,0x43,0x43, - 0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42, - 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20)); - private val mp2_quant_lut_step4 = arrayOf( - arrayOf(0, 1, 2, 17), - arrayOf(0, 1, 2, 3, 4, 5, 6, 17), - arrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17), - arrayOf(0, 1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17), - arrayOf(0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17)); - private data class mp2_quantizer_spec(var nlevels: Int, var grouping: Boolean, var cw_bits: Int, var Smul: Int, var Sdiv: Int) - private val mp2_quantizer_table =arrayOf( - mp2_quantizer_spec ( 3, true, 5, 0x7FFF, 0xFFFF ), - mp2_quantizer_spec ( 5, true, 7, 0x3FFF, 0x0002 ), - mp2_quantizer_spec ( 7, false, 3, 0x2AAA, 0x0003 ), - mp2_quantizer_spec ( 9, true, 10, 0x1FFF, 0x0002 ), - mp2_quantizer_spec ( 15, false, 4, 0x1249, 0xFFFF ), - mp2_quantizer_spec ( 31, false, 5, 0x0888, 0x0003 ), - mp2_quantizer_spec ( 63, false, 6, 0x0421, 0xFFFF ), - mp2_quantizer_spec ( 127, false, 7, 0x0208, 0x0009 ), - mp2_quantizer_spec ( 255, false, 8, 0x0102, 0x007F ), - mp2_quantizer_spec ( 511, false, 9, 0x0080, 0x0002 ), - mp2_quantizer_spec ( 1023, false, 10, 0x0040, 0x0009 ), - mp2_quantizer_spec ( 2047, false, 11, 0x0020, 0x0021 ), - mp2_quantizer_spec ( 4095, false, 12, 0x0010, 0x0089 ), - mp2_quantizer_spec ( 8191, false, 13, 0x0008, 0x0249 ), - mp2_quantizer_spec ( 16383, false, 14, 0x0004, 0x0AAB ), - mp2_quantizer_spec ( 32767, false, 15, 0x0002, 0x3FFF ), - mp2_quantizer_spec ( 65535, false, 16, 0x0001, 0xFFFF )); - - val KJMP2_MAGIC= 0x32706D; - private var mp2_initialized = false; - private var mp2_bit_window = 0; - private var mp2_bits_in_window = 0; - private var mp2_frame_pos = 0; - - private fun syspeek(ptr: Long) = vm.peek(ptr)!!.toUint() - - data class MP2( - var Voffs: Int = 0, - var id: Int = 0, - var V: Array = Array(2) { IntArray(1024) } - ) - - - private fun show_bits(bit_count: Int) = (mp2_bit_window shr (24 - (bit_count))); - private fun get_bits(bit_count: Int): Int { - var result = show_bits(bit_count); - mp2_bit_window = (mp2_bit_window shl bit_count) and 0xFFFFFF; - mp2_bits_in_window -= bit_count; - while (mp2_bits_in_window < 16) { - mp2_bit_window = mp2_bit_window or (syspeek(mp2_frame!! + mp2_frame_pos++) shl (16 - mp2_bits_in_window)); - mp2_bits_in_window += 8; - } - return result; - } - - fun mp2Init(): MP2 { - val mp2 = MP2() - - // check if global initialization is required - if (!mp2_initialized) { - mp2_initialized = true; - } - - // perform local initialization: clean the context and put the magic in it - for (i in 0 until 2){ - for (j in 1023 downTo 0){ - mp2.V[i][j] = 0; - }; - }; - mp2.Voffs = 0; - mp2.id = KJMP2_MAGIC; - - return mp2 - }; - - private fun kjmp2_get_sample_rate(frame: Long?): Int { - if (frame == null){ - return 0;}; - if ((syspeek(frame) != 0xFF) || (syspeek(frame +1) != 0xFD) || ((syspeek(frame +2) - 0x10) >= 0xE0)) { - return 0;}; - return mp2_sample_rates[(syspeek(frame +2) shr 2) and 3]; - }; - - private fun read_allocation(sb: Int, b2_table: Int): mp2_quantizer_spec? { - var table_idx = mp2_quant_lut_step3[b2_table][sb]; - table_idx = mp2_quant_lut_step4[table_idx and 15][get_bits(table_idx shr 4)]; - return if (table_idx != 0) (mp2_quantizer_table[table_idx - 1]) else null - } - - private fun read_samples(q: mp2_quantizer_spec?, scalefactor: Int, sample: IntArray) { - var adj = 0; - var value = 0; - if (q == null) { - // no bits allocated for this subband - sample[0] = 0 - sample[1] = 0 - sample[2] = 0; - return; - } - // resolve scalefactor - var scalefactor = mp2_scf_value[scalefactor]; - - // decode samples - adj = q.nlevels; - if (q.grouping) { - // decode grouped samples - value = get_bits(q.cw_bits); - sample[0] = value % adj; - value = Math.floor(value.toDouble() / adj).toInt(); - sample[1] = value % adj; - sample[2] = Math.floor(value.toDouble() / adj).toInt(); - } else { - // decode direct samples - for(idx in 0 until 3) - sample[idx] = get_bits(q.cw_bits); - } - - // postmultiply samples - adj = ((adj + 1) shr 1) - 1; - for (idx in 0 until 3) { - // step 1: renormalization to [-1..1] - value = adj - sample[idx]; - value = (value * q.Smul) + Math.floor(value.toDouble() / q.Sdiv).toInt(); - // step 2: apply scalefactor - sample[idx] = ( value * (scalefactor shr 12) + ((value * (scalefactor and 4095) + 2048) shr 12)) shr 12; // scale adjust - } - } - - private var mp2_allocation: Array> = Array(2) { Array(32) { null } } - private var mp2_scfsi = Array(2) { IntArray(32) } - private var mp2_scalefactor = Array(2) { Array(32) { IntArray(3) } } - private var mp2_sample = Array(2) { Array(32) { IntArray(3) } } - - - fun mp2GetInitialFrameSize(bytes: IntArray): Int { - val b0 = bytes[0] - val b1 = bytes[1] - val b2 = bytes[2] - - // check sync pattern - if ((b0 != 0xFF) || (b1 != 0xFD) || ((b2 - 0x10) >= 0xE0)) { - throw Error("Not a MP2 Frame Head: ${listOf(b0, b1, b2).map { it.toString(16).padStart(2,'0') }.joinToString(" ")}") - } - - val sampling_frequency = (b2 shr 2) and 3 - val bit_rate_index_minus1 = ((b2 shr 4) and 15) - 1 - if (bit_rate_index_minus1 > 13){ - throw Error("Invalid bit rate") // invalid bit rate or 'free format' - } - val padding_bit = b2.shr(1) and 1 - return Math.floor(144000.0 * mp2_bitrates[bit_rate_index_minus1] / mp2_sample_rates[sampling_frequency]).toInt() + padding_bit - } - - fun mp2DecodeFrame(mp2: MP2, framePtr: Long?, pcm: Boolean, outL: Long, outR: Long): IntArray { - - var pushSizeL = 0 - var pushSizeR = 0 - fun pushL(sampleL: Int) { - vm.poke(outL + pushSizeL + 0, (sampleL and 255).toByte()) - vm.poke(outL + pushSizeL + 1, (sampleL shr 8).toByte()) - pushSizeL += 2 - } - fun pushR(sampleR: Int) { - vm.poke(outR + pushSizeR + 0, (sampleR and 255).toByte()) - vm.poke(outR + pushSizeR + 1, (sampleR shr 8).toByte()) - pushSizeR += 2 - } - - - if (framePtr == null) { - throw Error("Frame is null") - } - mp2_frame = framePtr; - val bit_rate_index_minus1: Int; - val sampling_frequency: Int; - val padding_bit: Int; - val mode: Int; - val frame_size: Int; - var bound: Int - val sblimit: Int; - val nch: Int; - var sum: Int; - var table_idx: Int; - // general sanity check - if (!mp2_initialized || (mp2.id != KJMP2_MAGIC)){ - throw Error("MP2 not initialised") - }; - // check for valid header: syncword OK, MPEG-Audio Layer 2 - if ((syspeek(mp2_frame!!) != 0xFF) || ((syspeek(mp2_frame!! +1) and 0xFE) != 0xFC)){ - throw Error("Invalid header at $mp2_frame: ${syspeek(mp2_frame!!).toString(16)} ${syspeek(mp2_frame!! +1).toString(16)}") - }; - - // set up the bitstream reader - mp2_bit_window = syspeek(mp2_frame!! +2) shl 16; - mp2_bits_in_window = 8; - mp2_frame_pos = 3; - - // read the rest of the header - bit_rate_index_minus1 = get_bits(4) - 1; - if (bit_rate_index_minus1 > 13){ - throw Error("Invalid bit rate") // invalid bit rate or 'free format' - }; - sampling_frequency = get_bits(2); - if (sampling_frequency == 3){ - throw Error("Invalid sampling frequency") - }; - padding_bit = get_bits(1); - get_bits(1); // discard private_bit - mode = get_bits(2); - - // parse the mode_extension, set up the stereo bound - if (mode == JOINT_STEREO) { - bound = (get_bits(2) + 1) shl 2; - } else { - get_bits(2); - bound = if (mode == MONO) 0 else 32; - } - - // discard the last 4 bits of the header and the CRC value, if present - get_bits(4); - if ((syspeek(mp2_frame!! +1) and 1) == 0) - get_bits(16); - - // compute the frame size - frame_size = Math.floor(144000.0 * mp2_bitrates[bit_rate_index_minus1] / mp2_sample_rates[sampling_frequency]).toInt() + padding_bit; - if (!pcm){ - return intArrayOf(frame_size, pushSizeL); // no decoding - }; - - // prepare the quantizer table lookups - table_idx = if (mode == MONO) 0 else 1; - table_idx = mp2_quant_lut_step1[table_idx][bit_rate_index_minus1]; - table_idx = mp2_quant_lut_step2[table_idx][sampling_frequency]; - sblimit = table_idx and 63; - table_idx = table_idx shr 6; - if (bound > sblimit){ - bound = sblimit; - }; - - // read the allocation information - for (sb in 0 until bound){ - for (ch in 0 until 2){ - mp2_allocation[ch][sb] = read_allocation(sb, table_idx) - }; - }; - - for (sb in bound until sblimit){ - val tmp = read_allocation(sb, table_idx) - mp2_allocation[0][sb] = tmp - mp2_allocation[1][sb] = tmp - }; - - - // read scale factor selector information - nch = if (mode == MONO) 1 else 2; - for (sb in 0 until sblimit) { - for (ch in 0 until nch){ - if (mp2_allocation[ch][sb] != null){ - mp2_scfsi[ch][sb] = get_bits(2); - }; - } - if (mode == MONO){ - mp2_scfsi[1][sb] = mp2_scfsi[0][sb]; - }; - }; - // read scale factors - for (sb in 0 until sblimit) { - for (ch in 0 until nch) { - if (mp2_allocation[ch][sb] != null) { - when (mp2_scfsi[ch][sb]) { - 0 -> { - mp2_scalefactor[ch][sb][0] = get_bits(6); - mp2_scalefactor[ch][sb][1] = get_bits(6); - mp2_scalefactor[ch][sb][2] = get_bits(6); - } - 1 -> { - val tmp = get_bits(6); - mp2_scalefactor[ch][sb][0] = tmp - mp2_scalefactor[ch][sb][1] = tmp - mp2_scalefactor[ch][sb][2] = get_bits(6); - } - 2 -> { - val tmp = get_bits(6); - mp2_scalefactor[ch][sb][0] = tmp - mp2_scalefactor[ch][sb][1] = tmp - mp2_scalefactor[ch][sb][2] = tmp - } - 3 -> { - mp2_scalefactor[ch][sb][0] = get_bits(6); - val tmp = get_bits(6); - mp2_scalefactor[ch][sb][1] = tmp - mp2_scalefactor[ch][sb][2] = tmp - } - } - } - } - if (mode == MONO){ - for (part in 0 until 3){ - mp2_scalefactor[1][sb][part] = mp2_scalefactor[0][sb][part]; - }; - }; - } - // let ppcm=0; - // coefficient input and reconstruction - for (part in 0 until 3){ - for (gr in 0 until 4) { - - // read the samples - for (sb in 0 until bound){ - for (ch in 0 until 2){ - read_samples(mp2_allocation[ch][sb], mp2_scalefactor[ch][sb][part], mp2_sample[ch][sb]); - }; - }; - for (sb in bound until sblimit) { - read_samples(mp2_allocation[0][sb], mp2_scalefactor[0][sb][part], mp2_sample[0][sb]); - - for (idx in 0 until 3){ - mp2_sample[1][sb][idx] = mp2_sample[0][sb][idx]; - }; - }; - for (ch in 0 until 2){ - for (sb in sblimit until 32){ - for (idx in 0 until 3){ - mp2_sample[ch][sb][idx] = 0; - }; - }; - }; - - // synthesis loop - for (idx in 0 until 3) { - // shifting step - val tmp = (mp2.Voffs - 64) and 1023 - mp2.Voffs = tmp - table_idx = tmp - - for (ch in 0 until 2) { - // matrixing - for (i in 0 until 64) { - sum = 0; - for (j in 0 until 32) - sum += mp2_N[i][j] * mp2_sample[ch][j][idx]; // 8b*15b=23b - // intermediate value is 28 bit (23 + 5), clamp to 14b - mp2.V[ch][table_idx + i] = (sum + 8192) shr 14; - } - - // construction of U - for (i in 0 until 8){ - for (j in 0 until 32) { - mp2_U[(i shl 6) + j] = mp2.V[ch][(table_idx + (i shl 7) + j ) and 1023]; - mp2_U[(i shl 6) + j + 32] = mp2.V[ch][(table_idx + (i shl 7) + j + 96) and 1023]; - }; - }; - // apply window - for (i in 0 until 512){ - mp2_U[i] = (mp2_U[i] * mp2_D[i] + 32) shr 6; - }; - // output samples - for (j in 0 until 32) { - sum = 0; - for (i in 0 until 16){ - sum -= mp2_U[(i shl 5) + j]; - }; - sum = (sum + 8) shr 4; - sum = sum.coerceIn(-32768, 32767) - if (ch == 0) { pushL(sum) } - if (ch == 1) { pushR(sum) } - } - } // end of synthesis channel loop - } // end of synthesis sub-block loop - - // adjust PCM output pointer: decoded 3 * 32 = 96 stereo samples - // ppcm += 192; - - } // decoding of the granule finished - } - - if (pushSizeL != pushSizeR && pushSizeR > 0) { - throw Error("Push size mismatch -- U${pushSizeL} != R${pushSizeR}") - } - return intArrayOf(frame_size, pushSizeL); - // return intArrayOf(frame_size, 2304); - }; + }*/ diff --git a/tsvm_core/src/net/torvald/tsvm/DMADelegate.kt b/tsvm_core/src/net/torvald/tsvm/DMADelegate.kt index c130045..55d5c0e 100644 --- a/tsvm_core/src/net/torvald/tsvm/DMADelegate.kt +++ b/tsvm_core/src/net/torvald/tsvm/DMADelegate.kt @@ -64,7 +64,17 @@ class DMADelegate(private val vm: VM) { val response = SerialHelper.getStatusCode(vm, portNo) if (response == 0) { val file = SerialHelper.pullMessage(vm, portNo) - UnsafeHelper.memcpyRaw(file, UnsafeHelper.getArrayOffset(file) + srcOff.toLong(), null, vm.usermem.ptr + destOff, length.toLong()) + + // to user mem + if (destOff >= 0) + UnsafeHelper.memcpyRaw(file, UnsafeHelper.getArrayOffset(file) + srcOff.toLong(), null, vm.usermem.ptr + destOff, length.toLong()) + // to hardware + else { + val destL = destOff.toLong() + for (i in 0 until length) { + vm.poke(destL - i, file[i]) + } + } } } @@ -73,7 +83,16 @@ class DMADelegate(private val vm: VM) { val response = SerialHelper.getStatusCode(vm, portNo) if (response == 0) { val msg = ByteArray(length) - UnsafeHelper.memcpyRaw(null, vm.usermem.ptr + srcOff, msg, UnsafeHelper.getArrayOffset(msg), length.toLong()) + // from user mem + if (srcOff >= 0) + UnsafeHelper.memcpyRaw(null, vm.usermem.ptr + srcOff, msg, UnsafeHelper.getArrayOffset(msg), length.toLong()) + // from hardware + else { + val srcL = srcOff.toLong() + for (i in 0 until length) { + msg[i] = vm.peek(srcL - i)!! + } + } SerialHelper.sendMessage(vm, portNo, msg) SerialHelper.sendMessage(vm, portNo, FLUSH) SerialHelper.sendMessage(vm, portNo, CLOSE) diff --git a/tsvm_core/src/net/torvald/tsvm/SerialHelper.kt b/tsvm_core/src/net/torvald/tsvm/SerialHelper.kt index 04535b8..30c541c 100644 --- a/tsvm_core/src/net/torvald/tsvm/SerialHelper.kt +++ b/tsvm_core/src/net/torvald/tsvm/SerialHelper.kt @@ -119,7 +119,7 @@ object SerialHelper { private fun setBlockTransferStatus(vm: VM, portNo: Int, blockSize: Int, moreToSend: Boolean = false) { vm.getIO().mmio_write(4084L + (portNo * 2), (blockSize and 255).toByte()) vm.getIO().mmio_write(4085L + (portNo * 2), - ((blockSize ushr 8).and(15) or (moreToSend.toInt() shl 7)).toByte() + ((blockSize ushr 8).and(15) or (moreToSend.toInt(7))).toByte() ) } @@ -131,9 +131,6 @@ object SerialHelper { return (if (rawcnt == 0) BLOCK_SIZE else rawcnt) to gotMore } - - private fun Boolean.toInt() = if (this) 1 else 0 - data class DeviceStatus(val code: Int, val message: String) } diff --git a/tsvm_core/src/net/torvald/tsvm/Utils.kt b/tsvm_core/src/net/torvald/tsvm/Utils.kt index 7dcb48f..7d15f15 100644 --- a/tsvm_core/src/net/torvald/tsvm/Utils.kt +++ b/tsvm_core/src/net/torvald/tsvm/Utils.kt @@ -3,4 +3,7 @@ package net.torvald.tsvm /** * Created by minjaesong on 2023-01-04. */ -fun getHashStr(length: Int = 5) = (0 until length).map { "YBNDRFG8EJKMCPQXOTLVWIS2A345H769"[Math.random().times(32).toInt()] }.joinToString("") \ No newline at end of file +fun getHashStr(length: Int = 5) = (0 until length).map { "YBNDRFG8EJKMCPQXOTLVWIS2A345H769"[Math.random().times(32).toInt()] }.joinToString("") + +fun Boolean.toInt(shift: Int = 0) = if (this) 1 shl shift else 0 +fun Byte.isNonZero() = this != 0.toByte() diff --git a/tsvm_core/src/net/torvald/tsvm/VM.kt b/tsvm_core/src/net/torvald/tsvm/VM.kt index f8605c6..bee0a7d 100644 --- a/tsvm_core/src/net/torvald/tsvm/VM.kt +++ b/tsvm_core/src/net/torvald/tsvm/VM.kt @@ -119,6 +119,13 @@ class VM( return null } + fun findPeriSlotNum(peri: PeriBase): Int? { + for (i in 0 until peripheralSlots) { + if (peripheralTable[i].peripheral == peri) return i + } + return null + } + fun update(delta: Float) { getIO().update(delta) } diff --git a/tsvm_core/src/net/torvald/tsvm/VMJSR223Delegate.kt b/tsvm_core/src/net/torvald/tsvm/VMJSR223Delegate.kt index ec14b20..c47b0a5 100644 --- a/tsvm_core/src/net/torvald/tsvm/VMJSR223Delegate.kt +++ b/tsvm_core/src/net/torvald/tsvm/VMJSR223Delegate.kt @@ -21,6 +21,8 @@ class VMJSR223Delegate(private val vm: VM) { fun malloc(size: Int) = vm.malloc(size) fun free(ptr: Int) = vm.free(ptr) fun memcpy(from: Int, to: Int, len: Int) { + val fromVector = if (from >= 0) 1 else -1 + val toVector = if (to >= 0) 1 else -1 val len = len.toLong() // some special cases for native memcpy val ioSpace = vm.peripheralTable[0].peripheral!! as IOSpace @@ -35,7 +37,8 @@ class VMJSR223Delegate(private val vm: VM) { UnsafeHelper.memcpy(vm.usermem.ptr + from, ioSpace.blockTransferTx[0].ptr + (-4097 - to), len) else for (i in 0 until len) { - vm.poke(to + i, vm.peek(from + i)!!) +// println("vm.memcpy($from, $to, $len) = mem[${to + i*toVector}] <- mem[${from + i*fromVector}]") + vm.poke(to + i*toVector, vm.peek(from + i*fromVector)!!) } } fun mapRom(slot: Int) { diff --git a/tsvm_core/src/net/torvald/tsvm/peripheral/AudioAdapter.kt b/tsvm_core/src/net/torvald/tsvm/peripheral/AudioAdapter.kt index 53d12e8..26c11a4 100644 --- a/tsvm_core/src/net/torvald/tsvm/peripheral/AudioAdapter.kt +++ b/tsvm_core/src/net/torvald/tsvm/peripheral/AudioAdapter.kt @@ -10,8 +10,7 @@ import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.toUint import net.torvald.tsvm.ThreeFiveMiniUfloat import net.torvald.tsvm.VM import net.torvald.tsvm.getHashStr - -private fun Boolean.toInt() = if (this) 1 else 0 +import net.torvald.tsvm.toInt private class RenderRunnable(val playhead: AudioAdapter.Playhead) : Runnable { private fun printdbg(msg: Any) { @@ -98,7 +97,7 @@ private class WriteQueueingRunnable(val playhead: AudioAdapter.Playhead, val pcm /** * Created by minjaesong on 2022-12-30. */ -class AudioAdapter(val vm: VM) : PeriBase { +class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) { private fun printdbg(msg: Any) { if (DBGPRN) println("[AudioAdapter] $msg") @@ -116,6 +115,11 @@ class AudioAdapter(val vm: VM) : PeriBase { internal val cueSheet = Array(2048) { PlayCue() } internal val pcmBin = UnsafeHelper.allocate(65536L, this) + internal val mediaFrameBin = UnsafeHelper.allocate(1728, this) + internal val mediaDecodedBin = UnsafeHelper.allocate(2304, this) + + @Volatile private var mp2Busy = false + private val renderRunnables: Array private val renderThreads: Array private val writeQueueingRunnables: Array @@ -125,7 +129,7 @@ class AudioAdapter(val vm: VM) : PeriBase { throwable.printStackTrace() } - val hash = getHashStr() + internal val mp2Env = MP2Env(vm) override fun toString() = "AudioAdapter!$hash" @@ -225,6 +229,10 @@ class AudioAdapter(val vm: VM) : PeriBase { in 10..19 -> playheads[1].read(adi - 10) in 20..29 -> playheads[2].read(adi - 20) in 30..39 -> playheads[3].read(adi - 30) + 40 -> -1 + 41 -> mp2Busy.toInt().toByte() + in 64..2367 -> mediaDecodedBin[addr - 64] + in 2368..4095 -> mediaFrameBin[addr - 2368] in 32768..65535 -> (adi - 32768).let { cueSheet[it / 16].read(it % 15) } @@ -241,6 +249,12 @@ class AudioAdapter(val vm: VM) : PeriBase { in 10..19 -> { playheads[1].write(adi - 10, bi) } in 20..29 -> { playheads[2].write(adi - 20, bi) } in 30..39 -> { playheads[3].write(adi - 30, bi) } + 40 -> { + if (bi and 16 != 0) { mp2Context = mp2Env.initialise() } + if (bi and 1 != 0) decodeMp2() + } + in 64..2367 -> { mediaDecodedBin[addr - 64] = byte } + in 2368..4095 -> { mediaFrameBin[addr - 2368] = byte } in 32768..65535 -> { (adi - 32768).let { cueSheet[it / 16].write(it % 15, bi) } } @@ -254,13 +268,20 @@ class AudioAdapter(val vm: VM) : PeriBase { playheads.forEach { it.dispose() } sampleBin.destroy() pcmBin.destroy() + mediaFrameBin.destroy() + mediaDecodedBin.destroy() } override fun getVM(): VM { return vm } - override val typestring = VM.PERITYPE_SOUND + private var mp2Context = mp2Env.initialise() + + private fun decodeMp2() { + val periMmioBase = vm.findPeriSlotNum(this)!! * -131072 - 1L + mp2Env.decodeFrameU8(mp2Context, periMmioBase - 2368, true, periMmioBase - 64) + } @@ -346,7 +367,7 @@ class AudioAdapter(val vm: VM) : PeriBase { 3 -> pcmUploadLength.ushr(8).toByte() 4 -> masterVolume.toByte() 5 -> masterPan.toByte() - 6 -> (isPcmMode.toInt().shl(7) or isPlaying.toInt().shl(4) or pcmQueueSizeIndex.and(15)).toByte() + 6 -> (isPcmMode.toInt(7) or isPlaying.toInt(4) or pcmQueueSizeIndex.and(15)).toByte() 7 -> 0 8 -> (bpm - 24).toByte() 9 -> tickRate.toByte() diff --git a/tsvm_core/src/net/torvald/tsvm/peripheral/ExtDisp.kt b/tsvm_core/src/net/torvald/tsvm/peripheral/ExtDisp.kt index 96aaa58..d21848d 100644 --- a/tsvm_core/src/net/torvald/tsvm/peripheral/ExtDisp.kt +++ b/tsvm_core/src/net/torvald/tsvm/peripheral/ExtDisp.kt @@ -13,14 +13,12 @@ import net.torvald.tsvm.VM * * Created by minjaesong on 2021-12-01. */ -class ExtDisp(val vm: VM, val width: Int, val height: Int) : PeriBase { +class ExtDisp(val vm: VM, val width: Int, val height: Int) : PeriBase("oled") { constructor(vm: VM, w: java.lang.Integer, h: java.lang.Integer) : this( vm, w.toInt(), h.toInt() ) - override val typestring = "oled" - override fun getVM(): VM { return vm } diff --git a/tsvm_core/src/net/torvald/tsvm/peripheral/GlassTty.kt b/tsvm_core/src/net/torvald/tsvm/peripheral/GlassTty.kt index f532332..9bfb57e 100644 --- a/tsvm_core/src/net/torvald/tsvm/peripheral/GlassTty.kt +++ b/tsvm_core/src/net/torvald/tsvm/peripheral/GlassTty.kt @@ -10,7 +10,7 @@ import java.util.* * A paper tty must be able to implemented by extending this class (and butchering some of the features), of which it * sets limits on some of the functions (notably 'setCursorPos') */ -abstract class GlassTty(val TEXT_ROWS: Int, val TEXT_COLS: Int) { +abstract class GlassTty(val TEXT_ROWS: Int, val TEXT_COLS: Int): PeriBase("tty") { /** * (x, y) diff --git a/tsvm_core/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt b/tsvm_core/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt index 7f971fc..7e6f320 100644 --- a/tsvm_core/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt +++ b/tsvm_core/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt @@ -12,9 +12,9 @@ import com.badlogic.gdx.utils.GdxRuntimeException import net.torvald.UnsafeHelper import net.torvald.UnsafePtr import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.toUint +import net.torvald.tsvm.* import net.torvald.tsvm.FBM import net.torvald.tsvm.LoadShader -import net.torvald.tsvm.VM import net.torvald.tsvm.kB import net.torvald.tsvm.peripheral.GraphicsAdapter.Companion.DRAW_SHADER_FRAG import java.io.InputStream @@ -51,7 +51,7 @@ class ReferenceLikeLCD(assetsRoot: String, vm: VM) : GraphicsAdapter(assetsRoot, * NOTE: if TTY size is greater than 80*32, SEGFAULT will occur because text buffer is fixed in size */ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val config: AdapterConfig, val sgr: SuperGraphicsAddonConfig = SuperGraphicsAddonConfig()) : - GlassTty(config.textRows, config.textCols), PeriBase { + GlassTty(config.textRows, config.textCols) { override val typestring = VM.PERITYPE_GPU_AND_TERM @@ -255,7 +255,7 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi } private fun getTextmodeAttirbutes(): Byte = (currentChrRom.and(15).shl(4) or - ttyRawMode.toInt().shl(1) or + ttyRawMode.toInt(1) or blinkCursor.toInt()).toByte() private fun getGraphicsAttributes(): Byte = graphicsUseSprites.toInt().toByte() @@ -291,7 +291,7 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi 16L -> framebufferScrollY.toByte() 17L -> framebufferScrollY.ushr(8).toByte() - 18L -> (drawCallBusy.toInt() or codecBusy.toInt().shl(1)).toByte() + 18L -> (drawCallBusy.toInt() or codecBusy.toInt(1)).toByte() 19L -> -1 20L -> drawCallProgramCounter.and(255).toByte() 21L -> drawCallProgramCounter.ushr(8).and(255).toByte() @@ -363,11 +363,11 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi } } - private var codecBusy = false + @Volatile private var codecBusy = false private var drawCallSize = 0 private val drawCallBuffer = Array(3640) { DrawCallEnd } - private var drawCallBusy = false + @Volatile private var drawCallBusy = false internal var drawCallProgramCounter = 0 internal var rScanline = 0 @@ -1362,8 +1362,6 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi return vm.peek(-38)!!.toInt().and(255) } - private fun Boolean.toInt() = if (this) 1 else 0 - companion object { val VRAM_SIZE = 256.kB() diff --git a/tsvm_core/src/net/torvald/tsvm/peripheral/IOSpace.kt b/tsvm_core/src/net/torvald/tsvm/peripheral/IOSpace.kt index 4a1b1b0..6d67c2a 100644 --- a/tsvm_core/src/net/torvald/tsvm/peripheral/IOSpace.kt +++ b/tsvm_core/src/net/torvald/tsvm/peripheral/IOSpace.kt @@ -8,11 +8,11 @@ import net.torvald.DanglingPointerException import net.torvald.UnsafeHelper import net.torvald.tsvm.CircularArray import net.torvald.tsvm.VM +import net.torvald.tsvm.isNonZero +import net.torvald.tsvm.toInt import kotlin.experimental.and -class IOSpace(val vm: VM) : PeriBase, InputProcessor { - - override val typestring = "io" +class IOSpace(val vm: VM) : PeriBase("io"), InputProcessor { override fun getVM(): VM { return vm @@ -60,11 +60,11 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor { } private fun composeBlockTransferStatus(portno: Int): Int { - return blockTransferPorts[portno].isMaster.toInt().shl(5) or - blockTransferPorts[portno].isSlave.toInt().shl(4) or - blockTransferPorts[portno].getMode().toInt().shl(3) or - blockTransferPorts[portno].busy.get().toInt().shl(2) or - blockTransferPorts[portno].areYouReady().toInt().shl(1) or + return blockTransferPorts[portno].isMaster.toInt(5) or + blockTransferPorts[portno].isSlave.toInt(4) or + blockTransferPorts[portno].getMode().toInt(3) or + blockTransferPorts[portno].busy.get().toInt(2) or + blockTransferPorts[portno].areYouReady().toInt(1) or blockTransferPorts[portno].cableConnected().toInt() } @@ -103,17 +103,17 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor { 38L -> keyboardInputRequested.toInt().toByte() 39L -> rawInputFunctionLatched.toInt().toByte() in 40..47 -> keyEventBuffers[adi - 40] - 48L -> ((vm.resetDown.toInt() shl 7) or (vm.stopDown.toInt())).toByte() + 48L -> ((vm.resetDown.toInt(7)) or (vm.stopDown.toInt())).toByte() in 64..67 -> vm.memsize.shr((adi - 64) * 8).toByte() - 68L -> (uptimeCounterLatched.toInt() or RTClatched.toInt().shl(1)).toByte() + 68L -> (uptimeCounterLatched.toInt() or RTClatched.toInt(1)).toByte() in 72..79 -> systemUptime.ushr((adi - 72) * 8).and(255).toByte() in 80..87 -> rtc.ushr((adi - 80) * 8).and(255).toByte() 88L -> vm.romMapping.toByte() - 89L -> ((acpiShutoff.toInt() shl 7) or (bmsIsBatteryOperated.toInt() shl 3) or (bmsHasBattery.toInt() shl 1) + 89L -> ((acpiShutoff.toInt(7)) or (bmsIsBatteryOperated.toInt(3)) or (bmsHasBattery.toInt(1)) or bmsIsCharging.toInt()).toByte() in 1024..2047 -> peripheralFast[addr - 1024] @@ -129,13 +129,13 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor { 4083L -> blockTransferPorts[3].getYourStatusCode().toByte() 4084L -> (blockTransferPorts[0].yourBlockSize().toByte()) - 4085L -> (blockTransferPorts[0].doYouHaveNext().toInt().shl(7) or blockTransferPorts[0].yourBlockSize().ushr(8).and(15)).toByte() + 4085L -> (blockTransferPorts[0].doYouHaveNext().toInt(7) or blockTransferPorts[0].yourBlockSize().ushr(8).and(15)).toByte() 4086L -> (blockTransferPorts[1].yourBlockSize().toByte()) - 4087L -> (blockTransferPorts[1].doYouHaveNext().toInt().shl(7) or blockTransferPorts[1].yourBlockSize().ushr(8).and(15)).toByte() + 4087L -> (blockTransferPorts[1].doYouHaveNext().toInt(7) or blockTransferPorts[1].yourBlockSize().ushr(8).and(15)).toByte() 4088L -> (blockTransferPorts[2].yourBlockSize().toByte()) - 4089L -> (blockTransferPorts[2].doYouHaveNext().toInt().shl(7) or blockTransferPorts[2].yourBlockSize().ushr(8).and(15)).toByte() + 4089L -> (blockTransferPorts[2].doYouHaveNext().toInt(7) or blockTransferPorts[2].yourBlockSize().ushr(8).and(15)).toByte() 4090L -> (blockTransferPorts[3].yourBlockSize().toByte()) - 4091L -> (blockTransferPorts[3].doYouHaveNext().toInt().shl(7) or blockTransferPorts[3].yourBlockSize().ushr(8).and(15)).toByte() + 4091L -> (blockTransferPorts[3].doYouHaveNext().toInt(7) or blockTransferPorts[3].yourBlockSize().ushr(8).and(15)).toByte() in 4092..4095 -> composeBlockTransferStatus(adi - 4092).toByte() @@ -389,6 +389,4 @@ class IOSpace(val vm: VM) : PeriBase, InputProcessor { return false } - private fun Boolean.toInt() = if (this) 1 else 0 - private fun Byte.isNonZero() = this != 0.toByte() } diff --git a/tsvm_core/src/net/torvald/tsvm/peripheral/MP2Env.kt b/tsvm_core/src/net/torvald/tsvm/peripheral/MP2Env.kt new file mode 100644 index 0000000..4cba367 --- /dev/null +++ b/tsvm_core/src/net/torvald/tsvm/peripheral/MP2Env.kt @@ -0,0 +1,588 @@ +/* + mp2dec.js JavaScript MPEG-1 Audio Layer II decoder + Copyright (C) 2011 Liam Wilson + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/. + */ +/* Note this is a port of kjmp2 by Martin J. Fiedler: */ + +/****************************************************************************** + ** kjmp2 -- a minimal MPEG-1 Audio Layer II decoder library ** + ******************************************************************************* + ** Copyright (C) 2006 Martin J. Fiedler martin.fiedler@gmx.net ** + ** ** + ** This software is provided 'as-is', without any express or implied ** + ** warranty. In no event will the authors be held liable for any damages ** + ** arising from the use of this software. ** + ** ** + ** Permission is granted to anyone to use this software for any purpose, ** + ** including commercial applications, and to alter it and redistribute it ** + ** freely, subject to the following restrictions: ** + ** 1. The origin of this software must not be misrepresented; you must not ** + ** claim that you wrote the original software. If you use this software ** + ** in a product, an acknowledgment in the product documentation would ** + ** be appreciated but is not required. ** + ** 2. Altered source versions must be plainly marked as such, and must not ** + ** be misrepresented as being the original software. ** + ** 3. This notice may not be removed or altered from any source ** + ** distribution. ** + ******************************************************************************/ + +package net.torvald.tsvm.peripheral + +import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.toUint +import net.torvald.tsvm.VM +import java.util.* +import kotlin.collections.ArrayList +import kotlin.math.ceil +import kotlin.math.floor + +class MP2Env(val vm: VM) { + private var mp2_frame: Long? = null; // ptr + private var mp2_frameIncr = 1 + private var STEREO=0; + // #define JOINT_STEREO 1 + private var JOINT_STEREO=1; + // #define DUAL_CHANNEL 2 + private var DUAL_CHANNEL=2; + // #define MONO 3 + private var MONO=3; + private val mp2_sample_rates = arrayOf(44100, 48000, 32000, 0); + private val mp2_bitrates = arrayOf(32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384); + private val mp2_scf_value = arrayOf( + 0x02000000, 0x01965FEA, 0x01428A30, 0x01000000, + 0x00CB2FF5, 0x00A14518, 0x00800000, 0x006597FB, + 0x0050A28C, 0x00400000, 0x0032CBFD, 0x00285146, + 0x00200000, 0x001965FF, 0x001428A3, 0x00100000, + 0x000CB2FF, 0x000A1451, 0x00080000, 0x00065980, + 0x00050A29, 0x00040000, 0x00032CC0, 0x00028514, + 0x00020000, 0x00019660, 0x0001428A, 0x00010000, + 0x0000CB30, 0x0000A145, 0x00008000, 0x00006598, + 0x000050A3, 0x00004000, 0x000032CC, 0x00002851, + 0x00002000, 0x00001966, 0x00001429, 0x00001000, + 0x00000CB3, 0x00000A14, 0x00000800, 0x00000659, + 0x0000050A, 0x00000400, 0x0000032D, 0x00000285, + 0x00000200, 0x00000196, 0x00000143, 0x00000100, + 0x000000CB, 0x000000A1, 0x00000080, 0x00000066, + 0x00000051, 0x00000040, 0x00000033, 0x00000028, + 0x00000020, 0x00000019, 0x00000014, 0); + private val mp2_N = Array(64) { i -> IntArray(32) { j -> + Math.floor(256.0 * Math.cos((16 + i) * ((j shl 1) + 1) * 0.0490873852123405)).toInt() + } } + private val mp2_U = IntArray(512) + private val mp2_D = arrayOf( + 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,-0x00001, + -0x00001,-0x00001,-0x00001,-0x00002,-0x00002,-0x00003,-0x00003,-0x00004, + -0x00004,-0x00005,-0x00006,-0x00006,-0x00007,-0x00008,-0x00009,-0x0000A, + -0x0000C,-0x0000D,-0x0000F,-0x00010,-0x00012,-0x00014,-0x00017,-0x00019, + -0x0001C,-0x0001E,-0x00022,-0x00025,-0x00028,-0x0002C,-0x00030,-0x00034, + -0x00039,-0x0003E,-0x00043,-0x00048,-0x0004E,-0x00054,-0x0005A,-0x00060, + -0x00067,-0x0006E,-0x00074,-0x0007C,-0x00083,-0x0008A,-0x00092,-0x00099, + -0x000A0,-0x000A8,-0x000AF,-0x000B6,-0x000BD,-0x000C3,-0x000C9,-0x000CF, + 0x000D5, 0x000DA, 0x000DE, 0x000E1, 0x000E3, 0x000E4, 0x000E4, 0x000E3, + 0x000E0, 0x000DD, 0x000D7, 0x000D0, 0x000C8, 0x000BD, 0x000B1, 0x000A3, + 0x00092, 0x0007F, 0x0006A, 0x00053, 0x00039, 0x0001D,-0x00001,-0x00023, + -0x00047,-0x0006E,-0x00098,-0x000C4,-0x000F3,-0x00125,-0x0015A,-0x00190, + -0x001CA,-0x00206,-0x00244,-0x00284,-0x002C6,-0x0030A,-0x0034F,-0x00396, + -0x003DE,-0x00427,-0x00470,-0x004B9,-0x00502,-0x0054B,-0x00593,-0x005D9, + -0x0061E,-0x00661,-0x006A1,-0x006DE,-0x00718,-0x0074D,-0x0077E,-0x007A9, + -0x007D0,-0x007EF,-0x00808,-0x0081A,-0x00824,-0x00826,-0x0081F,-0x0080E, + 0x007F5, 0x007D0, 0x007A0, 0x00765, 0x0071E, 0x006CB, 0x0066C, 0x005FF, + 0x00586, 0x00500, 0x0046B, 0x003CA, 0x0031A, 0x0025D, 0x00192, 0x000B9, + -0x0002C,-0x0011F,-0x00220,-0x0032D,-0x00446,-0x0056B,-0x0069B,-0x007D5, + -0x00919,-0x00A66,-0x00BBB,-0x00D16,-0x00E78,-0x00FDE,-0x01148,-0x012B3, + -0x01420,-0x0158C,-0x016F6,-0x0185C,-0x019BC,-0x01B16,-0x01C66,-0x01DAC, + -0x01EE5,-0x02010,-0x0212A,-0x02232,-0x02325,-0x02402,-0x024C7,-0x02570, + -0x025FE,-0x0266D,-0x026BB,-0x026E6,-0x026ED,-0x026CE,-0x02686,-0x02615, + -0x02577,-0x024AC,-0x023B2,-0x02287,-0x0212B,-0x01F9B,-0x01DD7,-0x01BDD, + 0x019AE, 0x01747, 0x014A8, 0x011D1, 0x00EC0, 0x00B77, 0x007F5, 0x0043A, + 0x00046,-0x003E5,-0x00849,-0x00CE3,-0x011B4,-0x016B9,-0x01BF1,-0x0215B, + -0x026F6,-0x02CBE,-0x032B3,-0x038D3,-0x03F1A,-0x04586,-0x04C15,-0x052C4, + -0x05990,-0x06075,-0x06771,-0x06E80,-0x0759F,-0x07CCA,-0x083FE,-0x08B37, + -0x09270,-0x099A7,-0x0A0D7,-0x0A7FD,-0x0AF14,-0x0B618,-0x0BD05,-0x0C3D8, + -0x0CA8C,-0x0D11D,-0x0D789,-0x0DDC9,-0x0E3DC,-0x0E9BD,-0x0EF68,-0x0F4DB, + -0x0FA12,-0x0FF09,-0x103BD,-0x1082C,-0x10C53,-0x1102E,-0x113BD,-0x116FB, + -0x119E8,-0x11C82,-0x11EC6,-0x120B3,-0x12248,-0x12385,-0x12467,-0x124EF, + 0x1251E, 0x124F0, 0x12468, 0x12386, 0x12249, 0x120B4, 0x11EC7, 0x11C83, + 0x119E9, 0x116FC, 0x113BE, 0x1102F, 0x10C54, 0x1082D, 0x103BE, 0x0FF0A, + 0x0FA13, 0x0F4DC, 0x0EF69, 0x0E9BE, 0x0E3DD, 0x0DDCA, 0x0D78A, 0x0D11E, + 0x0CA8D, 0x0C3D9, 0x0BD06, 0x0B619, 0x0AF15, 0x0A7FE, 0x0A0D8, 0x099A8, + 0x09271, 0x08B38, 0x083FF, 0x07CCB, 0x075A0, 0x06E81, 0x06772, 0x06076, + 0x05991, 0x052C5, 0x04C16, 0x04587, 0x03F1B, 0x038D4, 0x032B4, 0x02CBF, + 0x026F7, 0x0215C, 0x01BF2, 0x016BA, 0x011B5, 0x00CE4, 0x0084A, 0x003E6, + -0x00045,-0x00439,-0x007F4,-0x00B76,-0x00EBF,-0x011D0,-0x014A7,-0x01746, + 0x019AE, 0x01BDE, 0x01DD8, 0x01F9C, 0x0212C, 0x02288, 0x023B3, 0x024AD, + 0x02578, 0x02616, 0x02687, 0x026CF, 0x026EE, 0x026E7, 0x026BC, 0x0266E, + 0x025FF, 0x02571, 0x024C8, 0x02403, 0x02326, 0x02233, 0x0212B, 0x02011, + 0x01EE6, 0x01DAD, 0x01C67, 0x01B17, 0x019BD, 0x0185D, 0x016F7, 0x0158D, + 0x01421, 0x012B4, 0x01149, 0x00FDF, 0x00E79, 0x00D17, 0x00BBC, 0x00A67, + 0x0091A, 0x007D6, 0x0069C, 0x0056C, 0x00447, 0x0032E, 0x00221, 0x00120, + 0x0002D,-0x000B8,-0x00191,-0x0025C,-0x00319,-0x003C9,-0x0046A,-0x004FF, + -0x00585,-0x005FE,-0x0066B,-0x006CA,-0x0071D,-0x00764,-0x0079F,-0x007CF, + 0x007F5, 0x0080F, 0x00820, 0x00827, 0x00825, 0x0081B, 0x00809, 0x007F0, + 0x007D1, 0x007AA, 0x0077F, 0x0074E, 0x00719, 0x006DF, 0x006A2, 0x00662, + 0x0061F, 0x005DA, 0x00594, 0x0054C, 0x00503, 0x004BA, 0x00471, 0x00428, + 0x003DF, 0x00397, 0x00350, 0x0030B, 0x002C7, 0x00285, 0x00245, 0x00207, + 0x001CB, 0x00191, 0x0015B, 0x00126, 0x000F4, 0x000C5, 0x00099, 0x0006F, + 0x00048, 0x00024, 0x00002,-0x0001C,-0x00038,-0x00052,-0x00069,-0x0007E, + -0x00091,-0x000A2,-0x000B0,-0x000BC,-0x000C7,-0x000CF,-0x000D6,-0x000DC, + -0x000DF,-0x000E2,-0x000E3,-0x000E3,-0x000E2,-0x000E0,-0x000DD,-0x000D9, + 0x000D5, 0x000D0, 0x000CA, 0x000C4, 0x000BE, 0x000B7, 0x000B0, 0x000A9, + 0x000A1, 0x0009A, 0x00093, 0x0008B, 0x00084, 0x0007D, 0x00075, 0x0006F, + 0x00068, 0x00061, 0x0005B, 0x00055, 0x0004F, 0x00049, 0x00044, 0x0003F, + 0x0003A, 0x00035, 0x00031, 0x0002D, 0x00029, 0x00026, 0x00023, 0x0001F, + 0x0001D, 0x0001A, 0x00018, 0x00015, 0x00013, 0x00011, 0x00010, 0x0000E, + 0x0000D, 0x0000B, 0x0000A, 0x00009, 0x00008, 0x00007, 0x00007, 0x00006, + 0x00005, 0x00005, 0x00004, 0x00004, 0x00003, 0x00003, 0x00002, 0x00002, + 0x00002, 0x00002, 0x00001, 0x00001, 0x00001, 0x00001, 0x00001, 0x00001); + private val mp2_quant_lut_step1= arrayOf( + arrayOf(0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2 ), + arrayOf(0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2 )); + private val mp2_QUANT_TAB_A = 27 or 64 // Table 3-B.2a: high-rate, sblimit = 27 + private val mp2_QUANT_TAB_B = 30 or 64 // Table 3-B.2b: high-rate, sblimit = 30 + private val mp2_QUANT_TAB_C = 8 // Table 3-B.2c: low-rate, sblimit = 8 + private val mp2_QUANT_TAB_D = 12 // Table 3-B.2d: low-rate, sblimit = 12 + private val mp2_quant_lut_step2 = arrayOf( + arrayOf(mp2_QUANT_TAB_C, mp2_QUANT_TAB_C, mp2_QUANT_TAB_D), + arrayOf(mp2_QUANT_TAB_A, mp2_QUANT_TAB_A, mp2_QUANT_TAB_A), + arrayOf(mp2_QUANT_TAB_B, mp2_QUANT_TAB_A, mp2_QUANT_TAB_B)); + private val mp2_quant_lut_step3 = arrayOf( + arrayOf(0x44,0x44, + 0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34 + ), + arrayOf(0x43,0x43,0x43, + 0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42, + 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20)); + private val mp2_quant_lut_step4 = arrayOf( + arrayOf(0, 1, 2, 17), + arrayOf(0, 1, 2, 3, 4, 5, 6, 17), + arrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17), + arrayOf(0, 1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17), + arrayOf(0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17)); + private data class mp2_quantizer_spec(var nlevels: Int, var grouping: Boolean, var cw_bits: Int, var Smul: Int, var Sdiv: Int) + private val mp2_quantizer_table =arrayOf( + mp2_quantizer_spec ( 3, true, 5, 0x7FFF, 0xFFFF ), + mp2_quantizer_spec ( 5, true, 7, 0x3FFF, 0x0002 ), + mp2_quantizer_spec ( 7, false, 3, 0x2AAA, 0x0003 ), + mp2_quantizer_spec ( 9, true, 10, 0x1FFF, 0x0002 ), + mp2_quantizer_spec ( 15, false, 4, 0x1249, 0xFFFF ), + mp2_quantizer_spec ( 31, false, 5, 0x0888, 0x0003 ), + mp2_quantizer_spec ( 63, false, 6, 0x0421, 0xFFFF ), + mp2_quantizer_spec ( 127, false, 7, 0x0208, 0x0009 ), + mp2_quantizer_spec ( 255, false, 8, 0x0102, 0x007F ), + mp2_quantizer_spec ( 511, false, 9, 0x0080, 0x0002 ), + mp2_quantizer_spec ( 1023, false, 10, 0x0040, 0x0009 ), + mp2_quantizer_spec ( 2047, false, 11, 0x0020, 0x0021 ), + mp2_quantizer_spec ( 4095, false, 12, 0x0010, 0x0089 ), + mp2_quantizer_spec ( 8191, false, 13, 0x0008, 0x0249 ), + mp2_quantizer_spec ( 16383, false, 14, 0x0004, 0x0AAB ), + mp2_quantizer_spec ( 32767, false, 15, 0x0002, 0x3FFF ), + mp2_quantizer_spec ( 65535, false, 16, 0x0001, 0xFFFF )); + + val KJMP2_MAGIC= 0x32706D; + private var mp2_initialized = false; + private var mp2_bit_window = 0; + private var mp2_bits_in_window = 0; + private var mp2_frame_pos = 0; + + private fun syspeek(ptr: Long) = vm.peek(ptr)!!.toUint() + + data class MP2( + var Voffs: Int = 0, + var id: Int = 0, + var V: Array = Array(2) { IntArray(1024) } + ) + + + private fun show_bits(bit_count: Int) = (mp2_bit_window shr (24 - (bit_count))); + private fun get_bits(bit_count: Int): Int { + var result = show_bits(bit_count); + mp2_bit_window = (mp2_bit_window shl bit_count) and 0xFFFFFF; + mp2_bits_in_window -= bit_count; + while (mp2_bits_in_window < 16) { + mp2_bit_window = mp2_bit_window or (syspeek(mp2_frame!! + mp2_frame_pos * mp2_frameIncr) shl (16 - mp2_bits_in_window)); + mp2_frame_pos += 1 + mp2_bits_in_window += 8; + } + return result; + } + + fun initialise(): MP2 { + val mp2 = MP2() + + // check if global initialization is required + if (!mp2_initialized) { + mp2_initialized = true; + } + + // perform local initialization: clean the context and put the magic in it + for (i in 0 until 2){ + for (j in 1023 downTo 0){ + mp2.V[i][j] = 0; + }; + }; + mp2.Voffs = 0; + mp2.id = KJMP2_MAGIC; + + return mp2 + }; + + private fun read_allocation(sb: Int, b2_table: Int): mp2_quantizer_spec? { + var table_idx = mp2_quant_lut_step3[b2_table][sb]; + table_idx = mp2_quant_lut_step4[table_idx and 15][get_bits(table_idx shr 4)]; + return if (table_idx != 0) (mp2_quantizer_table[table_idx - 1]) else null + } + + private fun read_samples(q: mp2_quantizer_spec?, scalefactor: Int, sample: IntArray) { + var adj = 0; + var value = 0; + if (q == null) { + // no bits allocated for this subband + sample[0] = 0 + sample[1] = 0 + sample[2] = 0; + return; + } + // resolve scalefactor + var scalefactor = mp2_scf_value[scalefactor]; + + // decode samples + adj = q.nlevels; + if (q.grouping) { + // decode grouped samples + value = get_bits(q.cw_bits); + sample[0] = value % adj; + value = Math.floor(value.toDouble() / adj).toInt(); + sample[1] = value % adj; + sample[2] = Math.floor(value.toDouble() / adj).toInt(); + } else { + // decode direct samples + for(idx in 0 until 3) + sample[idx] = get_bits(q.cw_bits); + } + + // postmultiply samples + adj = ((adj + 1) shr 1) - 1; + for (idx in 0 until 3) { + // step 1: renormalization to [-1..1] + value = adj - sample[idx]; + value = (value * q.Smul) + Math.floor(value.toDouble() / q.Sdiv).toInt(); + // step 2: apply scalefactor + sample[idx] = ( value * (scalefactor shr 12) + ((value * (scalefactor and 4095) + 2048) shr 12)) shr 12; // scale adjust + } + } + + private var mp2_allocation: Array> = Array(2) { Array(32) { null } } + private var mp2_scfsi = Array(2) { IntArray(32) } + private var mp2_scalefactor = Array(2) { Array(32) { IntArray(3) } } + private var mp2_sample = Array(2) { Array(32) { IntArray(3) } } + + + fun getInitialFrameSize(bytes: IntArray): Int { + val b0 = bytes[0] + val b1 = bytes[1] + val b2 = bytes[2] + + // check sync pattern + if ((b0 != 0xFF) || (b1 != 0xFD) || ((b2 - 0x10) >= 0xE0)) { + throw Error("Not a MP2 Frame Head: ${listOf(b0, b1, b2).map { it.toString(16).padStart(2,'0') }.joinToString(" ")}") + } + + val sampling_frequency = (b2 shr 2) and 3 + val bit_rate_index_minus1 = ((b2 shr 4) and 15) - 1 + if (bit_rate_index_minus1 > 13){ + throw Error("Invalid bit rate") // invalid bit rate or 'free format' + } + val padding_bit = b2.shr(1) and 1 + return floor(144000.0 * mp2_bitrates[bit_rate_index_minus1] / mp2_sample_rates[sampling_frequency]).toInt() + padding_bit + } + + private fun randomRound(k: Double): Double { + val rnd = (Math.random() + Math.random()) / 2.0 // this produces triangular distribution + return if (rnd < (k - (k.toInt()))) ceil(k) else floor(k) + } + + private fun s16Tou8(i: Int): Byte { + // apply dithering + val ufval = (i.toDouble() / 65536.0) + 0.5 + val ival = randomRound(ufval * 255.0) + return ival.toInt().toByte() + } + + private val samplesL = IntArray(1152) // should contain 1152 samples + private val samplesR = IntArray(1152) // should contain 1152 samples + + internal fun decodeFrameU8(mp2: MP2, framePtr: Long?, pcm: Boolean, out: Long): IntArray { + val outVector = if (out >= 0) 1 else -1 + + var pushSizeL = 0 + var pushSizeR = 0 + + val pushL: (Int) -> Unit = { sampleL: Int -> + samplesL[pushSizeL++] = sampleL + } + val pushR: (Int) -> Unit = { sampleR: Int -> + samplesR[pushSizeR++] = sampleR + } + + val ret = _decodeFrame(mp2, framePtr, pcm, pushL, pushR) + + // dither samples and store them to the given "out" pointer + var outPos = out + for (i in 0..1151) { + vm.poke(outPos, s16Tou8(samplesL[i])) + vm.poke(outPos + outVector, s16Tou8(samplesR[i])) + outPos += 2*outVector + } + + return ret + } + + fun decodeFrame(mp2: MP2, framePtr: Long?, pcm: Boolean, outL: Long, outR: Long): IntArray { + var pushSizeL = 0 + var pushSizeR = 0 + + val pushL = { sampleL: Int -> + vm.poke(outL + pushSizeL + 0, (sampleL and 255).toByte()) + vm.poke(outL + pushSizeL + 1, (sampleL shr 8).toByte()) + pushSizeL += 2 + } + val pushR = { sampleR: Int -> + vm.poke(outR + pushSizeR + 0, (sampleR and 255).toByte()) + vm.poke(outR + pushSizeR + 1, (sampleR shr 8).toByte()) + pushSizeR += 2 + } + + return _decodeFrame(mp2, framePtr, pcm, pushL, pushR) + } + + private fun _decodeFrame(mp2: MP2, framePtr: Long?, pcm: Boolean, pushL: (Int) -> Unit, pushR: (Int) -> Unit): IntArray { + if (framePtr == null) { + throw Error("Frame is null") + } + val incr = if (framePtr >= 0) 1 else -1 + mp2_frameIncr = incr + mp2_frame = framePtr; + val bit_rate_index_minus1: Int; + val sampling_frequency: Int; + val padding_bit: Int; + val mode: Int; + val frame_size: Int; + var bound: Int + val sblimit: Int; + val nch: Int; + var sum: Int; + var table_idx: Int; + // general sanity check + if (!mp2_initialized || (mp2.id != KJMP2_MAGIC)){ + throw Error("MP2 not initialised") + }; + // check for valid header: syncword OK, MPEG-Audio Layer 2 + if ((syspeek(mp2_frame!!) != 0xFF) || ((syspeek(mp2_frame!! + 1*incr) and 0xFE) != 0xFC)){ + throw Error("Invalid MP2 header at $mp2_frame: ${syspeek(mp2_frame!!).toString(16)} ${syspeek(mp2_frame!! + 1*incr).toString(16)}") + }; + + // set up the bitstream reader + mp2_bit_window = syspeek(mp2_frame!! + 2*incr) shl 16; + mp2_bits_in_window = 8; + mp2_frame_pos = 3; + + // read the rest of the header + bit_rate_index_minus1 = get_bits(4) - 1; + if (bit_rate_index_minus1 > 13){ + throw Error("Invalid bit rate") // invalid bit rate or 'free format' + }; + sampling_frequency = get_bits(2); + if (sampling_frequency == 3){ + throw Error("Invalid sampling frequency") + }; + padding_bit = get_bits(1); + get_bits(1); // discard private_bit + mode = get_bits(2); + + // parse the mode_extension, set up the stereo bound + if (mode == JOINT_STEREO) { + bound = (get_bits(2) + 1) shl 2; + } else { + get_bits(2); + bound = if (mode == MONO) 0 else 32; + } + + // discard the last 4 bits of the header and the CRC value, if present + get_bits(4); + if ((syspeek(mp2_frame!! + 1*incr) and 1) == 0) + get_bits(16); + + // compute the frame size + frame_size = Math.floor(144000.0 * mp2_bitrates[bit_rate_index_minus1] / mp2_sample_rates[sampling_frequency]).toInt() + padding_bit; + if (!pcm){ + return intArrayOf(frame_size, 0); // no decoding + }; + + // prepare the quantizer table lookups + table_idx = if (mode == MONO) 0 else 1; + table_idx = mp2_quant_lut_step1[table_idx][bit_rate_index_minus1]; + table_idx = mp2_quant_lut_step2[table_idx][sampling_frequency]; + sblimit = table_idx and 63; + table_idx = table_idx shr 6; + if (bound > sblimit){ + bound = sblimit; + }; + + // read the allocation information + for (sb in 0 until bound){ + for (ch in 0 until 2){ + mp2_allocation[ch][sb] = read_allocation(sb, table_idx) + }; + }; + + for (sb in bound until sblimit){ + val tmp = read_allocation(sb, table_idx) + mp2_allocation[0][sb] = tmp + mp2_allocation[1][sb] = tmp + }; + + + // read scale factor selector information + nch = if (mode == MONO) 1 else 2; + for (sb in 0 until sblimit) { + for (ch in 0 until nch){ + if (mp2_allocation[ch][sb] != null){ + mp2_scfsi[ch][sb] = get_bits(2); + }; + } + if (mode == MONO){ + mp2_scfsi[1][sb] = mp2_scfsi[0][sb]; + }; + }; + // read scale factors + for (sb in 0 until sblimit) { + for (ch in 0 until nch) { + if (mp2_allocation[ch][sb] != null) { + when (mp2_scfsi[ch][sb]) { + 0 -> { + mp2_scalefactor[ch][sb][0] = get_bits(6); + mp2_scalefactor[ch][sb][1] = get_bits(6); + mp2_scalefactor[ch][sb][2] = get_bits(6); + } + 1 -> { + val tmp = get_bits(6); + mp2_scalefactor[ch][sb][0] = tmp + mp2_scalefactor[ch][sb][1] = tmp + mp2_scalefactor[ch][sb][2] = get_bits(6); + } + 2 -> { + val tmp = get_bits(6); + mp2_scalefactor[ch][sb][0] = tmp + mp2_scalefactor[ch][sb][1] = tmp + mp2_scalefactor[ch][sb][2] = tmp + } + 3 -> { + mp2_scalefactor[ch][sb][0] = get_bits(6); + val tmp = get_bits(6); + mp2_scalefactor[ch][sb][1] = tmp + mp2_scalefactor[ch][sb][2] = tmp + } + } + } + } + if (mode == MONO){ + for (part in 0 until 3){ + mp2_scalefactor[1][sb][part] = mp2_scalefactor[0][sb][part]; + }; + }; + } + // let ppcm=0; + // coefficient input and reconstruction + for (part in 0 until 3){ + for (gr in 0 until 4) { + + // read the samples + for (sb in 0 until bound){ + for (ch in 0 until 2){ + read_samples(mp2_allocation[ch][sb], mp2_scalefactor[ch][sb][part], mp2_sample[ch][sb]); + }; + }; + for (sb in bound until sblimit) { + read_samples(mp2_allocation[0][sb], mp2_scalefactor[0][sb][part], mp2_sample[0][sb]); + + for (idx in 0 until 3){ + mp2_sample[1][sb][idx] = mp2_sample[0][sb][idx]; + }; + }; + for (ch in 0 until 2){ + for (sb in sblimit until 32){ + for (idx in 0 until 3){ + mp2_sample[ch][sb][idx] = 0; + }; + }; + }; + + // synthesis loop + for (idx in 0 until 3) { + // shifting step + val tmp = (mp2.Voffs - 64) and 1023 + mp2.Voffs = tmp + table_idx = tmp + + for (ch in 0 until 2) { + // matrixing + for (i in 0 until 64) { + sum = 0; + for (j in 0 until 32) + sum += mp2_N[i][j] * mp2_sample[ch][j][idx]; // 8b*15b=23b + // intermediate value is 28 bit (23 + 5), clamp to 14b + mp2.V[ch][table_idx + i] = (sum + 8192) shr 14; + } + + // construction of U + for (i in 0 until 8){ + for (j in 0 until 32) { + mp2_U[(i shl 6) + j] = mp2.V[ch][(table_idx + (i shl 7) + j ) and 1023]; + mp2_U[(i shl 6) + j + 32] = mp2.V[ch][(table_idx + (i shl 7) + j + 96) and 1023]; + }; + }; + // apply window + for (i in 0 until 512){ + mp2_U[i] = (mp2_U[i] * mp2_D[i] + 32) shr 6; + }; + // output samples + for (j in 0 until 32) { + sum = 0; + for (i in 0 until 16){ + sum -= mp2_U[(i shl 5) + j]; + }; + sum = (sum + 8) shr 4; + sum = sum.coerceIn(-32768, 32767) + if (ch == 0) { pushL(sum) } + if (ch == 1) { pushR(sum) } + } + } // end of synthesis channel loop + } // end of synthesis sub-block loop + + // adjust PCM output pointer: decoded 3 * 32 = 96 stereo samples + // ppcm += 192; + + } // decoding of the granule finished + } + +// if (pushSizeL != pushSizeR && pushSizeR > 0) { +// throw Error("Push size mismatch -- U${pushSizeL} != R${pushSizeR}") +// } +// return intArrayOf(frame_size, pushSizeL); + return intArrayOf(frame_size, 2304); + }; + +} diff --git a/tsvm_core/src/net/torvald/tsvm/peripheral/PeriBase.kt b/tsvm_core/src/net/torvald/tsvm/peripheral/PeriBase.kt index 2efb023..fd7724b 100644 --- a/tsvm_core/src/net/torvald/tsvm/peripheral/PeriBase.kt +++ b/tsvm_core/src/net/torvald/tsvm/peripheral/PeriBase.kt @@ -1,25 +1,30 @@ package net.torvald.tsvm.peripheral import net.torvald.tsvm.VM +import net.torvald.tsvm.getHashStr -interface PeriBase { +abstract class PeriBase(open val typestring: String) { + + val hash = getHashStr() /** * Addr is not an offset; they can be "wired" into any other "chip" in the card other than its RAM */ - fun peek(addr: Long): Byte? + abstract fun peek(addr: Long): Byte? /** * Addr is not an offset; they can be "wired" into any other "chip" in the card other than its RAM */ - fun poke(addr: Long, byte: Byte) + abstract fun poke(addr: Long, byte: Byte) - fun mmio_read(addr: Long): Byte? - fun mmio_write(addr: Long, byte: Byte) + abstract fun mmio_read(addr: Long): Byte? + abstract fun mmio_write(addr: Long, byte: Byte) - fun dispose() + abstract fun dispose() - fun getVM(): VM + abstract fun getVM(): VM - val typestring: String + override fun equals(other: Any?): Boolean { + return (this.hash == (other as PeriBase).hash) + } } \ No newline at end of file diff --git a/tsvm_core/src/net/torvald/tsvm/peripheral/RamBank.kt b/tsvm_core/src/net/torvald/tsvm/peripheral/RamBank.kt index 48ee478..9535a43 100644 --- a/tsvm_core/src/net/torvald/tsvm/peripheral/RamBank.kt +++ b/tsvm_core/src/net/torvald/tsvm/peripheral/RamBank.kt @@ -8,7 +8,7 @@ import java.io.File /** * Created by minjaesong on 2022-07-20. */ -open class RamBank(val vm: VM, bankCount: Int) : PeriBase { +open class RamBank(val vm: VM, bankCount: Int) : PeriBase("ramb") { val bankSize = 524288L @@ -60,8 +60,6 @@ open class RamBank(val vm: VM, bankCount: Int) : PeriBase { } override fun getVM() = vm - - override val typestring = "RAMB" } open class RomBank(vm: VM, romfile: File, bankCount: Int) : RamBank(vm, bankCount) { diff --git a/tsvm_core/src/net/torvald/tsvm/peripheral/TTY.kt b/tsvm_core/src/net/torvald/tsvm/peripheral/TTY.kt index 1b2f84c..fb76633 100644 --- a/tsvm_core/src/net/torvald/tsvm/peripheral/TTY.kt +++ b/tsvm_core/src/net/torvald/tsvm/peripheral/TTY.kt @@ -6,7 +6,7 @@ import net.torvald.tsvm.VM import java.io.InputStream import java.io.OutputStream -class TTY(assetsRoot: String, val vm: VM) : GlassTty(TEXT_ROWS, TEXT_COLS), PeriBase { +class TTY(assetsRoot: String, val vm: VM) : GlassTty(TEXT_ROWS, TEXT_COLS) { override val typestring = VM.PERITYPE_GPU_AND_TERM diff --git a/tsvm_core/src/net/torvald/tsvm/vdc/Videotron2K.kt b/tsvm_core/src/net/torvald/tsvm/vdc/Videotron2K.kt index 4a42ad1..c6d7a98 100644 --- a/tsvm_core/src/net/torvald/tsvm/vdc/Videotron2K.kt +++ b/tsvm_core/src/net/torvald/tsvm/vdc/Videotron2K.kt @@ -2,6 +2,7 @@ package net.torvald.tsvm.vdc import net.torvald.UnsafeHelper import net.torvald.tsvm.peripheral.GraphicsAdapter +import net.torvald.tsvm.toInt import net.torvald.tsvm.vdc.Command.instSet import java.lang.NumberFormatException import java.util.* @@ -358,8 +359,6 @@ class Videotron2K(var gpu: GraphicsAdapter?) { regs.destroy() } - private fun Boolean.toInt() = if (this) 1 else 0 - private fun Array.linearSearch(selector: (T) -> Boolean): Int? { this.forEachIndexed { index, it -> if (selector.invoke(it)) return index