From cdeb839355f8e051b232c876ff08d95821d258ab Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Tue, 24 Jun 2025 13:14:35 -0500 Subject: [PATCH] refactor: decode pattern once during initialization --- font/tandy1k.data.h | 6 +-- src/graphics.cpp | 4 +- src/interpreter.cpp | 115 +++++++++++++++++++++++--------------------- src/interpreter.hpp | 8 ++- src/main.cpp | 22 ++++++++- src/malloc.c | 33 +++++++++++++ src/malloc.h | 14 ++++++ src/sound.cpp | 47 +----------------- src/sound.hpp | 2 +- src/xm.c | 78 ++++++++++++++++++++++++++++-- src/xm.h | 7 ++- xm_player.mk | 3 +- 12 files changed, 219 insertions(+), 120 deletions(-) create mode 100644 src/malloc.c create mode 100644 src/malloc.h diff --git a/font/tandy1k.data.h b/font/tandy1k.data.h index 332214f..b14d986 100644 --- a/font/tandy1k.data.h +++ b/font/tandy1k.data.h @@ -6,9 +6,9 @@ extern "C" { #endif -extern uint32_t _binary_font_tandy1k_tandy1k_data_start __asm("_binary_font_tandy1k_tandy1k_data_start"); -extern uint32_t _binary_font_tandy1k_tandy1k_data_end __asm("_binary_font_tandy1k_tandy1k_data_end"); -extern uint32_t _binary_font_tandy1k_tandy1k_data_size __asm("_binary_font_tandy1k_tandy1k_data_size"); +extern uint32_t _binary_font_tandy1k_data_start __asm("_binary_font_tandy1k_data_start"); +extern uint32_t _binary_font_tandy1k_data_end __asm("_binary_font_tandy1k_data_end"); +extern uint32_t _binary_font_tandy1k_data_size __asm("_binary_font_tandy1k_data_size"); #ifdef __cplusplus } diff --git a/src/graphics.cpp b/src/graphics.cpp index 52856f3..bc4a4c7 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -127,8 +127,8 @@ void transfer_textures() uint32_t offset = texture_memory_alloc.texture.start + 0; void * dst = reinterpret_cast(&ta_fifo_texture_memory[offset / 4]); - void * src = reinterpret_cast(&_binary_font_tandy1k_tandy1k_data_start); - int size = reinterpret_cast(&_binary_font_tandy1k_tandy1k_data_size); + void * src = reinterpret_cast(&_binary_font_tandy1k_data_start); + int size = reinterpret_cast(&_binary_font_tandy1k_data_size); transfer_ta_fifo_texture_memory_32byte(dst, src, size); } diff --git a/src/interpreter.cpp b/src/interpreter.cpp index 71787f7..9057c25 100644 --- a/src/interpreter.cpp +++ b/src/interpreter.cpp @@ -177,87 +177,94 @@ void rekey_note(int ch, xm_pattern_format_t * pf) } } -int parse_pattern_line(xm_pattern_header_t * pattern_header, int note_offset, void (*func)(int, xm_pattern_format_t*)) +static inline void next_pattern() { - uint8_t * pattern = (uint8_t *)(((int)pattern_header) + s32(&pattern_header->pattern_header_length)); - - for (int i = 0; i < state.cache.number_of_channels; i++) { - int p = pattern[note_offset]; - if (p & 0x80) { - note_offset += 1; - xm_pattern_format_t pf = {}; - if (p & (1 << 0)) - pf.note = pattern[note_offset++]; - if (p & (1 << 1)) - pf.instrument = pattern[note_offset++]; - if (p & (1 << 2)) - pf.volume_column_byte = pattern[note_offset++]; - if (p & (1 << 3)) - pf.effect_type = pattern[note_offset++]; - if (p & (1 << 4)) - pf.effect_parameter = pattern[note_offset++]; - func(i, &pf); - } else { - xm_pattern_format_t * pf = (xm_pattern_format_t *)&pattern[note_offset]; - func(i, pf); - note_offset += 5; - } - } - return note_offset; -} - -static inline void next_pattern(int pattern_break) -{ - state.line_index = 0; - state.next_note_offset = 0; + if (state.pattern_break >= 0) + state.next_line_index = state.pattern_break; + else + state.next_line_index = 0; state.pattern_break = -1; state.pattern_order_table_index += 1; - printf("pattern_order_table_index: %d\n", state.pattern_order_table_index); - if (state.pattern_order_table_index >= state.cache.song_length) + if (state.pattern_order_table_index >= state.xm.song_length) state.pattern_order_table_index = 0; + printf("pattern_order_table_index: %d\n", state.pattern_order_table_index); + state.pattern_index = state.xm.header->pattern_order_table[state.pattern_order_table_index]; + + printf("note_count: %d\n", state.xm.pattern_note_count[state.pattern_index]); +} + +template +void execute_line(int line_index) +{ + int line_pattern_index = line_index * state.xm.number_of_channels; + for (int ch = 0; ch < state.xm.number_of_channels; ch++) { + xm_pattern_format_t * pattern = state.xm.pattern[state.pattern_index]; + F(ch, &pattern[line_pattern_index + ch]); + } } void interrupt() { - xm_pattern_header_t * pattern_header = state.xm.pattern_header[state.pattern_index]; - int pattern_data_size = s16(&pattern_header->packed_pattern_data_size); - bool keyoff_tick = (state.tick + 1) % (state.ticks_per_line * 2) == 0; bool note_tick = state.tick % (state.ticks_per_line * 2) == 0; bool effect_tick = (state.tick & 1) == 0; bool pattern_break_tick = (state.tick % (state.ticks_per_line * 2)) == (state.ticks_per_line * 2 - 1); + if (keyoff_tick) { // execute keyoffs - parse_pattern_line(pattern_header, state.next_note_offset, rekey_note); - wait(); aica_sound.channel[0].KYONEX(1); + execute_line(state.next_line_index); } if (state.pattern_break >= 0 && pattern_break_tick) { printf("pattern_break\n"); - next_pattern(-1); + next_pattern(); } if (note_tick) { - state.note_offset = state.next_note_offset; - //state.next_note_offset = parse_pattern_line(pattern_header, state.note_offset, play_debug_note); - state.next_note_offset = parse_pattern_line(pattern_header, state.note_offset, play_note); - state.line_index += 1; - wait(); aica_sound.channel[0].KYONEX(1); - } - if (effect_tick && !note_tick) { - // execute effects - parse_pattern_line(pattern_header, state.note_offset, play_note_effect); - wait(); aica_sound.channel[0].KYONEX(1); - } + // execute notes + state.line_index = state.next_line_index; + state.next_line_index += 1; - if (state.next_note_offset >= pattern_data_size && pattern_break_tick) { - printf("pattern_data_size\n"); - next_pattern(-1); + execute_line(state.line_index); + } else if (effect_tick) { + // execute effects + execute_line(state.line_index); + } + wait(); aica_sound.channel[0].KYONEX(1); + + int note_index = state.next_line_index * state.xm.number_of_channels; + bool end_of_pattern = note_index >= state.xm.pattern_note_count[state.pattern_index]; + if (end_of_pattern && pattern_break_tick) { + printf("end_of_pattern\n"); + next_pattern(); } state.tick += 1; } +void init() +{ + // 195 = 1ms + // 2500 / bpm milliseconds + + int default_bpm = s16(&state.xm.header->default_bpm); + int default_tempo = s16(&state.xm.header->default_tempo); + int tick_rate = 195.32 * 2500 / default_bpm; + + printf("default_bpm %d\n", default_bpm); + printf("default_tempo %d\n", default_tempo); + printf("tick_rate %d\n", tick_rate); + + state.tick_rate = tick_rate; + state.ticks_per_line = default_tempo; + state.tick = 0; + state.line_index = 0; + state.pattern_order_table_index = -1; + next_pattern(); + + printf("tick_rate %d\n", state.tick_rate); +} + } diff --git a/src/interpreter.hpp b/src/interpreter.hpp index 745dd7d..61efb82 100644 --- a/src/interpreter.hpp +++ b/src/interpreter.hpp @@ -17,18 +17,16 @@ struct interpreter_state { int pattern_order_table_index; int pattern_break; int pattern_index; - int line_index; // within the current pattern (for debugging) - int note_offset; // within the current pattern - int next_note_offset; + int line_index; + int next_line_index; // within the current pattern struct xm_state xm; - struct xm_state_cache cache; - struct channel_state channel[max_channels]; }; extern struct interpreter_state state; void interrupt(); +void init(); } diff --git a/src/main.cpp b/src/main.cpp index 95933d8..6915546 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,6 +11,9 @@ #include "interpreter.hpp" #include "sound.hpp" +#include "xm/milkypack01.xm.h" +#include "xm.h" + void vbr100() { serial::string("vbr100\n"); @@ -171,11 +174,28 @@ void input_update() last_b = b; } +void load_xm() +{ + using namespace interpreter; + + int buf = (int)&_binary_xm_milkypack01_xm_start; + + static uint8_t __attribute__((aligned(32))) sample_data[1024 * 1024 * 2]; + const int sample_data_length = (sizeof (sample_data)); + + int sample_data_ix = xm_init(&state.xm, + buf, + sample_data, + sample_data_length); + interpreter::init(); + sound_init(sample_data, sample_data_ix, state.tick_rate); +} + void main() { serial::init(0); - sound_init(); + load_xm(); graphics_init(); interrupt_init(); //channel_sandbox_defaults(); diff --git a/src/malloc.c b/src/malloc.c new file mode 100644 index 0000000..e9cf48c --- /dev/null +++ b/src/malloc.c @@ -0,0 +1,33 @@ +#include "assert.h" +#include "malloc.h" + +struct arena { + uint8_t * mem; + uint32_t size; + uint32_t ix; +}; + +static uint8_t arena_mem[0x100000]; + +static struct arena arena = { + .mem = arena_mem, + .size = (sizeof (arena_mem)), + .ix = 0, +}; + +void malloc_arena_reset() +{ + arena.ix = 0; +} + +void * malloc_arena(uint32_t size) +{ + if (size == 0) + return nullptr; + + assert((arena.ix & (~3)) == arena.ix); + void * ptr = &arena.mem[arena.ix]; + size = (size + 3) & (~3); + arena.ix += size; + return ptr; +} diff --git a/src/malloc.h b/src/malloc.h new file mode 100644 index 0000000..f0a2a6d --- /dev/null +++ b/src/malloc.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void malloc_arena_reset(); +void * malloc_arena(uint32_t size); + +#ifdef __cplusplus +} +#endif diff --git a/src/sound.cpp b/src/sound.cpp index 3a56d9f..71ea7ee 100644 --- a/src/sound.cpp +++ b/src/sound.cpp @@ -17,10 +17,6 @@ #include "assert.h" -#include "xm/xm.h" -#include "xm/milkypack01.xm.h" - -#include "xm.h" #include "interpreter.hpp" void g2_aica_dma(uint32_t g2_address, uint32_t system_address, int length) @@ -73,20 +69,8 @@ void writeback(void const * const buf, uint32_t size) static uint8_t __attribute__((aligned(32))) zero[0x28c0] = {}; -void sound_init() +void sound_init(uint8_t * sample_data, int sample_data_ix, int tick_rate) { - using namespace interpreter; - - int buf = (int)&_binary_xm_milkypack01_xm_start; - - static uint8_t __attribute__((aligned(32))) sample_data[1024 * 1024 * 2]; - const int sample_data_length = (sizeof (sample_data)); - int sample_data_ix = xm_init(&state.xm, - &state.cache, - buf, - sample_data, - sample_data_length); - wait(); aica_sound.common.vreg_armrst = aica::vreg_armrst::ARMRST(1); wait(); aica_sound.common.dmea0_mrwinh = aica::dmea0_mrwinh::MRWINH(0b0111); system.ISTNRM = istnrm::end_of_dma_aica_dma; @@ -99,14 +83,6 @@ void sound_init() g2_aica_dma((uint32_t)0x00703000, (int)zero, 0x15e0); g2_aica_dma_wait_complete(); - printf("i[0] start %d size %d\n", - state.xm.sample_data_offset[0], - s32(&state.xm.sample_header[0]->sample_length)); - - printf("i[1] start %d size %d\n", - state.xm.sample_data_offset[1], - s32(&state.xm.sample_header[1]->sample_length)); - for (int i = 0; i < 16; i++) { serial::hexlify(&sample_data[i * 16], 16); } @@ -175,27 +151,8 @@ void sound_init() | aica::mono_mem8mb_dac18b_ver_mvol::MVOL(0xc) // volume ; - // 195 = 1ms - // 2500 / bpm milliseconds - printf("default_bpm %d\n", s16(&state.xm.header->default_bpm)); - printf("default_tempo %d\n", s16(&state.xm.header->default_tempo)); - - state.tick_rate = 195.32 * 2500 / s16(&state.xm.header->default_bpm); - state.ticks_per_line = s16(&state.xm.header->default_tempo); - state.tick = 0; - state.pattern_break = -1; - state.pattern_order_table_index = 0; - state.pattern_index = state.xm.header->pattern_order_table[state.pattern_order_table_index]; - state.line_index = 0; - state.note_offset = 0; - state.next_note_offset = 0; - - printf("tick_rate %d\n", state.tick_rate); - - printf("pattern %d\n", state.pattern_index); - sh7091.TMU.TSTR = 0; // stop all timers - sh7091.TMU.TCOR0 = state.tick_rate / 2; + sh7091.TMU.TCOR0 = tick_rate / 2; sh7091.TMU.TOCR = tmu::tocr::tcoe::tclk_is_external_clock_or_input_capture; sh7091.TMU.TCR0 = tmu::tcr0::UNIE diff --git a/src/sound.hpp b/src/sound.hpp index c8a6def..f03ab74 100644 --- a/src/sound.hpp +++ b/src/sound.hpp @@ -13,4 +13,4 @@ static inline void wait() }; } -void sound_init(); +void sound_init(uint8_t * sample_data, int sample_data_ix, int tick_rate); diff --git a/src/xm.c b/src/xm.c index dd2c3c2..f535dab 100644 --- a/src/xm.c +++ b/src/xm.c @@ -1,6 +1,7 @@ #include "xm/xm.h" #include "printf/printf.h" #include "xm.h" +#include "malloc.h" static int xm_unpack_sample(int buf, int offset, @@ -93,7 +94,6 @@ static int xm_samples_init(xm_state_t * xm, sample_header[i], sample_data, *sample_data_ix); - printf("%d\n", sample_data_length); assert(*sample_data_ix <= sample_data_length); } offset += sample_length; @@ -101,8 +101,71 @@ static int xm_samples_init(xm_state_t * xm, return offset; } +static inline xm_pattern_format_t parse_pattern_line(uint8_t * pattern, int * note_offset) +{ + int offset = *note_offset; + + int p = pattern[offset]; + if (p & 0x80) { + offset += 1; + xm_pattern_format_t pf = {}; + if (p & (1 << 0)) + pf.note = pattern[offset++]; + if (p & (1 << 1)) + pf.instrument = pattern[offset++]; + if (p & (1 << 2)) + pf.volume_column_byte = pattern[offset++]; + if (p & (1 << 3)) + pf.effect_type = pattern[offset++]; + if (p & (1 << 4)) + pf.effect_parameter = pattern[offset++]; + *note_offset = offset; + return pf; + } else { + xm_pattern_format_t * pf = (xm_pattern_format_t *)&pattern[offset]; + offset += 5; + *note_offset = offset; + return *pf; + } +} + +static inline int count_pattern_notes(uint8_t * pattern, int pattern_data_size) +{ + int note_offset = 0; + int note_count = 0; + + while (note_offset < pattern_data_size) { + parse_pattern_line(pattern, ¬e_offset); + note_count += 1; + } + assert(note_offset == pattern_data_size); + + return note_count; +} + +void xm_unpack_pattern(xm_state_t * xm, + int pattern_index) +{ + xm_pattern_header_t * pattern_header = xm->pattern_header[pattern_index]; + uint8_t * pattern = (uint8_t *)(((int)pattern_header) + s32(&pattern_header->pattern_header_length)); + + int pattern_data_size = s16(&pattern_header->packed_pattern_data_size); + + int note_count = count_pattern_notes(pattern, pattern_data_size); + + xm_pattern_format_t * pf = (xm_pattern_format_t *)malloc_arena((sizeof (xm_pattern_format_t)) * note_count); + + xm->pattern[pattern_index] = pf; + xm->pattern_note_count[pattern_index] = note_count; + + int note_offset = 0; + for (int i = 0; i < note_count; i++) { + pf[i] = parse_pattern_line(pattern, ¬e_offset); + } + assert(note_offset == pattern_data_size); +} + int xm_init(xm_state_t * xm, - xm_state_cache_t * cache, int buf, uint8_t * sample_data, int sample_data_length) @@ -142,12 +205,19 @@ int xm_init(xm_state_t * xm, printf("end_of_instruments: %d\n", offset); int number_of_channels = s16(&xm->header->number_of_channels); - cache->number_of_channels = number_of_channels; + xm->number_of_channels = number_of_channels; printf("number_of_channels: %d\n", number_of_channels); int song_length = s16(&xm->header->song_length); - cache->song_length = song_length; + xm->song_length = song_length; printf("song_length: %d\n", song_length); + // reset arena + malloc_arena_reset(); + + for (int pattern_index = 0; pattern_index < number_of_patterns; pattern_index++) { + xm_unpack_pattern(xm, pattern_index); + } + return sample_data_ix; } diff --git a/src/xm.h b/src/xm.h index 383da75..4ef42ea 100644 --- a/src/xm.h +++ b/src/xm.h @@ -15,15 +15,14 @@ typedef struct xm_state { xm_instrument_header_t * instrument_header[xm_max_instruments]; xm_sample_header_t * sample_header[xm_max_instruments]; // array int sample_data_offset[xm_max_instruments]; -} xm_state_t; -typedef struct xm_state_cache { int number_of_channels; int song_length; -} xm_state_cache_t; + xm_pattern_format_t * pattern[xm_max_patterns]; + int pattern_note_count[xm_max_patterns]; +} xm_state_t; int xm_init(xm_state_t * xm, - xm_state_cache_t * cache, int buf, uint8_t * sample_data, int sample_data_length); diff --git a/xm_player.mk b/xm_player.mk index 72cf45a..25153ee 100644 --- a/xm_player.mk +++ b/xm_player.mk @@ -20,7 +20,8 @@ XM_PLAYER_OBJ = \ src/interpreter.o \ src/main.o \ src/sound.o \ - src/xm.o + src/xm.o \ + src/malloc.o xm_player.elf: LDSCRIPT = $(LIB)/main.lds xm_player.elf: $(START_OBJ) $(XM_PLAYER_OBJ) $(TEXTURE_OBJ) $(XM_OBJ) $(LIBGCC)