From f75fd0811e6f7006ed516e418f7776ecb72131b5 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 10 Oct 2023 22:41:07 +0200 Subject: [PATCH] restructure play-adpcm example code, stream-wav can now play stereo adpcm wavs --- docs/source/todo.rst | 2 - examples/cx16/pcmaudio/adpcm.p8 | 33 ++- examples/cx16/pcmaudio/play-adpcm.p8 | 325 +++++++++++++-------------- examples/cx16/pcmaudio/stream-wav.p8 | 108 ++++++++- gradle.properties | 2 +- 5 files changed, 285 insertions(+), 185 deletions(-) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 5f46d08a3..2dc1d34dc 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,8 +1,6 @@ TODO ==== -- stream-wav example: add stereo adpcm support - - [on branch: shortcircuit] investigate McCarthy evaluation again? this may also reduce code size perhaps for things like if a>4 or a<2 .... - [on branch: ir-less-branch-opcodes] IR: reduce the number of branch instructions such as BEQ, BEQR, etc (gradually), replace with CMP(I) + status branch instruction - IR: reduce amount of CMP/CMPI after instructions that set the status bits correctly (LOADs? INC? etc), but only after setting the status bits is verified! diff --git a/examples/cx16/pcmaudio/adpcm.p8 b/examples/cx16/pcmaudio/adpcm.p8 index 7678dd832..aacffd9f6 100644 --- a/examples/cx16/pcmaudio/adpcm.p8 +++ b/examples/cx16/pcmaudio/adpcm.p8 @@ -12,6 +12,9 @@ adpcm { ; $ sox --guard source.mp3 -r 8000 -c 1 -e ima-adpcm out.wav trim 01:27.50 00:09 ; $ ffmpeg -i source.mp3 -ss 00:01:27.50 -to 00:01:36.50 -ar 8000 -ac 1 -c:a adpcm_ima_wav -block_size 256 -map_metadata -1 -bitexact out.wav ; And/or use a tool such as https://github.com/dbry/adpcm-xq (make sure to set the correct block size, -b8) + ; + ; NOTE: for speed reasons this implementation doesn't guard against clipping errors. + ; if the output sounds distorted, lower the volume of the source waveform to 80% and try again etc. ; IMA-ADPCM file data stream format: @@ -40,8 +43,8 @@ adpcm { 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767] - uword @zp predict ; decoded 16 bit pcm sample for first channel. - uword @zp predict_2 ; decoded 16 bit pcm sample for second channel. + uword @requirezp predict ; decoded 16 bit pcm sample for first channel. + uword @requirezp predict_2 ; decoded 16 bit pcm sample for second channel. ubyte @requirezp index ubyte @requirezp index_2 uword @zp pstep @@ -76,8 +79,17 @@ adpcm { pstep >>= 1 cx16.r0s += pstep if nibble & %1000 - cx16.r0s = -cx16.r0s - predict += cx16.r0s as uword + predict -= cx16.r0s + else + predict += cx16.r0s + + ; NOTE: the original C/Python code uses a 32 bits prediction value and clips it to a 16 bit word + ; but for speed reasons we only work with 16 bit words here all the time (with possible clipping error) + ; if predicted > 32767: + ; predicted = 32767 + ; elif predicted < -32767: + ; predicted = - 32767 + index += t_index[nibble] if_neg ; was: if index & 128 index = 0 @@ -101,8 +113,17 @@ adpcm { pstep_2 >>= 1 cx16.r0s += pstep_2 if nibble_2 & %1000 - cx16.r0s = -cx16.r0s - predict_2 += cx16.r0s as uword + predict_2 -= cx16.r0s + else + predict_2 += cx16.r0s + + ; NOTE: the original C/Python code uses a 32 bits prediction value and clips it to a 16 bit word + ; but for speed reasons we only work with 16 bit words here all the time (with possible clipping error) + ; if predicted > 32767: + ; predicted = 32767 + ; elif predicted < -32767: + ; predicted = - 32767 + index_2 += t_index[nibble_2] if_neg ; was: if index & 128 index_2 = 0 diff --git a/examples/cx16/pcmaudio/play-adpcm.p8 b/examples/cx16/pcmaudio/play-adpcm.p8 index 4d562b622..aad59bc4d 100644 --- a/examples/cx16/pcmaudio/play-adpcm.p8 +++ b/examples/cx16/pcmaudio/play-adpcm.p8 @@ -18,11 +18,11 @@ main { ubyte adpcm_blocks_left - uword @requirezp nibblesptr uword vera_rate_hz ubyte vera_rate ubyte num_adpcm_blocks uword adpcm_size + uword @requirezp nibblesptr sub start() { if not wavfile.parse_header(&wavdata.wav_data) { @@ -45,10 +45,19 @@ main { txt.print("\n\n(b)enchmark or (p)layback? ") when cbm.CHRIN() { - 'b' -> when wavfile.nchannels { - 1-> benchmark_mono() - 2-> benchmark_stereo() - } + 'b' -> { + cbm.SETTIM(0,0,0) + when wavfile.nchannels { + 1-> { + mono.benchmark() + decoding_report(1 + 252*2) + } + 2-> { + stereo.benchmark() + decoding_report(2 + 248*4) + } + } + } 'p' -> playback() } } @@ -64,127 +73,6 @@ main { num_adpcm_blocks = (adpcm_size / 256) as ubyte ; THE ADPCM DATA NEEDS TO BE ENCODED IN 256-byte BLOCKS ! } - sub benchmark_mono() { - nibblesptr = &wavdata.wav_data + wavfile.data_offset - - txt.print("\ndecoding all blocks...\n") - cbm.SETTIM(0,0,0) - repeat num_adpcm_blocks { - adpcm.init(peekw(nibblesptr), @(nibblesptr+2)) - nibblesptr += 4 - decode_mono_nibbles() - } - - decoding_report(1 + 252*2) - } - - sub decode_mono_nibbles() { - ; slightly unrolled - ubyte @zp nibble - repeat 252/2 { - unroll 2 { - nibble = @(nibblesptr) - adpcm.decode_nibble(nibble & 15) ; first word - cx16.VERA_AUDIO_DATA = lsb(adpcm.predict) - cx16.VERA_AUDIO_DATA = msb(adpcm.predict) - adpcm.decode_nibble(nibble>>4) ; second word - cx16.VERA_AUDIO_DATA = lsb(adpcm.predict) - cx16.VERA_AUDIO_DATA = msb(adpcm.predict) - nibblesptr++ - } - } - } - - uword[8] left - uword[8] right - - sub benchmark_stereo() { - nibblesptr = &wavdata.wav_data + wavfile.data_offset - txt.print("\n\ndecoding all blocks...\n") - cbm.SETTIM(0,0,0) - - repeat num_adpcm_blocks { - - adpcm.init(peekw(nibblesptr), @(nibblesptr+2)) - nibblesptr += 4 - adpcm.init_second(peekw(nibblesptr), @(nibblesptr+2)) - nibblesptr += 4 - - repeat 248/8 { - decode_stereo_nibbles() - nibblesptr += 8 - copy_stereo_to_fifo() - } - } - - decoding_report(2 + 248*4) - } - - asmsub copy_stereo_to_fifo() clobbers(A, Y) { - %asm {{ - ; copy to vera PSG fifo buffer - ldy #0 -- lda p8_left,y - sta cx16.VERA_AUDIO_DATA - lda p8_left+1,y - sta cx16.VERA_AUDIO_DATA - lda p8_right,y - sta cx16.VERA_AUDIO_DATA - lda p8_right+1,y - sta cx16.VERA_AUDIO_DATA - iny - iny - cpy #16 - bne - - }} - } - - sub decode_stereo_nibbles() { - ; decode 4 left channel nibbles - ubyte @zp nibble = @(nibblesptr) - adpcm.decode_nibble(nibble & 15) ; first word - left[0] = adpcm.predict - adpcm.decode_nibble(nibble>>4) ; second word - left[1] = adpcm.predict - nibble = @(nibblesptr+1) - adpcm.decode_nibble(nibble & 15) ; first word - left[2] = adpcm.predict - adpcm.decode_nibble(nibble>>4) ; second word - left[3] = adpcm.predict - nibble = @(nibblesptr+2) - adpcm.decode_nibble(nibble & 15) ; first word - left[4] = adpcm.predict - adpcm.decode_nibble(nibble>>4) ; second word - left[5] = adpcm.predict - nibble = @(nibblesptr+3) - adpcm.decode_nibble(nibble & 15) ; first word - left[6] = adpcm.predict - adpcm.decode_nibble(nibble>>4) ; second word - left[7] = adpcm.predict - - ; decode 4 right channel nibbles - nibble = @(nibblesptr+4) - adpcm.decode_nibble_second(nibble & 15) ; first word - right[0] = adpcm.predict_2 - adpcm.decode_nibble_second(nibble>>4) ; second word - right[1] = adpcm.predict_2 - nibble = @(nibblesptr+5) - adpcm.decode_nibble_second(nibble & 15) ; first word - right[2] = adpcm.predict_2 - adpcm.decode_nibble_second(nibble>>4) ; second word - right[3] = adpcm.predict_2 - nibble = @(nibblesptr+6) - adpcm.decode_nibble_second(nibble & 15) ; first word - right[4] = adpcm.predict_2 - adpcm.decode_nibble_second(nibble>>4) ; second word - right[5] = adpcm.predict_2 - nibble = @(nibblesptr+7) - adpcm.decode_nibble_second(nibble & 15) ; first word - right[6] = adpcm.predict_2 - adpcm.decode_nibble_second(nibble>>4) ; second word - right[7] = adpcm.predict_2 - } - sub decoding_report(float pcm_words_per_block) { const float REFRESH_RATE = 25.0e6/(525.0*800) ; Vera VGA refresh rate is not precisely 60 hz! float duration_secs = (cbm.RDTIM16() as float) / REFRESH_RATE @@ -212,23 +100,25 @@ main { nibblesptr = &wavdata.wav_data + wavfile.data_offset adpcm_blocks_left = num_adpcm_blocks - when wavfile.nchannels { - 1 -> cx16.VERA_AUDIO_CTRL = %10101111 ; mono 16 bit - 2 -> cx16.VERA_AUDIO_CTRL = %10111111 ; stereo 16 bit - } + sys.set_irqd() cx16.VERA_AUDIO_RATE = 0 ; halt playback repeat 1024 { cx16.VERA_AUDIO_DATA = 0 } - sys.set_irqd() when wavfile.nchannels { - 1 -> cx16.CINV = &irq_handler_mono - 2 -> cx16.CINV = &irq_handler_stereo + 1 -> { + cx16.VERA_AUDIO_CTRL = %10101011 ; mono 16 bit, volume 11 + cx16.CINV = &mono.irq_handler + } + 2 -> { + cx16.VERA_AUDIO_CTRL = %10111011 ; stereo 16 bit, volume 11 + cx16.CINV = &stereo.irq_handler + } } + cx16.VERA_IEN = %00001000 ; enable AFLOW sys.clear_irqd() - cx16.VERA_AUDIO_RATE = vera_rate ; start playback txt.print("\naudio via irq\n") @@ -243,22 +133,48 @@ main { ; txt.print("audio off.\n") } - sub irq_handler_mono() { +} + +mono { + sub benchmark() { + main.nibblesptr = &wavdata.wav_data + wavfile.data_offset + txt.print("\ndecoding all blocks...\n") + repeat main.num_adpcm_blocks + decode_block() + } + + sub decode_block() { + ; refill the fifo buffer with one decoded adpcm block (1010 bytes of pcm data) + adpcm.init(peekw(main.nibblesptr), @(main.nibblesptr+2)) + cx16.VERA_AUDIO_DATA = lsb(adpcm.predict) + cx16.VERA_AUDIO_DATA = msb(adpcm.predict) + main.nibblesptr += 4 + ubyte @zp nibble + repeat 252/2 { + unroll 2 { + nibble = @(main.nibblesptr) + adpcm.decode_nibble(nibble & 15) ; first word + cx16.VERA_AUDIO_DATA = lsb(adpcm.predict) + cx16.VERA_AUDIO_DATA = msb(adpcm.predict) + adpcm.decode_nibble(nibble>>4) ; second word + cx16.VERA_AUDIO_DATA = lsb(adpcm.predict) + cx16.VERA_AUDIO_DATA = msb(adpcm.predict) + main.nibblesptr++ + } + } + } + + sub irq_handler() { if cx16.VERA_ISR & %00001000 { ; AFLOW irq. ;; cx16.vpoke(1,$fa0c, $a0) ; paint a screen color - ; refill the fifo buffer with one decoded adpcm block (1010 bytes of pcm data) - adpcm.init(peekw(nibblesptr), @(nibblesptr+2)) - cx16.VERA_AUDIO_DATA = lsb(adpcm.predict) - cx16.VERA_AUDIO_DATA = msb(adpcm.predict) - nibblesptr += 4 - decode_mono_nibbles() - adpcm_blocks_left-- - if adpcm_blocks_left==0 { + decode_block() + main.adpcm_blocks_left-- + if main.adpcm_blocks_left==0 { ; restart adpcm data from the beginning - nibblesptr = &wavdata.wav_data + wavfile.data_offset - adpcm_blocks_left = num_adpcm_blocks + main.nibblesptr = &wavdata.wav_data + wavfile.data_offset + main.adpcm_blocks_left = main.num_adpcm_blocks txt.print("end of data, restarting.\n") } @@ -276,33 +192,108 @@ main { }} } - sub irq_handler_stereo() { +} + +stereo { + + sub benchmark() { + main.nibblesptr = &wavdata.wav_data + wavfile.data_offset + txt.print("\n\ndecoding all blocks...\n") + + repeat main.num_adpcm_blocks + decode_block() + } + + sub decode_block() { + ; refill the fifo buffer with one decoded adpcm block (1010 bytes of pcm data) + adpcm.init(peekw(main.nibblesptr), @(main.nibblesptr+2)) ; left channel + cx16.VERA_AUDIO_DATA = lsb(adpcm.predict) + cx16.VERA_AUDIO_DATA = msb(adpcm.predict) + adpcm.init_second(peekw(main.nibblesptr+4), @(main.nibblesptr+6)) ; right channel + cx16.VERA_AUDIO_DATA = lsb(adpcm.predict_2) + cx16.VERA_AUDIO_DATA = msb(adpcm.predict_2) + main.nibblesptr += 8 + repeat 248/8 + decode_nibbles_unrolled() + } + + sub decode_nibbles_unrolled() { + ; decode 4 left channel nibbles + uword[8] left + uword[8] right + ubyte @requirezp nibble = @(main.nibblesptr) + adpcm.decode_nibble(nibble & 15) ; first word + left[0] = adpcm.predict + adpcm.decode_nibble(nibble>>4) ; second word + left[1] = adpcm.predict + nibble = @(main.nibblesptr+1) + adpcm.decode_nibble(nibble & 15) ; first word + left[2] = adpcm.predict + adpcm.decode_nibble(nibble>>4) ; second word + left[3] = adpcm.predict + nibble = @(main.nibblesptr+2) + adpcm.decode_nibble(nibble & 15) ; first word + left[4] = adpcm.predict + adpcm.decode_nibble(nibble>>4) ; second word + left[5] = adpcm.predict + nibble = @(main.nibblesptr+3) + adpcm.decode_nibble(nibble & 15) ; first word + left[6] = adpcm.predict + adpcm.decode_nibble(nibble>>4) ; second word + left[7] = adpcm.predict + + ; decode 4 right channel nibbles + nibble = @(main.nibblesptr+4) + adpcm.decode_nibble_second(nibble & 15) ; first word + right[0] = adpcm.predict_2 + adpcm.decode_nibble_second(nibble>>4) ; second word + right[1] = adpcm.predict_2 + nibble = @(main.nibblesptr+5) + adpcm.decode_nibble_second(nibble & 15) ; first word + right[2] = adpcm.predict_2 + adpcm.decode_nibble_second(nibble>>4) ; second word + right[3] = adpcm.predict_2 + nibble = @(main.nibblesptr+6) + adpcm.decode_nibble_second(nibble & 15) ; first word + right[4] = adpcm.predict_2 + adpcm.decode_nibble_second(nibble>>4) ; second word + right[5] = adpcm.predict_2 + nibble = @(main.nibblesptr+7) + adpcm.decode_nibble_second(nibble & 15) ; first word + right[6] = adpcm.predict_2 + adpcm.decode_nibble_second(nibble>>4) ; second word + right[7] = adpcm.predict_2 + main.nibblesptr += 8 + + %asm {{ + ; copy to vera PSG fifo buffer + ldy #0 +- lda p8_left,y + sta cx16.VERA_AUDIO_DATA + lda p8_left+1,y + sta cx16.VERA_AUDIO_DATA + lda p8_right,y + sta cx16.VERA_AUDIO_DATA + lda p8_right+1,y + sta cx16.VERA_AUDIO_DATA + iny + iny + cpy #16 + bne - + }} + } + + sub irq_handler() { if cx16.VERA_ISR & %00001000 { ; AFLOW irq. ;; cx16.vpoke(1,$fa0c, $a0) ; paint a screen color - ; refill the fifo buffer with one decoded adpcm block (1010 bytes of pcm data) - ; left channel - adpcm.init(peekw(nibblesptr), @(nibblesptr+2)) - cx16.VERA_AUDIO_DATA = lsb(adpcm.predict) - cx16.VERA_AUDIO_DATA = msb(adpcm.predict) - nibblesptr += 4 - ; right channel - adpcm.init_second(peekw(nibblesptr), @(nibblesptr+2)) - cx16.VERA_AUDIO_DATA = lsb(adpcm.predict_2) - cx16.VERA_AUDIO_DATA = msb(adpcm.predict_2) - nibblesptr += 4 - repeat 31 { - decode_stereo_nibbles() - nibblesptr += 8 - copy_stereo_to_fifo() - } - - adpcm_blocks_left-- - if adpcm_blocks_left==0 { + decode_block() + main.adpcm_blocks_left-- + if main.adpcm_blocks_left==0 { ; restart adpcm data from the beginning - nibblesptr = &wavdata.wav_data + wavfile.data_offset - adpcm_blocks_left = num_adpcm_blocks + main.nibblesptr = &wavdata.wav_data + wavfile.data_offset + main.adpcm_blocks_left = main.num_adpcm_blocks txt.print("end of data, restarting.\n") } diff --git a/examples/cx16/pcmaudio/stream-wav.p8 b/examples/cx16/pcmaudio/stream-wav.p8 index ad3513f99..b39367186 100644 --- a/examples/cx16/pcmaudio/stream-wav.p8 +++ b/examples/cx16/pcmaudio/stream-wav.p8 @@ -79,17 +79,17 @@ main { } if wavfile.wavefmt==wavfile.WAVE_FORMAT_DVI_ADPCM { - if(wavfile.block_align!=256 or wavfile.nchannels!=1) { - error("unsupported format!") + if(wavfile.block_align!=256) { + error("unsupported block alignment!") } } txt.print("\ngood file! playback starts!\n") cx16.rombank(0) ; activate kernal bank for faster calls cx16.VERA_AUDIO_RATE = 0 ; halt playback - cx16.VERA_AUDIO_CTRL = %10101111 ; mono 16 bit + cx16.VERA_AUDIO_CTRL = %10101011 ; mono 16 bit, volume 11 if wavfile.nchannels==2 - cx16.VERA_AUDIO_CTRL = %10111111 ; stereo 16 bit + cx16.VERA_AUDIO_CTRL = %10111011 ; stereo 16 bit, volume 11 if(wavfile.bits_per_sample==8) cx16.VERA_AUDIO_CTRL &= %11011111 ; set to 8 bit instead repeat 1024 @@ -103,7 +103,7 @@ main { if diskio.f_open(MUSIC_FILENAME) { uword block_size = 1024 if wavfile.wavefmt==wavfile.WAVE_FORMAT_DVI_ADPCM - block_size = wavfile.block_align + block_size = wavfile.block_align * 2 ; read 2 adpcm blocks at a time (512 bytes) void diskio.f_read(buffer, wavfile.data_offset) ; skip to actual sample data start void diskio.f_read(buffer, block_size) ; preload buffer cx16.VERA_AUDIO_RATE = vera_rate ; start playback @@ -144,6 +144,7 @@ main { interrupt { bool aflow_semaphore + uword @requirezp nibblesptr asmsub wait_and_clear_aflow_semaphore() { %asm {{ @@ -159,8 +160,17 @@ interrupt { if cx16.VERA_ISR & %00001000 { ; AFLOW irq occurred, refill buffer aflow_semaphore = 0 - if wavfile.wavefmt==wavfile.WAVE_FORMAT_DVI_ADPCM - adpcm_block() + if wavfile.wavefmt==wavfile.WAVE_FORMAT_DVI_ADPCM { + nibblesptr = main.start.buffer + if wavfile.nchannels==2 { + adpcm_block_stereo() + adpcm_block_stereo() + } + else { + adpcm_block_mono() + adpcm_block_mono() + } + } else if wavfile.bits_per_sample==16 uncompressed_block_16() else @@ -235,9 +245,8 @@ interrupt { ; } } - sub adpcm_block() { + sub adpcm_block_mono() { ; refill the fifo buffer with one decoded adpcm block (1010 bytes of pcm data) - uword @requirezp nibblesptr = main.start.buffer adpcm.init(peekw(nibblesptr), @(nibblesptr+2)) cx16.VERA_AUDIO_DATA = lsb(adpcm.predict) cx16.VERA_AUDIO_DATA = msb(adpcm.predict) @@ -256,4 +265,85 @@ interrupt { } } } + + sub adpcm_block_stereo() { + ; refill the fifo buffer with one decoded adpcm block (1010 bytes of pcm data) + adpcm.init(peekw(nibblesptr), @(nibblesptr+2)) ; left channel + cx16.VERA_AUDIO_DATA = lsb(adpcm.predict) + cx16.VERA_AUDIO_DATA = msb(adpcm.predict) + adpcm.init_second(peekw(nibblesptr+4), @(nibblesptr+6)) ; right channel + cx16.VERA_AUDIO_DATA = lsb(adpcm.predict_2) + cx16.VERA_AUDIO_DATA = msb(adpcm.predict_2) + nibblesptr += 8 + repeat 248/8 + decode_nibbles_unrolled() + } + + + sub decode_nibbles_unrolled() { + ; decode 4 left channel nibbles + uword[8] left + uword[8] right + ubyte @requirezp nibble = @(nibblesptr) + adpcm.decode_nibble(nibble & 15) ; first word + left[0] = adpcm.predict + adpcm.decode_nibble(nibble>>4) ; second word + left[1] = adpcm.predict + nibble = @(nibblesptr+1) + adpcm.decode_nibble(nibble & 15) ; first word + left[2] = adpcm.predict + adpcm.decode_nibble(nibble>>4) ; second word + left[3] = adpcm.predict + nibble = @(nibblesptr+2) + adpcm.decode_nibble(nibble & 15) ; first word + left[4] = adpcm.predict + adpcm.decode_nibble(nibble>>4) ; second word + left[5] = adpcm.predict + nibble = @(nibblesptr+3) + adpcm.decode_nibble(nibble & 15) ; first word + left[6] = adpcm.predict + adpcm.decode_nibble(nibble>>4) ; second word + left[7] = adpcm.predict + + ; decode 4 right channel nibbles + nibble = @(nibblesptr+4) + adpcm.decode_nibble_second(nibble & 15) ; first word + right[0] = adpcm.predict_2 + adpcm.decode_nibble_second(nibble>>4) ; second word + right[1] = adpcm.predict_2 + nibble = @(nibblesptr+5) + adpcm.decode_nibble_second(nibble & 15) ; first word + right[2] = adpcm.predict_2 + adpcm.decode_nibble_second(nibble>>4) ; second word + right[3] = adpcm.predict_2 + nibble = @(nibblesptr+6) + adpcm.decode_nibble_second(nibble & 15) ; first word + right[4] = adpcm.predict_2 + adpcm.decode_nibble_second(nibble>>4) ; second word + right[5] = adpcm.predict_2 + nibble = @(nibblesptr+7) + adpcm.decode_nibble_second(nibble & 15) ; first word + right[6] = adpcm.predict_2 + adpcm.decode_nibble_second(nibble>>4) ; second word + right[7] = adpcm.predict_2 + nibblesptr += 8 + + %asm {{ + ; copy to vera PSG fifo buffer + ldy #0 +- lda p8_left,y + sta cx16.VERA_AUDIO_DATA + lda p8_left+1,y + sta cx16.VERA_AUDIO_DATA + lda p8_right,y + sta cx16.VERA_AUDIO_DATA + lda p8_right+1,y + sta cx16.VERA_AUDIO_DATA + iny + iny + cpy #16 + bne - + }} + } + } diff --git a/gradle.properties b/gradle.properties index 9d0a920cf..a050c2090 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,4 +5,4 @@ org.gradle.daemon=true kotlin.code.style=official javaVersion=11 kotlinVersion=1.9.10 -version=9.5-SNAPSHOT +version=9.5