switch to AICA timing

This commit is contained in:
Zack Buhman 2025-06-25 15:47:15 -05:00
parent 01eaa32e37
commit 2066923f10
4 changed files with 78 additions and 29 deletions

View File

@ -113,12 +113,13 @@ void _play_note(int ch, const xm_pattern_format_t * pf)
wait(); aica_sound.channel[ch].PLFOS(0);
}
wait(); aica_sound.channel[ch].KYONB(1);
state.channel[ch].keyon = 1;
wait(); aica_sound.channel[ch].KYONB(0);
}
void play_note_effect(int ch, const xm_pattern_format_t * pf)
{
int effect_tick = (state.tick / 2) % state.ticks_per_line;
int effect_tick = state.tick % state.ticks_per_line;
switch (pf->effect_type) {
case 0x04: // 4 vibrato
@ -207,51 +208,75 @@ void execute_line(int line_index)
void interrupt()
{
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);
state.interrupt_clock += 1;
// execute keyons
for (int ch = 0; ch < 64; ch++) {
/*
if (state.channel[ch].keyon == 2) {
int sgc = aica_sound.common.SGC();
if (sgc == 3) {
wait(); aica_sound.channel[ch].KYONB(1);
state.channel[ch].keyon = 0;
}
break;
} else if (state.channel[ch].keyon == 1) {
wait(); aica_sound.common.afsel_mslc_mobuf
= aica::afsel_mslc_mobuf::AFSEL(0)
| aica::afsel_mslc_mobuf::MSLC(ch);
state.channel[ch].keyon = 2;
break;
}
*/
if (state.channel[ch].keyon != 0) {
wait(); aica_sound.channel[ch].KYONB(1);
state.channel[ch].keyon = 0;
}
}
wait(); aica_sound.channel[0].KYONEX(1);
if (keyoff_tick) {
// execute keyoffs
execute_line<rekey_note>(state.next_line_index);
}
if (state.pattern_break >= 0 && pattern_break_tick) {
printf("pattern_break\n");
next_pattern();
if ((state.interrupt_clock % state.tick_rate) != 0) {
return;
}
int tick = state.tick % state.ticks_per_line;
bool note_tick = tick == 0;
if (note_tick) {
// execute notes
state.line_index = state.next_line_index;
state.next_line_index += 1;
execute_line<play_note>(state.line_index);
} else if (effect_tick) {
} else {
// execute effects
execute_line<play_note_effect>(state.line_index);
}
wait(); aica_sound.channel[0].KYONEX(1);
bool pattern_break_tick = tick == (state.ticks_per_line - 1);
if (pattern_break_tick) {
if (state.pattern_break >= 0) {
printf("pattern_break\n");
next_pattern();
}
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) {
if (end_of_pattern) {
printf("end_of_pattern\n");
next_pattern();
}
}
state.tick += 1;
}
void init()
void init(float clock_multiplier)
{
// 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;
int tick_rate = clock_multiplier * 2500 / default_bpm;
printf("default_bpm %d\n", default_bpm);
printf("default_tempo %d\n", default_tempo);

View File

@ -7,10 +7,12 @@ namespace interpreter {
constexpr int max_channels = 64;
struct channel_state {
int instrument;
uint8_t instrument;
uint8_t keyon;
};
struct interpreter_state {
int interrupt_clock;
int tick_rate;
int ticks_per_line;
int tick;
@ -27,6 +29,6 @@ struct interpreter_state {
extern struct interpreter_state state;
void interrupt();
void init();
void init(float clock_multiplier);
}

View File

@ -6,6 +6,8 @@
#include "maple/maple_bus_commands.hpp"
#include "maple/maple_bus_ft0.hpp"
#include "aica/aica.hpp"
#include "interrupt.hpp"
#include "graphics.hpp"
#include "interpreter.hpp"
@ -43,6 +45,13 @@ void vbr600()
}
graphics_interrupt(istnrm);
} else if (sh7091.CCN.EXPEVT == 0 && sh7091.CCN.INTEVT == 0x360) { // AICA
wait(); aica_sound.common.mcire = (1 << 6); // interrupt timer A
wait(); aica_sound.common.tactl_tima =
aica::tactl_tima::TACTL(0) // increment once every sample
| aica::tactl_tima::TIMA(0xffff) // interrupt after 1 counts
;
interpreter::interrupt();
} else if (sh7091.CCN.EXPEVT == 0 && sh7091.CCN.INTEVT == 0x400) { // TMU0
sh7091.TMU.TCR0
= tmu::tcr0::UNIE
@ -174,7 +183,7 @@ void input_update()
last_b = b;
}
void load_xm()
void load_xm(float clock_multiplier)
{
using namespace interpreter;
@ -187,8 +196,9 @@ void load_xm()
buf,
sample_data,
sample_data_length);
interpreter::init();
interpreter::init(clock_multiplier);
sound_init(sample_data, sample_data_ix, state.tick_rate);
printf("tick_rate %d\n", state.tick_rate);
}
#include "memorymap.hpp"
@ -225,7 +235,9 @@ void main()
{
serial::init(0);
load_xm();
//const float tmu_clock_multiplier = 195.32;
const float aica_clock_multiplier = 44.1;
load_xm(aica_clock_multiplier);
graphics_init();
test_pattern();
@ -237,6 +249,8 @@ void main()
| istnrm::v_blank_in
| istnrm::end_of_transferring_opaque_list;
system.IML4EXT = istext::aica;
static uint8_t __attribute__((aligned(32))) ta_parameter_buf[1024 * 1024 * 1];
ta_parameter_writer writer = ta_parameter_writer(ta_parameter_buf, (sizeof (ta_parameter_buf)));

View File

@ -17,8 +17,6 @@
#include "assert.h"
#include "interpreter.hpp"
void g2_aica_dma(uint32_t g2_address, uint32_t system_address, int length)
{
using namespace dmac;
@ -151,6 +149,7 @@ void sound_init(uint8_t * sample_data, int sample_data_ix, int tick_rate)
| aica::mono_mem8mb_dac18b_ver_mvol::MVOL(0xc) // volume
;
/*
sh7091.TMU.TSTR = 0; // stop all timers
sh7091.TMU.TCOR0 = tick_rate / 2;
sh7091.TMU.TOCR = tmu::tocr::tcoe::tclk_is_external_clock_or_input_capture;
@ -161,4 +160,13 @@ void sound_init(uint8_t * sample_data, int sample_data_ix, int tick_rate)
sh7091.TMU.TSTR = tmu::tstr::str0::counter_start;
sh7091.INTC.IPRA = intc::ipra::TMU0(1);
*/
wait(); aica_sound.common.tactl_tima =
aica::tactl_tima::TACTL(0) // increment once every sample
| aica::tactl_tima::TIMA(0xffff) // interrupt after 1 counts
;
wait(); aica_sound.common.mcieb = (1 << 6); // interrupt timer A
wait(); aica_sound.common.mcire = (1 << 6); // interrupt timer A
}