Compare commits

...

1 Commits

Author SHA1 Message Date
fb0c47e95d aica_xm: non-working support for long samples 2025-06-22 18:38:43 -05:00
7 changed files with 277 additions and 40 deletions

View File

@ -18,6 +18,7 @@ AICA_XM_OBJ = \
xm/xmtest.xm.o \ xm/xmtest.xm.o \
xm/catch_this_rebel.xm.o \ xm/catch_this_rebel.xm.o \
xm/middle_c.xm.o \ xm/middle_c.xm.o \
xm/reign.xm.o \
font/tandy1k/tandy1k.data.o \ font/tandy1k/tandy1k.data.o \
holly/core.o \ holly/core.o \
holly/region_array.o \ holly/region_array.o \

View File

@ -42,6 +42,7 @@
#include "xm/test.xm.h" #include "xm/test.xm.h"
#include "xm/xmtest.xm.h" #include "xm/xmtest.xm.h"
#include "xm/catch_this_rebel.xm.h" #include "xm/catch_this_rebel.xm.h"
#include "xm/reign.xm.h"
#include "font/tandy1k/tandy1k.data.h" #include "font/tandy1k/tandy1k.data.h"
@ -57,7 +58,23 @@ struct xm_state {
int sample_data_offset[max_instruments]; int sample_data_offset[max_instruments];
}; };
xm_state xm = {0}; xm_state xm = {};
struct channel_state {
float rate; // const
int bytes_per_sample; // const
int sample_data_offset; // const bytes
int loop_start; // const bytes
int loop_end; // const bytes
int start; // bytes
int fragment_length; // bytes
int next_interrupt_tick; // ticks
bool active;
};
channel_state channel_state[64] = {};
int aica_interrupt_tick = 0;
struct interpreter_state { struct interpreter_state {
int tick_rate; int tick_rate;
@ -73,7 +90,7 @@ struct interpreter_state {
int song_length; int song_length;
}; };
struct interpreter_state state; interpreter_state state = {};
union aica_sandbox_channel { union aica_sandbox_channel {
struct { struct {
@ -167,7 +184,7 @@ int s32(void * buf)
return v; return v;
} }
uint8_t __attribute__((aligned(32))) sample_data[1024 * 1024]; uint8_t __attribute__((aligned(32))) sample_data[1024 * 1024 * 2];
int sample_data_ix; int sample_data_ix;
int unpack_sample(int buf, int offset, xm_sample_header_t * sample_header) int unpack_sample(int buf, int offset, xm_sample_header_t * sample_header)
@ -377,6 +394,106 @@ const static int cent_to_fns[] = {
}; };
const int cent_to_fns_length = (sizeof (cent_to_fns)) / (sizeof (cent_to_fns[0])); const int cent_to_fns_length = (sizeof (cent_to_fns)) / (sizeof (cent_to_fns[0]));
const static float note_to_period_ratio[] = {
89.38863558291501,
84.37163697237835,
79.63622085713295,
75.16658322609392,
70.94780707916073,
66.9658126431162,
63.207310381692366,
59.65975664297837,
56.31131179614828,
53.15080071779401,
50.16767549598949,
47.35198022761529,
44.694317791457536,
42.18581848618918,
39.81811042856648,
37.58329161304696,
35.47390353958036,
33.4829063215581,
31.603655190846183,
29.829878321489186,
28.15565589807414,
26.575400358897006,
25.083837747994746,
23.675990113807647,
22.347158895728768,
21.09290924309459,
19.90905521428324,
18.79164580652348,
17.73695176979018,
16.74145316077905,
15.801827595423092,
14.914939160744586,
14.07782794903707,
13.287700179448503,
12.54191887399737,
11.837995056903823,
11.173579447864384,
10.546454621547293,
9.95452760714162,
9.39582290326174,
8.868475884895087,
8.370726580389524,
7.900913797711546,
7.457469580372293,
7.038913974518535,
6.643850089724252,
6.270959436998685,
5.918997528451912,
5.586789723932192,
5.273227310773646,
4.97726380357081,
4.69791145163087,
4.4342379424475435,
4.185363290194762,
3.9504568988557724,
3.7287347901861465,
3.5194569872592676,
3.321925044862126,
3.1354797184993424,
2.959498764225956,
2.793394861966096,
2.636613655386823,
2.488631901785405,
2.3489557258154345,
2.2171189712237718,
2.092681645097381,
1.9752284494278862,
1.8643673950930733,
1.759728493629634,
1.6609625224310627,
1.5677398592496712,
1.4797493821129784,
1.3966974309830478,
1.3183068276934116,
1.2443159508927024,
1.1744778629077175,
1.1085594856118859,
1.0463408225486905,
0.9876142247139432,
0.9321836975465366,
0.8798642468148169,
0.8304812612155315,
0.7838699296248356,
0.739874691056489,
0.698348715491524,
0.6591534138467058,
0.6221579754463512,
0.5872389314538587,
0.5542797428059429,
0.5231704112743453,
0.4938071123569716,
0.4660918487732683,
0.43993212340740845,
0.4152406306077657,
0.3919349648124178,
0.3699373455282445,
0.349174357745762,
};
uint16_t uint16_t
note_to_oct_fns(const int8_t note) note_to_oct_fns(const int8_t note)
{ {
@ -400,6 +517,8 @@ note_to_oct_fns(const int8_t note)
int fns = cent_to_fns[(int)(fraction * cent_to_fns_length)]; int fns = cent_to_fns[(int)(fraction * cent_to_fns_length)];
printf(" oct %d fns %d\n", whole, fns);
return aica::oct_fns::OCT((int)whole) | aica::oct_fns::FNS((int)fns); return aica::oct_fns::OCT((int)whole) | aica::oct_fns::FNS((int)fns);
} }
@ -689,6 +808,102 @@ static inline void pump_events(uint32_t istnrm)
} }
} }
constexpr inline int intmin(int a, int b)
{
if (a < b)
return a;
else
return b;
}
constexpr inline int loop_mask(int n)
{
return (n) & ~(0b11);
}
void channel_state_init(int ch, int instrument, int note)
{
xm_sample_header_t * sample_header = xm.sample_header[instrument - 1];
int sample_type = ((sample_header->type & (1 << 4)) != 0);
int bytes_per_sample = 1 + sample_type;
int loop_start = s32(&sample_header->sample_loop_start);
int loop_length = s32(&sample_header->sample_loop_length);
if (loop_length == 0) {
loop_length = s32(&sample_header->sample_length);
}
struct channel_state& chs = channel_state[ch];
chs.rate = note_to_period_ratio[note + sample_header->relative_note_number];
printf("rate %f\n", chs.rate);
chs.bytes_per_sample = bytes_per_sample;
chs.sample_data_offset = xm.sample_data_offset[instrument - 1];
printf("sample_data_offset %d\n", chs.sample_data_offset);
chs.loop_start = loop_mask(loop_start);
chs.loop_end = loop_mask(loop_start + loop_length);
chs.start = -0xfffc;
chs.fragment_length = 0xfffc;
//wait(); aica_sound.channel[ch].oct_fns = note_to_oct_fns(note + sample_header->relative_note_number);
wait(); aica_sound.channel[ch].oct_fns = 0;
}
void channel_state_next(int ch)
{
struct channel_state& chs = channel_state[ch];
chs.start += chs.fragment_length;
if (chs.start >= chs.loop_end) {
chs.start = chs.loop_start;
}
int length = chs.loop_end - chs.start;
length = intmin(length, 0xfffc);
chs.fragment_length = length;
assert(length > 0);
//chs.next_interrupt_tick = aica_interrupt_tick + chs.rate * (length >> 1);
chs.next_interrupt_tick = aica_interrupt_tick + (length >> 1) + 1;
}
void channel_state_execute(int ch)
{
struct channel_state& chs = channel_state[ch];
int pcms = (chs.bytes_per_sample == 2) ? 1 : 0;
int sa = chs.sample_data_offset + chs.start;
int lea = chs.fragment_length >> (chs.bytes_per_sample - 1);
printf("execute sa %d lea %d\n", sa, lea);
wait(); aica_sound.channel[ch].PCMS(0);
wait(); aica_sound.channel[ch].SA(sa);
wait(); aica_sound.channel[ch].LSA(0);
wait(); aica_sound.channel[ch].LEA(lea);
wait(); aica_sound.channel[ch].LPCTL(1);
}
static inline void aica_events()
{
aica_interrupt_tick += 1;
for (int ch = 0; ch < 64; ch++) {
if (channel_state[ch].active) {
//printf("active %d\n", ch);
}
if (channel_state[ch].active && channel_state[ch].next_interrupt_tick == aica_interrupt_tick) {
channel_state_next(ch);
channel_state_execute(ch);
}
}
//printf("return\n");
}
static inline void tmu0_events() static inline void tmu0_events()
{ {
xm_pattern_header_t * pattern_header = xm.pattern_header[state.pattern_index]; xm_pattern_header_t * pattern_header = xm.pattern_header[state.pattern_index];
@ -747,6 +962,13 @@ void vbr600()
} }
pump_events(istnrm); pump_events(istnrm);
} else if (sh7091.CCN.EXPEVT == 0 && sh7091.CCN.INTEVT == 0x360) { // AICA
aica_sound.common.mcire = (1 << 6); // interrupt timer A
aica_sound.common.tactl_tima =
aica::tactl_tima::TACTL(0) // increment once every samples
| aica::tactl_tima::TIMA(0xffff) // interrupt after 1 counts
;
aica_events();
} else if (sh7091.CCN.EXPEVT == 0 && sh7091.CCN.INTEVT == 0x400) { // TMU0 } else if (sh7091.CCN.EXPEVT == 0 && sh7091.CCN.INTEVT == 0x400) { // TMU0
sh7091.TMU.TCR0 sh7091.TMU.TCR0
= tmu::tcr0::UNIE = tmu::tcr0::UNIE
@ -1211,8 +1433,9 @@ void sound_init()
//int buf = (int)&_binary_xm_milkypack01_xm_start; //int buf = (int)&_binary_xm_milkypack01_xm_start;
//int buf = (int)&_binary_xm_middle_c_xm_start; //int buf = (int)&_binary_xm_middle_c_xm_start;
//int buf = (int)&_binary_xm_test_xm_start; //int buf = (int)&_binary_xm_test_xm_start;
int buf = (int)&_binary_xm_xmtest_xm_start; //int buf = (int)&_binary_xm_xmtest_xm_start;
//int buf = (int)&_binary_xm_catch_this_rebel_xm_start; //int buf = (int)&_binary_xm_catch_this_rebel_xm_start;
int buf = (int)&_binary_xm_reign_xm_start;
xm_init(buf); xm_init(buf);
wait(); aica_sound.common.vreg_armrst = aica::vreg_armrst::ARMRST(1); wait(); aica_sound.common.vreg_armrst = aica::vreg_armrst::ARMRST(1);
@ -1268,6 +1491,8 @@ void sound_init()
wait(); aica_sound.common.dmea0_mrwinh = aica::dmea0_mrwinh::MRWINH(0b0001); wait(); aica_sound.common.dmea0_mrwinh = aica::dmea0_mrwinh::MRWINH(0b0001);
for (int i = 0; i < 64; i++) { for (int i = 0; i < 64; i++) {
channel_state[i].active = 0;
wait(); aica_sound.channel[i].KYONB(0); wait(); aica_sound.channel[i].KYONB(0);
wait(); aica_sound.channel[i].LPCTL(0); wait(); aica_sound.channel[i].LPCTL(0);
wait(); aica_sound.channel[i].PCMS(0); wait(); aica_sound.channel[i].PCMS(0);
@ -1327,6 +1552,15 @@ void sound_init()
sh7091.TMU.TSTR = tmu::tstr::str0::counter_start; sh7091.TMU.TSTR = tmu::tstr::str0::counter_start;
sh7091.INTC.IPRA = intc::ipra::TMU0(1); sh7091.INTC.IPRA = intc::ipra::TMU0(1);
aica_sound.common.tactl_tima =
aica::tactl_tima::TACTL(0) // increment once every samples
| aica::tactl_tima::TIMA(0xffff) // interrupt after 1 counts
;
aica_sound.common.mcieb = (1 << 6); // interrupt timer A
aica_sound.common.mcire = (1 << 6); // interrupt timer A
} }
static ft0::data_transfer::data_format data[4]; static ft0::data_transfer::data_format data[4];
@ -1378,40 +1612,6 @@ void do_get_condition()
void execute_note(int ch, const aica_sandbox_channel& channel) void execute_note(int ch, const aica_sandbox_channel& channel)
{ {
xm_sample_header_t * sample_header = xm.sample_header[channel.instrument - 1];
int sample_type = ((sample_header->type & (1 << 4)) != 0);
int bytes_per_sample = 1 + sample_type;
int start = xm.sample_data_offset[channel.instrument - 1];
int loop_type = sample_header->type & 0b11;
int lsa = s32(&sample_header->sample_loop_start) / bytes_per_sample;
int len = s32(&sample_header->sample_loop_length) / bytes_per_sample;
if (len == 0) {
len = s32(&sample_header->sample_length) / bytes_per_sample;
}
if (len >= 65535) {
len = 65532;
}
assert(start >= 0);
assert(lsa >= 0);
assert(len >= 0);
if (channel.loop && loop_type == 2) // bidirectional
len += len - 2;
assert(sample_header->volume >= 0 && sample_header->volume <= 64);
bool pcms = !sample_type;
wait(); aica_sound.channel[ch].oct_fns = note_to_oct_fns(channel.note + sample_header->relative_note_number);
wait(); aica_sound.channel[ch].PCMS(pcms);
wait(); aica_sound.channel[ch].SA(start);
wait(); aica_sound.channel[ch].LSA((lsa) & ~(0b11));
wait(); aica_sound.channel[ch].LEA((lsa + len) & ~(0b11));
wait(); aica_sound.channel[ch].LPCTL(channel.loop);
wait(); aica_sound.channel[ch].KRS(channel.krs); wait(); aica_sound.channel[ch].KRS(channel.krs);
wait(); aica_sound.channel[ch].AR(channel.ar); wait(); aica_sound.channel[ch].AR(channel.ar);
wait(); aica_sound.channel[ch].D1R(channel.d1r);; wait(); aica_sound.channel[ch].D1R(channel.d1r);;
@ -1427,6 +1627,10 @@ void execute_note(int ch, const aica_sandbox_channel& channel)
wait(); aica_sound.channel[ch].DISDL(channel.disdl); wait(); aica_sound.channel[ch].DISDL(channel.disdl);
wait(); aica_sound.channel[ch].DIPAN(channel.dipan); wait(); aica_sound.channel[ch].DIPAN(channel.dipan);
channel_state_init(ch, channel.instrument, channel.note);
channel_state_next(ch);
channel_state_execute(ch);
} }
void label_update(int delta) void label_update(int delta)
@ -1455,6 +1659,7 @@ void execute_key(bool on)
execute_note(ch, channel); execute_note(ch, channel);
channel.kyonb = on; channel.kyonb = on;
channel_state[ch].active = on;
wait(); aica_sound.channel[ch].KYONB(on); wait(); aica_sound.channel[ch].KYONB(on);
wait(); aica_sound.channel[ch].KYONEX(1); wait(); aica_sound.channel[ch].KYONEX(1);
} }
@ -1548,13 +1753,14 @@ void main()
sound_init(); sound_init();
graphics_init(); graphics_init();
interrupt_init();
channel_sandbox_defaults(); channel_sandbox_defaults();
interrupt_init();
system.IML6NRM = istnrm::end_of_render_tsp system.IML6NRM = istnrm::end_of_render_tsp
| istnrm::v_blank_in | istnrm::v_blank_in
| istnrm::end_of_transferring_opaque_list; | istnrm::end_of_transferring_opaque_list;
system.IML4EXT = istext::aica;
static uint8_t __attribute__((aligned(32))) ta_parameter_buf[1024 * 1024 * 1]; 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))); ta_parameter_writer writer = ta_parameter_writer(ta_parameter_buf, (sizeof (ta_parameter_buf)));

