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" {
#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
}

View File

@ -127,8 +127,8 @@ void transfer_textures()
uint32_t offset = texture_memory_alloc.texture.start + 0;
void * dst = reinterpret_cast<void *>(&ta_fifo_texture_memory[offset / 4]);
void * src = reinterpret_cast<void *>(&_binary_font_tandy1k_tandy1k_data_start);
int size = reinterpret_cast<int>(&_binary_font_tandy1k_tandy1k_data_size);
void * src = reinterpret_cast<void *>(&_binary_font_tandy1k_data_start);
int size = reinterpret_cast<int>(&_binary_font_tandy1k_data_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));
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 (*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()
{
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<rekey_note>(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<play_note>(state.line_index);
} else if (effect_tick) {
// 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;
}
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_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();
}

View File

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

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 "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

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 "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, &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,
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;
}

View File

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

View File

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