diff --git a/README.md b/README.md index b63ab78..b58d56b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Raspberry Casket A fast and small open source Pretracker replayer -## Raspberry Casket Player V1.0 (26-Dec-2022) +## Raspberry Casket Player V1.1 (28-Dec-2022) Provided by Chris 'platon42' Hodges <chrisly@platon42.de> @@ -83,12 +83,15 @@ The original code compressed with *Blueberry's* Shrinkler goes from 18052 bytes down to 9023 bytes. Raspberry Casket, depending on the features compiled in, is about -6374 bytes and goes down to ~4410 bytes (in isolation). +6216 bytes and goes down to ~4348 bytes (in isolation). So this means that the optimization is not just "on the outside". ### Timing +Sample generation is a bit faster (I guess around 10-15%), but most of the time is spent on muls operations, so this is the limiting factor. +Raspberry Casket is about twice as fast as the old replayer for playback. + Unfortunately, the replayer is still pretty slow and has high jitter compared to other standard music replayers. @@ -103,24 +106,30 @@ solve this problem. ### Known issues - Behaviour for undefined volume slides with both up- and down nibble specified is different (e.g. A9A, hi Rapture!). Don't do that. -- Don't use loops with odd lengths and offsets. +- Don't use loops with odd lengths and offsets (even if Pretracker allows this when dragging the loop points). - Don't stop the music with F00 and use a note delay (EDx) in the same line. - Don't try to play music with no waves, instruments or patterns. -- Shinobi seemed to have used an early beta version of Pretracker where it was possible to specify a Subloop Wait of 0. That's illegal. +- Pattern breaks with target row >= $7f will be ignored. +- Shinobi seemed to have used an early beta version of Pretracker where it was possible to specify a Subloop Wait of 0. That's illegal and unsupported. +- Pattern break (Dxx) + Song pos (Bxx) on the same line does not work in original Pretracker & Player: New Dxx position is ignored. + There is code to enable it in the player, so you could in theory make backwards running tracks like in Protracker. + But this doesn't make sense as long as the tracker itself does not support it. ## Changelog -### V1.1 (unreleased) -- Optimized base displacement by reordering variables -- Further optimized ADSR code -- Optimized wave loop code -- Bake in this strange vibrato speed multiplication to precalculated vibrato value (where possible) -- Various small optimizations -- Store instrument number * 4 on loading to avoid using two adds every frame +### V1.1 (28-Dec-22) +- Optimized base displacement by reordering variables. +- Further optimized ADSR code. +- Optimized wave loop code. +- Bake in this strange vibrato speed multiplication to precalculated vibrato value (where possible). +- Various small optimizations. +- Store instrument number * 4 on loading to avoid using two adds every frame. - Optimized speed/shuffle code. Idea of using xor turned out to make things too complicated for pattern breaks/jumps. - Rearranged code for more short branches. - Optimized track delay code further. -- Drop-in replacement code size: 6304 bytes. +- Optimized pattern / song advance code. +- Maximum jitter now about one rasterline less, average about 0.5 rasterlines less (measurements, your mileage may vary). +- Drop-in replacement code size: 6228 bytes. ### V1.0 (26-Dec-22) diff --git a/binaries/raspberry_casket.bin b/binaries/raspberry_casket.bin index 25808b1..2f641c2 100644 Binary files a/binaries/raspberry_casket.bin and b/binaries/raspberry_casket.bin differ diff --git a/binaries/readme.txt b/binaries/readme.txt index cfc7dc3..6ae6124 100644 --- a/binaries/readme.txt +++ b/binaries/readme.txt @@ -1,5 +1,5 @@ This file "raspberry_casket.bin" can replace the original "player.bin" provided that you used the 16 KB + 16 KB allocation for player -variables as in the original player. +variables as in the original player (you need only about 2 + 12 KB). -It has been assembled from src/drop_in_replacement.asm (version V1.0). \ No newline at end of file +It has been assembled from src/drop_in_replacement.asm (version V1.1). \ No newline at end of file diff --git a/example/plastic_duff.exe b/example/plastic_duff.exe index 5e0e469..d3dd32c 100644 Binary files a/example/plastic_duff.exe and b/example/plastic_duff.exe differ diff --git a/example/readme.txt b/example/readme.txt index b3abecd..06cde5f 100644 --- a/example/readme.txt +++ b/example/readme.txt @@ -3,7 +3,7 @@ It features Pink's song Plastic Dove (from the Coda Intro). Even though it features the full replayer code and tune, the replayer including my demo framework and the song only -takes a mere 6560 bytes (shrinkled), which is even smaller +takes a mere 6448 bytes (shrinkled), which is even smaller than the new streaming replayer used in Pink's new streaming player demonstrated (closed source) in @@ -11,7 +11,7 @@ https://www.pouet.net/prod.php?which=91551 which is 6860 bytes. -The Raspberry Casket replayer takes an average of 14 +The Raspberry Casket replayer takes an average of 13.5 rasterlines while Pink's new streaming player takes only about 10 rasterlines. diff --git a/src/raspberry_casket.asm b/src/raspberry_casket.asm index 92f389f..4fb8a77 100755 --- a/src/raspberry_casket.asm +++ b/src/raspberry_casket.asm @@ -1,6 +1,6 @@ ;-------------------------------------------------------------------- -; Raspberry Casket Player V1.1beta (28-Dec-2022) -; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +; Raspberry Casket Player V1.1 (28-Dec-2022) +; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; ; Provided by Chris 'platon42' Hodges <chrisly@platon42.de> ; @@ -87,15 +87,22 @@ ; 18052 bytes down to 9023 bytes. ; ; Raspberry Casket, depending on the features compiled in, is about -; 6374 bytes and goes down to ~4410 bytes (in isolation). +; 6216 bytes and goes down to ~4348 bytes (in isolation). ; ; So this means that the optimization is not just "on the outside". ; ; Timing ; ~~~~~~ +; Sample generation is a bit faster (I guess around 10-15%), but most +; of the time is spent on muls operations, so this is the limiting +; factor. +; +; Raspberry Casket is about twice as fast as the old replayer for +; playback. +; ; Unfortunately, the replayer is still pretty slow and has high ; jitter compared to other standard music replayers. -; This means it may take up to 33 raster lines (14-19 on average) +; This means it may take up to 32 raster lines (13-18 on average) ; which is significant more than a standard Protracker replayer ; (the original one could take about 60 raster lines worst case and ; about 34 on average!). @@ -639,7 +646,7 @@ pre_FuncTable dc.l pre_PlayerInit-pre_FuncTable dc.l pre_PlayerTick-pre_FuncTable ENDC - ;dc.b '$VER: Raspberry Casket 1.0',0 + ;dc.b '$VER: Raspberry Casket 1.1',0 ;even IFNE PRETRACKER_COPPER_OUTPUT @@ -2906,29 +2913,66 @@ pre_PlayerTick: ; end of pattern loop ; ---------------------------------------- -; pattern advancing FIXME try to figure out all the cases +; Pattern advancing and pattern break and jump handling. Song looping and song-end detection. .pattern_advancing subq.b #1,pv_pat_line_ticks_b(a4) bne .no_pattern_advance ; clear note delay info - moveq.l #0,d1 + moveq.l #0,d0 REPT NUM_CHANNELS - move.b d1,pv_channeldata+pcd_note_delay_b+REPTN*pcd_SIZEOF(a4) + move.b d0,pv_channeldata+pcd_note_delay_b+REPTN*pcd_SIZEOF(a4) ENDR - move.b sv_num_steps_b(a6),d1 - subq.b #1,d1 - move.b pv_next_pat_row_b(a4),d0 - blt.s .no_pattern_break - cmp.b d0,d1 - bgt.s .has_legal_break_pos - move.b d1,d0 ; limit to last step -.has_legal_break_pos - move.b d0,pv_pat_curr_row_b(a4) + move.b sv_num_steps_b(a6),d1 ; number of steps in pattern - move.w sv_curr_pat_pos_w(a6),d3 ; go to next pattern pos - addq.w #1,d3 + move.b pv_pat_curr_row_b(a4),d0 + addq.b #1,d0 ; normal step increment + + move.w sv_curr_pat_pos_w(a6),d3 ; current song position + + move.b pv_next_pat_row_b(a4),d2 ; $ff means no pattern break + bmi.s .no_pattern_break + st pv_next_pat_row_b(a4) ; processed break, set to $ff + move.b d2,d0 + IFNE 0 ; PRETRACKER_BUGFIX_CODE ; currently disabled to keep old behaviour + moveq.l #0,d2 ; clear mask + ENDC + cmp.b d1,d0 + blo.s .has_legal_break_pos + move.b d1,d0 ; limit to last step + subq.b #1,d0 + bra.s .has_legal_break_pos + +.no_pattern_break + cmp.b d1,d0 + blo.s .pattern_end_not_reached + moveq.l #0,d0 + IFNE PRETRACKER_PARANOIA_MODE + move.b pv_loop_pattern_b(a4),d0 ; keep same pattern rolling? + bne.s .pattern_end_not_reached + ENDC +.has_legal_break_pos + addq.w #1,d3 ; pattern break will increment song pos -- if there is no new pattern position + +.pattern_end_not_reached + move.b pv_next_pat_pos_b(a4),d4 + bmi.s .no_new_position ; has a new pattern position + st pv_next_pat_pos_b(a4) + IFNE PRETRACKER_SONG_END_DETECTION + cmp.b d4,d3 + bgt.s .no_backjump + st pv_songend_detected_b(a4) ; detect jumping back +.no_backjump + ENDC + move.b d4,d3 ; load new position + IFNE 0 ; PRETRACKER_BUGFIX_CODE ; currently disabled to keep old behaviour + not.b d2 + and.b d2,d0 ; if we had NO pattern break, we will clear d0 + ELSE + moveq.l #0,d0 + ENDC +.no_new_position cmp.w sv_pat_pos_len_w(a6),d3 blt.s .no_restart_song @@ -2937,87 +2981,15 @@ pre_PlayerTick: st pv_songend_detected_b(a4) ENDC .no_restart_song - move.w d3,sv_curr_pat_pos_w(a6) - - st pv_next_pat_row_b(a4) ; processed break, set to $ff - - move.b pv_next_pat_pos_b(a4),d3 - bge.s .has_new_position ; has a new pattern position together with a break - move.b d0,d2 ; backup pv_pat_curr_row_b - cmp.b d0,d1 - ble.s .end_of_pattern_reached - bra.s .done_pat_advance - -.no_pattern_break - move.b pv_next_pat_pos_b(a4),d3 - bge.s .has_new_position - move.b pv_pat_curr_row_b(a4),d0 - move.b d0,d2 - cmp.b d1,d2 - blt.s .advancetonextpos - -.end_of_pattern_reached - clr.b pv_pat_curr_row_b(a4) - IFNE PRETRACKER_PARANOIA_MODE - move.b pv_loop_pattern_b(a4),d0 ; keep same pattern rolling? - bne .done_pat_advance - ENDC - bra.s .advance_song_pos - -.advancetonextpos - addq.b #1,d2 - move.b sv_num_steps_b(a6),d1 - addq.b #1,d0 - cmp.b d2,d1 - bgt.s .dont_go_to_top_row - moveq.l #0,d0 -.dont_go_to_top_row move.b d0,pv_pat_curr_row_b(a4) - bra.s .done_pat_advance - -.has_new_position - clr.b pv_pat_curr_row_b(a4) - IFNE PRETRACKER_PARANOIA_MODE - move.b pv_loop_pattern_b(a4),d0 - bne .no_restart_song_after_jump - ENDC - - cmp.w sv_pat_pos_len_w(a6),d3 - blt.s .set_song_pos2 - move.w sv_pat_restart_pos_w(a6),d3 - IFNE PRETRACKER_SONG_END_DETECTION - st pv_songend_detected_b(a4) - ENDC -.set_song_pos2 move.w d3,sv_curr_pat_pos_w(a6) - ; enters with d0 = 0 -.no_restart_song_after_jump - st pv_next_pat_pos_b(a4) ; processed jump, set to $ff - subq.b #1,d2 - bhi.s .done_pat_advance - clr.b pv_pat_curr_row_b(a4) - tst.b d0 - bne.s .done_pat_advance - -.advance_song_pos - move.w sv_curr_pat_pos_w(a6),d0 - addq.w #1,d0 - cmp.w sv_pat_pos_len_w(a6),d0 - blt.s .set_song_pos - move.w sv_pat_restart_pos_w(a6),d0 - IFNE PRETRACKER_SONG_END_DETECTION - st pv_songend_detected_b(a4) - ENDC -.set_song_pos - move.w d0,sv_curr_pat_pos_w(a6) -.done_pat_advance - move.b pv_pat_speed_even_b(a4),d0 - btst #0,pv_pat_curr_row_b(a4) - beq.s .set_speed_even - move.b pv_pat_speed_odd_b(a4),d0 + move.b pv_pat_speed_even_b(a4),d1 + lsr.b #1,d0 + bcc.s .set_speed_even + move.b pv_pat_speed_odd_b(a4),d1 .set_speed_even - move.b d0,pv_pat_line_ticks_b(a4) + move.b d1,pv_pat_line_ticks_b(a4) .no_pattern_advance ; ----------------------------------------