View File

@ -60,3 +60,8 @@
"FFST",,"5","holly_cpu_if_block_internal_write_buffer",,, "FFST",,"5","holly_cpu_if_block_internal_write_buffer",,,
"FFST",,"4","holly_g2_if_block_internal_write_buffer",,, "FFST",,"4","holly_g2_if_block_internal_write_buffer",,,
"FFST",,"0","aica_internal_write_buffer",,, "FFST",,"0","aica_internal_write_buffer",,,
,,,,,,
"ISTEXT",,3,"external_device",1,,
"ISTEXT",,2,"modem",1,,
"ISTEXT",,1,"aica",1,,
"ISTEXT",,0,"gdrom",1,,

1 register_name enum_name bits bit_name value mask description
60 FFST 5 holly_cpu_if_block_internal_write_buffer
61 FFST 4 holly_g2_if_block_internal_write_buffer
62 FFST 0 aica_internal_write_buffer
63
64 ISTEXT 3 external_device 1
65 ISTEXT 2 modem 1
66 ISTEXT 1 aica 1
67 ISTEXT 0 gdrom 1

Binary file not shown.

View File

@ -131,3 +131,13 @@ constexpr uint32_t holly_g2_if_block_internal_write_buffer(uint32_t reg) { retur
constexpr uint32_t aica_internal_write_buffer(uint32_t reg) { return (reg >> 0) & 0x1; } constexpr uint32_t aica_internal_write_buffer(uint32_t reg) { return (reg >> 0) & 0x1; }
} }
namespace istext {
constexpr uint32_t external_device = 1 << 3;
constexpr uint32_t modem = 1 << 2;
constexpr uint32_t aica = 1 << 1;
constexpr uint32_t gdrom = 1 << 0;
}

BIN
xm/reign.xm Normal file

Binary file not shown.

15
xm/reign.xm.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
extern uint32_t _binary_xm_reign_xm_start __asm("_binary_xm_reign_xm_start");
extern uint32_t _binary_xm_reign_xm_end __asm("_binary_xm_reign_xm_end");
extern uint32_t _binary_xm_reign_xm_size __asm("_binary_xm_reign_xm_size");
#ifdef __cplusplus
}
#endif