refactor: decode pattern once during initialization

This commit is contained in:
Zack Buhman 2025-06-24 13:14:35 -05:00
parent 9db24882b8
commit cdeb839355
12 changed files with 219 additions and 120 deletions

View File

@ -6,9 +6,9 @@
extern "C" { extern "C" {
#endif #endif
extern uint32_t _binary_font_tandy1k_tandy1k_data_start __asm("_binary_font_tandy1k_tandy1k_data_start"); extern uint32_t _binary_font_tandy1k_data_start __asm("_binary_font_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_data_end __asm("_binary_font_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_size __asm("_binary_font_tandy1k_data_size");
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -127,8 +127,8 @@ void transfer_textures()
uint32_t offset = texture_memory_alloc.texture.start + 0; uint32_t offset = texture_memory_alloc.texture.start + 0;
void * dst = reinterpret_cast<void *>(&ta_fifo_texture_memory[offset / 4]); void * dst = reinterpret_cast<void *>(&ta_fifo_texture_memory[offset / 4]);
void * src = reinterpret_cast<void *>(&_binary_font_tandy1k_tandy1k_data_start); void * src = reinterpret_cast<void *>(&_binary_font_tandy1k_data_start);
int size = reinterpret_cast<int>(&_binary_font_tandy1k_tandy1k_data_size); int size = reinterpret_cast<int>(&_binary_font_tandy1k_data_size);
transfer_ta_fifo_texture_memory_32byte(dst, src, size); transfer_ta_fifo_texture_memory_32byte(dst, src, size);
} }

View File

@ -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)); if (state.pattern_break >= 0)
state.next_line_index = state.pattern_break;
for (int i = 0; i < state.cache.number_of_channels; i++) { else
int p = pattern[note_offset]; state.next_line_index = 0;
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;
state.pattern_break = -1; state.pattern_break = -1;
state.pattern_order_table_index += 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.xm.song_length)
if (state.pattern_order_table_index >= state.cache.song_length)
state.pattern_order_table_index = 0; 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]; 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 (*F)(int, xm_pattern_format_t *)>
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() 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 keyoff_tick = (state.tick + 1) % (state.ticks_per_line * 2) == 0;
bool note_tick = state.tick % (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 effect_tick = (state.tick & 1) == 0;
bool pattern_break_tick = (state.tick % (state.ticks_per_line * 2)) == (state.ticks_per_line * 2 - 1); bool pattern_break_tick = (state.tick % (state.ticks_per_line * 2)) == (state.ticks_per_line * 2 - 1);
if (keyoff_tick) { if (keyoff_tick) {
// execute keyoffs // execute keyoffs
parse_pattern_line(pattern_header, state.next_note_offset, rekey_note); execute_line<rekey_note>(state.next_line_index);
wait(); aica_sound.channel[0].KYONEX(1);
} }
if (state.pattern_break >= 0 && pattern_break_tick) { if (state.pattern_break >= 0 && pattern_break_tick) {
printf("pattern_break\n"); printf("pattern_break\n");
next_pattern(-1); next_pattern();
} }
if (note_tick) { if (note_tick) {
state.note_offset = state.next_note_offset; // execute notes
//state.next_note_offset = parse_pattern_line(pattern_header, state.note_offset, play_debug_note); state.line_index = state.next_line_index;
state.next_note_offset = parse_pattern_line(pattern_header, state.note_offset, play_note); state.next_line_index += 1;
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);
}
if (state.next_note_offset >= pattern_data_size && pattern_break_tick) { execute_line<play_note>(state.line_index);
printf("pattern_data_size\n"); } else if (effect_tick) {
next_pattern(-1); // execute effects
execute_line<play_note_effect>(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; 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);
}
} }

View File

@ -17,18 +17,16 @@ struct interpreter_state {
int pattern_order_table_index; int pattern_order_table_index;
int pattern_break; int pattern_break;
int pattern_index; int pattern_index;
int line_index; // within the current pattern (for debugging) int line_index;
int note_offset; // within the current pattern int next_line_index; // within the current pattern
int next_note_offset;
struct xm_state xm; struct xm_state xm;
struct xm_state_cache cache;
struct channel_state channel[max_channels]; struct channel_state channel[max_channels];
}; };
extern struct interpreter_state state; extern struct interpreter_state state;
void interrupt(); void interrupt();
void init();
} }

View File

