refactor: decode pattern once during initialization
This commit is contained in:
parent
9db24882b8
commit
cdeb839355
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
}
|
||||
|
22
src/main.cpp
22
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();
|
||||
|
33
src/malloc.c
Normal file
33
src/malloc.c
Normal 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
14
src/malloc.h
Normal 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
|
@ -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
|
||||
|
@ -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);
|
||||
|
78
src/xm.c
78
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;
|
||||
}
|
||||
|
7
src/xm.h
7
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);
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user