@ -11,6 +11,9 @@
#include "interpreter.hpp" #include "interpreter.hpp"
#include "sound.hpp" #include "sound.hpp"
#include "xm/milkypack01.xm.h"
#include "xm.h"
void vbr100() void vbr100()
{ {
serial::string("vbr100\n"); serial::string("vbr100\n");
@ -171,11 +174,28 @@ void input_update()
last_b = b; 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() void main()
{ {
serial::init(0); serial::init(0);
sound_init(); load_xm();
graphics_init(); graphics_init();
interrupt_init(); interrupt_init();
//channel_sandbox_defaults(); //channel_sandbox_defaults();

33
src/malloc.c Normal file
View File

@ -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;
}

14
src/malloc.h Normal file
View File

@ -0,0 +1,14 @@
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
void malloc_arena_reset();
void * malloc_arena(uint32_t size);
#ifdef __cplusplus
}
#endif

View File

@ -17,10 +17,6 @@
#include "assert.h" #include "assert.h"
#include "xm/xm.h"
#include "xm/milkypack01.xm.h"
#include "xm.h"
#include "interpreter.hpp" #include "interpreter.hpp"
void g2_aica_dma(uint32_t g2_address, uint32_t system_address, int length) 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] = {}; 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.vreg_armrst = aica::vreg_armrst::ARMRST(1);
wait(); aica_sound.common.dmea0_mrwinh = aica::dmea0_mrwinh::MRWINH(0b0111); wait(); aica_sound.common.dmea0_mrwinh = aica::dmea0_mrwinh::MRWINH(0b0111);
system.ISTNRM = istnrm::end_of_dma_aica_dma; 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((uint32_t)0x00703000, (int)zero, 0x15e0);
g2_aica_dma_wait_complete(); 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++) { for (int i = 0; i < 16; i++) {
serial::hexlify(&sample_data[i * 16], 16); serial::hexlify(&sample_data[i * 16], 16);
} }
@ -175,27 +151,8 @@ void sound_init()
| aica::mono_mem8mb_dac18b_ver_mvol::MVOL(0xc) // volume | 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.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.TOCR = tmu::tocr::tcoe::tclk_is_external_clock_or_input_capture;
sh7091.TMU.TCR0 sh7091.TMU.TCR0
= tmu::tcr0::UNIE = tmu::tcr0::UNIE

View File

@ -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);

View File

@ -1,6 +1,7 @@
#include "xm/xm.h" #include "xm/xm.h"
#include "printf/printf.h" #include "printf/printf.h"
#include "xm.h" #include "xm.h"
#include "malloc.h"
static int xm_unpack_sample(int buf, static int xm_unpack_sample(int buf,
int offset, int offset,
@ -93,7 +94,6 @@ static int xm_samples_init(xm_state_t * xm,
sample_header[i], sample_header[i],
sample_data, sample_data,
*sample_data_ix); *sample_data_ix);
printf("%d\n", sample_data_length);
assert(*sample_data_ix <= sample_data_length); assert(*sample_data_ix <= sample_data_length);
} }
offset += sample_length; offset += sample_length;
@ -101,8 +101,71 @@ static int xm_samples_init(xm_state_t * xm,
return offset; 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, &note_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, &note_offset);
}
assert(note_offset == pattern_data_size);
}
int xm_init(xm_state_t * xm, int xm_init(xm_state_t * xm,
xm_state_cache_t * cache,
int buf, int buf,
uint8_t * sample_data, uint8_t * sample_data,
int sample_data_length) int sample_data_length)
@ -142,12 +205,19 @@ int xm_init(xm_state_t * xm,
printf("end_of_instruments: %d\n", offset); printf("end_of_instruments: %d\n", offset);
int number_of_channels = s16(&xm->header->number_of_channels); 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); printf("number_of_channels: %d\n", number_of_channels);
int song_length = s16(&xm->header->song_length); 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); 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; return sample_data_ix;
} }

View File

@ -15,15 +15,14 @@ typedef struct xm_state {
xm_instrument_header_t * instrument_header[xm_max_instruments]; xm_instrument_header_t * instrument_header[xm_max_instruments];
xm_sample_header_t * sample_header[xm_max_instruments]; // array xm_sample_header_t * sample_header[xm_max_instruments]; // array
int sample_data_offset[xm_max_instruments]; int sample_data_offset[xm_max_instruments];
} xm_state_t;
typedef struct xm_state_cache {
int number_of_channels; int number_of_channels;
int song_length; 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, int xm_init(xm_state_t * xm,
xm_state_cache_t * cache,
int buf, int buf,
uint8_t * sample_data, uint8_t * sample_data,
int sample_data_length); int sample_data_length);

View File

@ -20,7 +20,8 @@ XM_PLAYER_OBJ = \
src/interpreter.o \ src/interpreter.o \
src/main.o \ src/main.o \
src/sound.o \ src/sound.o \
src/xm.o src/xm.o \
src/malloc.o
xm_player.elf: LDSCRIPT = $(LIB)/main.lds xm_player.elf: LDSCRIPT = $(LIB)/main.lds
xm_player.elf: $(START_OBJ) $(XM_PLAYER_OBJ) $(TEXTURE_OBJ) $(XM_OBJ) $(LIBGCC) xm_player.elf: $(START_OBJ) $(XM_PLAYER_OBJ) $(TEXTURE_OBJ) $(XM_OBJ) $(LIBGCC)