diff --git a/Makefile b/Makefile index a2f2ac5..82843a5 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,7 @@ include ip.mk include example/example.mk include example/bsp/bsp.mk +include example/aica/aica.mk include chess/chess.mk include text_editor/text_editor.mk diff --git a/example/aica.cpp b/example/aica/aica.cpp similarity index 100% rename from example/aica.cpp rename to example/aica/aica.cpp diff --git a/example/aica/aica.mk b/example/aica/aica.mk new file mode 100644 index 0000000..79e789f --- /dev/null +++ b/example/aica/aica.mk @@ -0,0 +1,37 @@ +AICA_OBJ = \ + example/aica/aica.o \ + sh7091/serial.o \ + example/arm/channel.bin.o + +example/aica/aica.elf: LDSCRIPT = $(LIB)/main.lds +example/aica/aica.elf: $(START_OBJ) $(AICA_OBJ) + +AICA_XM_OBJ = \ + example/aica/aica_xm.o \ + sh7091/serial.o \ + example/arm/xm.bin.o + +example/aica/aica_xm.elf: LDSCRIPT = $(LIB)/main.lds +example/aica/aica_xm.elf: $(START_OBJ) $(AICA_XM_OBJ) + +AICA_GDROM_OBJ = \ + example/aica/aica_gdrom.o \ + sh7091/serial.o \ + example/arm/sh4_interrupt.bin.o + +example/aica/aica_gdrom.elf: LDSCRIPT = $(LIB)/main.lds +example/aica/aica_gdrom.elf: $(START_OBJ) $(AICA_GDROM_OBJ) + +AICA_GDROM_DFT_OBJ = \ + example/aica/aica_gdrom_dft.o \ + holly/core.o \ + holly/region_array.o \ + holly/background.o \ + holly/ta_fifo_polygon_converter.o \ + holly/video_output.o \ + sh7091/serial.o \ + example/arm/sh4_interrupt.bin.o \ + $(LIBGCC) + +example/aica/aica_gdrom_dft.elf: LDSCRIPT = $(LIB)/main.lds +example/aica/aica_gdrom_dft.elf: $(START_OBJ) $(AICA_GDROM_DFT_OBJ) diff --git a/example/aica_gdrom.cpp b/example/aica/aica_gdrom.cpp similarity index 100% rename from example/aica_gdrom.cpp rename to example/aica/aica_gdrom.cpp diff --git a/example/aica_gdrom_dft.cpp b/example/aica/aica_gdrom_dft.cpp similarity index 100% rename from example/aica_gdrom_dft.cpp rename to example/aica/aica_gdrom_dft.cpp diff --git a/example/aica/aica_xm.cpp b/example/aica/aica_xm.cpp new file mode 100644 index 0000000..4b9e933 --- /dev/null +++ b/example/aica/aica_xm.cpp @@ -0,0 +1,65 @@ +#include "memorymap.hpp" +#include "sh7091/serial.hpp" +#include "systembus.hpp" +#include "systembus_bits.hpp" +#include "aica/aica.hpp" + +extern void * _binary_start __asm("_binary_example_arm_xm_bin_start"); +extern void * _binary_size __asm("_binary_example_arm_xm_bin_size"); + +void wait() +{ + while (ffst::aica_internal_write_buffer(system.FFST)); +} + +void wait_read() +{ + uint32_t ffst = system.FFST; + while ( ffst::holly_cpu_if_block_internal_write_buffer(ffst) + | ffst::holly_g2_if_block_internal_write_buffer(ffst) + | ffst::aica_internal_write_buffer(ffst)) { + ffst = system.FFST; + }; +} + +void main() +{ + const uint32_t * binary = reinterpret_cast(&_binary_start); + const uint32_t binary_size = reinterpret_cast(&_binary_size); + + wait(); aica_sound.common.vreg_armrst = aica::vreg_armrst::ARMRST(1); + wait(); aica_sound.common.dmea0_mrwinh = aica::dmea0_mrwinh::MRWINH(0b0111); + for (uint32_t i = 0; i < binary_size / 4; i++) { + // copy + while (aica_wave_memory[i] != binary[i]) { + wait(); + aica_wave_memory[i] = binary[i]; + } + } + wait(); aica_sound.common.dmea0_mrwinh = aica::dmea0_mrwinh::MRWINH(0b0001); + wait(); aica_sound.common.vreg_armrst = aica::vreg_armrst::ARMRST(0); + + wait(); aica_sound.common.afsel_mslc_mobuf = aica::afsel_mslc_mobuf::MSLC(0); + serial::string("mrwinh: "); + wait_read(); + serial::integer(aica_sound.common.MRWINH()); + while (1) { + wait_read(); + serial::string("sgc: "); + serial::integer(aica_sound.common.SGC(), ' '); + serial::string("; ca: "); + serial::integer(aica_sound.common.CA(), ' '); + serial::string("; eg: "); + serial::integer(aica_sound.common.EG(), ' '); + serial::string("; lp: "); + serial::integer(aica_sound.common.LP(), ' '); + serial::character('\n'); + for (int i = 0; i < 10000000; i++) { + asm volatile ("nop"); + } + serial::integer(aica_wave_memory[0], ' '); + serial::integer(aica_wave_memory[1], '\n'); + } + + while (1); +} diff --git a/example/arm/Makefile b/example/arm/Makefile index 76042d7..a5af401 100644 --- a/example/arm/Makefile +++ b/example/arm/Makefile @@ -25,6 +25,9 @@ channel.elf: start.o channel.o audio.pcm.o sh4_interrupt.elf: LDSCRIPT = main.lds sh4_interrupt.elf: start.o sh4_interrupt.o +xm.elf: LDSCRIPT = main.lds +xm.elf: start.o xm.o audio.pcm.o + clean: find -P \ -regextype posix-egrep \ diff --git a/example/arm/xm.cpp b/example/arm/xm.cpp new file mode 100644 index 0000000..6d7bc69 --- /dev/null +++ b/example/arm/xm.cpp @@ -0,0 +1,75 @@ +#include "aica/aica.hpp" + +extern void * _sine_start __asm("_binary_audio_pcm_start"); + +extern volatile uint32_t dram[0x200000] __asm("dram"); + +extern "C" +void main() +{ + const uint32_t sine_addr = reinterpret_cast(&_sine_start); + + volatile uint32_t * slot = reinterpret_cast(0x00800000); + for (uint32_t i = 0; i < (sizeof (struct aica_channel)) * 64 / 4; i++) { + slot[i] = 0; + } + + volatile uint32_t * dsp = reinterpret_cast(0x00803000); + for (int i = 0; i < 0xb00 / 4; i++) { + dsp[i] = 0; + } + + aica_sound.channel[0].KYONB(1); + aica_sound.channel[0].LPCTL(1); + aica_sound.channel[0].PCMS(0); + aica_sound.channel[0].LSA(0); + aica_sound.channel[0].LEA(128); + aica_sound.channel[0].D2R(0x0); + aica_sound.channel[0].D1R(0x0); + aica_sound.channel[0].RR(0x0); + aica_sound.channel[0].AR(0x1f); + + aica_sound.channel[0].OCT(0); + aica_sound.channel[0].FNS(0); + aica_sound.channel[0].DISDL(0xf); + aica_sound.channel[0].DIPAN(0x0); + + aica_sound.channel[0].Q(0b00100); + aica_sound.channel[0].TL(0); + aica_sound.channel[0].LPOFF(1); + + aica_sound.common.mono_mem8mb_dac18b_ver_mvol = + aica::mono_mem8mb_dac18b_ver_mvol::MONO(0) // enable panpots + | aica::mono_mem8mb_dac18b_ver_mvol::MEM8MB(0) // 16Mbit SDRAM + | aica::mono_mem8mb_dac18b_ver_mvol::DAC18B(0) // 16-bit DAC + | aica::mono_mem8mb_dac18b_ver_mvol::MVOL(0xf) // 15/15 volume + ; + + uint32_t segment = 0; + + dram[0] = 0x11223344; + dram[1] = sine_addr; + constexpr uint32_t timer_a_interrupt = (1 << 6); + aica_sound.common.scire = timer_a_interrupt; + uint32_t next_sa = sine_addr; + bool started = 0; + + while (1) { + if (!started || (aica_sound.common.SCIPD() & timer_a_interrupt)) { + aica_sound.channel[0].SA(next_sa); + aica_sound.common.tactl_tima = + aica::tactl_tima::TACTL(0) // increment once every 128 samples + | aica::tactl_tima::TIMA(256 - 128) // interrupt after 128 counts + ; + + if (!started) { aica_sound.channel[0].KYONEX(1); started = 1; } + + aica_sound.common.scire = timer_a_interrupt; + dram[1] = next_sa; + + segment += 1; + if (segment >= 3440) segment = 0; + next_sa = sine_addr + (128 * 2) * segment; + } + } +} diff --git a/example/example.mk b/example/example.mk index 23e0346..3dddc68 100644 --- a/example/example.mk +++ b/example/example.mk @@ -502,37 +502,6 @@ GDROM_JVM_BOOT_OBJ = \ example/gdrom_jvm_boot.elf: LDSCRIPT = $(LIB)/alt.lds example/gdrom_jvm_boot.elf: $(START_OBJ) $(GDROM_JVM_BOOT_OBJ) - -AICA_OBJ = \ - example/aica.o \ - sh7091/serial.o \ - example/arm/channel.bin.o - -example/aica.elf: LDSCRIPT = $(LIB)/main.lds -example/aica.elf: $(START_OBJ) $(AICA_OBJ) - -AICA_GDROM_OBJ = \ - example/aica_gdrom.o \ - sh7091/serial.o \ - example/arm/sh4_interrupt.bin.o - -example/aica_gdrom.elf: LDSCRIPT = $(LIB)/main.lds -example/aica_gdrom.elf: $(START_OBJ) $(AICA_GDROM_OBJ) - -AICA_GDROM_DFT_OBJ = \ - example/aica_gdrom_dft.o \ - holly/core.o \ - holly/region_array.o \ - holly/background.o \ - holly/ta_fifo_polygon_converter.o \ - holly/video_output.o \ - sh7091/serial.o \ - example/arm/sh4_interrupt.bin.o \ - $(LIBGCC) - -example/aica_gdrom_dft.elf: LDSCRIPT = $(LIB)/main.lds -example/aica_gdrom_dft.elf: $(START_OBJ) $(AICA_GDROM_DFT_OBJ) - MAC_SATURATION_OBJ = \ example/mac_saturation.o \ example/macl_saturation.o \ diff --git a/xm/debug.c b/xm/debug.c new file mode 100644 index 0000000..8ee7389 --- /dev/null +++ b/xm/debug.c @@ -0,0 +1,322 @@ +#include +#include +#include +#include + +#include "xm.h" + +int read_file(const char * filename, void ** buf, uint32_t * size_out) +{ + FILE * file = fopen(filename, "rb"); + if (file == NULL) { + fprintf(stderr, "fopen(\"%s\", \"rb\"): %s\n", filename, strerror(errno)); + return -1; + } + + int ret; + ret = fseek(file, 0L, SEEK_END); + if (ret < 0) { + fprintf(stderr, "fseek(SEEK_END)"); + return -1; + } + + long offset = ftell(file); + if (offset < 0) { + fprintf(stderr, "ftell"); + return -1; + } + size_t size = offset; + + ret = fseek(file, 0L, SEEK_SET); + if (ret < 0) { + fprintf(stderr, "fseek(SEEK_SET)"); + return -1; + } + + fprintf(stderr, "read_file: %s size %ld\n", filename, size); + *buf = (uint8_t *)malloc(size); + size_t fread_size = fread(*buf, 1, size, file); + if (fread_size != size) { + fprintf(stderr, "fread `%s` short read: %d ; expected: %d\n", filename, (int)fread_size, (int)size); + return -1; + } + + ret = fclose(file); + if (ret < 0) { + fprintf(stderr, "fclose"); + return -1; + } + + *size_out = size; + + return 0; +} + +int s16(void * buf) +{ + uint8_t * b = (uint8_t *)buf; + int16_t v = (b[0] << 0) | (b[1] << 8); + return v; +} + +int s32(void * buf) +{ + uint8_t * b = (uint8_t *)buf; + int32_t v = (b[0] << 0) | (b[1] << 8) | (b[2] << 16) | (b[3] << 24); + return v; +} + +void print_chars(int8_t * chars, int length, const char * end) +{ + for (int i = 0; i < length; i++) { + int8_t c = chars[i]; + if (c >= 0x20 && c <= 0x7e) { + fputc(c, stdout); + } else { + printf("\\x%02x", c); + } + } + if (end != NULL) + fputs(end, stdout); +} + +void debug_header(void * buf) +{ + xm_header_t * header = (xm_header_t *)buf; + printf("header:\n"); + printf(" id_text: '"); + print_chars(header->id_text, 17, "'\n"); + printf(" module_name: '"); + print_chars(header->module_name, 20, "'\n"); + printf(" xm_type: 0x%02x\n", header->xm_type); + printf(" tracker_name: '"); + print_chars(header->tracker_name, 20, "'\n"); + printf(" version_number: 0x%04x\n", s16(&header->version_number)); + printf(" header_size: %d\n", s32(&header->header_size)); + printf(" song_length: %d\n", s16(&header->song_length)); + printf(" restart_position: %d\n", s16(&header->restart_position)); + printf(" number_of_channels: %d\n", s16(&header->number_of_channels)); + printf(" number_of_patterns: %d\n", s16(&header->number_of_patterns)); + printf(" number_of_instruments: %d\n", s16(&header->number_of_instruments)); + printf(" flags: %d\n", s16(&header->flags)); + printf(" default_tempo: %d\n", s16(&header->default_tempo)); + printf(" default_bpm: %d\n", s16(&header->default_bpm)); +} + +static xm_pattern_format_t column[8]; + +void debug_pattern_format(int note_ix, xm_pattern_format_t * pf) +{ + /* + printf("note[%d]\n", note_ix); + printf(" note: %d\n", pf->note); + printf(" instrument: %d\n", pf->instrument); + printf(" volume_column_byte: %d\n", pf->volume_column_byte); + printf(" effect_type: %d\n", pf->effect_type); + printf(" effect_parameter: %d\n", pf->effect_parameter); + */ + column[note_ix & 7] = *pf; + if ((note_ix & 7) == 7) { + printf("%3d |", note_ix / 8); + for (int i = 0; i < 8; i++) + printf(" n:%2d i:%2d |", + column[i].note, column[i].instrument); + printf("\n"); + } +} + +void debug_pattern(xm_pattern_header_t * pattern_header) +{ + printf(" | channel 0 | channel 1 | channel 2 | channel 3 | channel 4 | channel 5 | channel 6 | channel 7 |\n"); + uint8_t * pattern = (uint8_t *)(((ptrdiff_t)pattern_header) + s32(&pattern_header->pattern_header_length)); + int ix = 0; + int note_ix = 0; + while (ix < s16(&pattern_header->packed_pattern_data_size)) { + int p = pattern[ix]; + if (p & 0x80) { + ix += 1; + xm_pattern_format_t pf = {}; + if (p & (1 << 0)) + pf.note = pattern[ix++]; + if (p & (1 << 1)) + pf.instrument = pattern[ix++]; + if (p & (1 << 2)) + pf.volume_column_byte = pattern[ix++]; + if (p & (1 << 3)) + pf.effect_type = pattern[ix++]; + if (p & (1 << 4)) + pf.effect_parameter = pattern[ix++]; + debug_pattern_format(note_ix, &pf); + } else { + xm_pattern_format_t * pf = (xm_pattern_format_t *)&pattern[ix]; + debug_pattern_format(note_ix, pf); + ix += 5; + } + note_ix += 1; + } + assert(ix == s16(&pattern_header->packed_pattern_data_size)); +} + +int debug_pattern_headers(void * buf) +{ + xm_header_t * header = (xm_header_t *)buf; + int pattern_header_offset = s32(&header->header_size) + (offsetof (struct xm_header, header_size)); + + for (int i = 0; i < s16(&header->number_of_patterns); i++) { + xm_pattern_header_t * pattern_header = (xm_pattern_header_t *)(((ptrdiff_t)buf) + pattern_header_offset); + + printf("pattern_header[%d]:\n", i); + printf(" pattern_header_length: %d\n", s32(&pattern_header->pattern_header_length)); + printf(" packing_type: %d\n", pattern_header->packing_type); + printf(" number_of_rows_in_pattern: %d\n", s16(&pattern_header->number_of_rows_in_pattern)); + printf(" packed_pattern_data_size: %d\n", s16(&pattern_header->packed_pattern_data_size)); + //debug_pattern(pattern_header); + pattern_header_offset += s32(&pattern_header->pattern_header_length) + s16(&pattern_header->packed_pattern_data_size); + } + return pattern_header_offset; +} + +void write_file(const char * filename, void * buf, int size) +{ + printf("write %s\n", filename); + FILE * file = fopen(filename, "wb"); + if (file == NULL) { + fprintf(stderr, "fopen(\"%s\", \"wb\"): %s\n", filename, strerror(errno)); + return; + } + + size_t write = fwrite(buf, 1, size, file); + assert(write == size); + + int ret = fclose(file); + assert(ret == 0); +} + +int saturation16(int v) +{ + /* + if (v > 32767) + return 32767; + if (v < -32768) + return -32768; + */ + return v; +} + +int saturation8(int v) +{ + /* + if (v > 127) + return 127; + if (v < -128) + return -128; + */ + return v; +} + +void dump_sample(void * buf, int offset, int sample_ix, xm_sample_header_t * sample_header) +{ + assert(sample_header->sample_data_type == 0); + int old = 0; + int size = s32(&sample_header->sample_length); + printf("%d offset %d\n", sample_ix, offset); + if (sample_header->type & (1 << 4)) { // 16-bit samples + int num_samples = size / 2; + int old = 0; + int16_t out[num_samples]; + int16_t * in = (int16_t *)(((ptrdiff_t)buf) + offset); + for (int i = 0; i < num_samples; i++) { + old += in[i]; + out[i] = saturation16(old); + } + char filename[64]; + snprintf(filename, 64, "sample%03d.s16le.pcm", sample_ix); + write_file(filename, out, size); + + } else { // 8-bit + int num_samples = size; + int old = 0; + int8_t out[num_samples]; + int8_t * in = (int8_t *)(((ptrdiff_t)buf) + offset); + for (int i = 0; i < num_samples; i++) { + old += in[i]; + out[i] = old; + } + char filename[64]; + snprintf(filename, 64, "sample%03d.s8.pcm", sample_ix); + write_file(filename, out, size); + } +} + +int debug_samples(void * buf, int offset, int instrument_ix, int number_of_samples) +{ + xm_sample_header_t * sample_header[number_of_samples]; + printf("A offset %d\n", offset); + for (int i = 0; i < number_of_samples; i++) { + sample_header[i] = (xm_sample_header_t *)(((ptrdiff_t)buf) + offset); + printf(" sample header %d offset %d\n", i, offset); + printf(" sample[%d]\n", i); + printf(" sample_length: %d\n", s32(&sample_header[i]->sample_length)); + printf(" sample_loop_start: %d\n", s32(&sample_header[i]->sample_loop_start)); + printf(" sample_loop_length: %d\n", s32(&sample_header[i]->sample_loop_length)); + printf(" volume: %d\n", sample_header[i]->volume); + printf(" finetune: %d\n", sample_header[i]->finetune); + printf(" type: %d\n", sample_header[i]->type); + printf(" panning: %d\n", sample_header[i]->panning); + printf(" relative_note_number: %d\n", sample_header[i]->relative_note_number); + printf(" sample_data_type: %d\n", sample_header[i]->sample_data_type); + printf(" sample_name: '"); + print_chars(sample_header[i]->sample_name, 22, "'\n"); + offset += (sizeof (xm_sample_header_t)); + } + + printf("B offset %d\n", offset); + for (int i = 0; i < number_of_samples; i++) { + if (s32(&sample_header[i]->sample_length) > 0) + dump_sample(buf, offset, instrument_ix, sample_header[i]); + offset += s32(&sample_header[i]->sample_length); + } + return offset; +} + +int debug_instruments(void * buf, int offset) +{ + xm_header_t * header = (xm_header_t *)buf; + + for (int i = 0; i < s16(&header->number_of_instruments); i++) { + printf("instrument offset %d: %d\n", i, offset); + xm_instrument_header_t * instrument_header = (xm_instrument_header_t *)(((ptrdiff_t)buf) + offset); + + printf("instrument[%d]\n", i); + printf(" instrument_size: %d\n", s32(&instrument_header->instrument_size)); + printf(" instrument_name: '"); + print_chars(instrument_header->instrument_name, 22, "'\n"); + printf(" instrument_type: %d\n", instrument_header->instrument_type); + printf(" number_of_samples: %d\n", s16(&instrument_header->number_of_samples)); + + offset += s32(&instrument_header->instrument_size); + printf("this offset %d\n", offset); + + if (s16(&instrument_header->number_of_samples) > 0) { + printf(" sample_header_size: %d\n", s32(&instrument_header->sample_header_size)); + + offset = debug_samples(buf, offset, i, s16(&instrument_header->number_of_samples)); + } + } +} + +int main(int argc, const char *argv[]) +{ + assert(argc == 2); + const char * filename = argv[1]; + + void * buf; + uint32_t size; + int res = read_file(filename, &buf, &size); + if (res != 0) + return EXIT_FAILURE; + + debug_header(buf); + int end_of_patterns = debug_pattern_headers(buf); + debug_instruments(buf, end_of_patterns); +} diff --git a/xm/milkypack01.xm b/xm/milkypack01.xm new file mode 100644 index 0000000..7bc7008 Binary files /dev/null and b/xm/milkypack01.xm differ diff --git a/xm/tune.txt b/xm/tune.txt new file mode 100644 index 0000000..b0dbeec --- /dev/null +++ b/xm/tune.txt @@ -0,0 +1 @@ +11025 F-4 diff --git a/xm/xm.h b/xm/xm.h new file mode 100644 index 0000000..912207a --- /dev/null +++ b/xm/xm.h @@ -0,0 +1,141 @@ +#pragma once + +#include +#include +#include + +typedef struct __attribute__((packed)) xm_header { + int8_t id_text[17]; + int8_t module_name[20]; + uint8_t xm_type; + int8_t tracker_name[20]; + int16_t version_number; + int32_t header_size; + int16_t song_length; + int16_t restart_position; + int16_t number_of_channels; + int16_t number_of_patterns; + int16_t number_of_instruments; + int16_t flags; + int16_t default_tempo; + int16_t default_bpm; + uint8_t pattern_order_table[]; +} xm_header_t; + +static_assert((offsetof (struct xm_header, id_text)) == 0); +static_assert((offsetof (struct xm_header, module_name)) == 17); +static_assert((offsetof (struct xm_header, xm_type)) == 37); +static_assert((offsetof (struct xm_header, tracker_name)) == 38); +static_assert((offsetof (struct xm_header, version_number)) == 58); +static_assert((offsetof (struct xm_header, header_size)) == 60); +static_assert((offsetof (struct xm_header, song_length)) == 64); +static_assert((offsetof (struct xm_header, restart_position)) == 66); +static_assert((offsetof (struct xm_header, number_of_channels)) == 68); +static_assert((offsetof (struct xm_header, number_of_patterns)) == 70); +static_assert((offsetof (struct xm_header, number_of_instruments)) == 72); +static_assert((offsetof (struct xm_header, flags)) == 74); +static_assert((offsetof (struct xm_header, default_tempo)) == 76); +static_assert((offsetof (struct xm_header, default_bpm)) == 78); +static_assert((offsetof (struct xm_header, pattern_order_table)) == 80); + +typedef struct __attribute__((packed)) xm_pattern_header { + int32_t pattern_header_length; + int8_t packing_type; + int16_t number_of_rows_in_pattern; + int16_t packed_pattern_data_size; + //int8_t packed_pattern_data[]; +} xm_pattern_header_t; + +static_assert((offsetof (struct xm_pattern_header, pattern_header_length)) == 0); +static_assert((offsetof (struct xm_pattern_header, packing_type)) == 4); +static_assert((offsetof (struct xm_pattern_header, number_of_rows_in_pattern)) == 5); +static_assert((offsetof (struct xm_pattern_header, packed_pattern_data_size)) == 7); +//static_assert((offsetof (struct xm_pattern_header, packed_pattern_data)) == 9); + +typedef struct __attribute__((packed)) xm_instrument_header { + int32_t instrument_size; + int8_t instrument_name[22]; + uint8_t instrument_type; + int16_t number_of_samples; + int32_t sample_header_size; + uint8_t sample_keymap_assignments[96]; + int16_t points_for_volume_envelope[24]; + int16_t points_for_panning_envelope[24]; + int8_t number_of_volume_points; + int8_t number_of_panning_points; + int8_t volume_sustain_point; + int8_t volume_loop_start_point; + int8_t volume_loop_end_point; + int8_t panning_sustain_point; + int8_t panning_loop_start_point; + int8_t panning_loop_end_point; + int8_t volume_type; + int8_t panning_type; + int8_t vibrato_type; + int8_t vibrato_sweep; + int8_t vibrato_depth; + int8_t vibrato_rate; + int8_t volume_fadeout; +} xm_instrument_header_t; + +static_assert((offsetof (struct xm_instrument_header, instrument_size)) == 0); +static_assert((offsetof (struct xm_instrument_header, instrument_name)) == 4); +static_assert((offsetof (struct xm_instrument_header, instrument_type)) == 26); +static_assert((offsetof (struct xm_instrument_header, number_of_samples)) == 27); +static_assert((offsetof (struct xm_instrument_header, sample_header_size)) == 29); +static_assert((offsetof (struct xm_instrument_header, sample_keymap_assignments)) == 33); +static_assert((offsetof (struct xm_instrument_header, points_for_volume_envelope)) == 129); +static_assert((offsetof (struct xm_instrument_header, points_for_panning_envelope)) == 177); +static_assert((offsetof (struct xm_instrument_header, number_of_volume_points)) == 225); +static_assert((offsetof (struct xm_instrument_header, number_of_panning_points)) == 226); +static_assert((offsetof (struct xm_instrument_header, volume_sustain_point)) == 227); +static_assert((offsetof (struct xm_instrument_header, volume_loop_start_point)) == 228); +static_assert((offsetof (struct xm_instrument_header, volume_loop_end_point)) == 229); +static_assert((offsetof (struct xm_instrument_header, panning_sustain_point)) == 230); +static_assert((offsetof (struct xm_instrument_header, panning_loop_start_point)) == 231); +static_assert((offsetof (struct xm_instrument_header, panning_loop_end_point)) == 232); +static_assert((offsetof (struct xm_instrument_header, volume_type)) == 233); +static_assert((offsetof (struct xm_instrument_header, panning_type)) == 234); +static_assert((offsetof (struct xm_instrument_header, vibrato_type)) == 235); +static_assert((offsetof (struct xm_instrument_header, vibrato_sweep)) == 236); +static_assert((offsetof (struct xm_instrument_header, vibrato_depth)) == 237); +static_assert((offsetof (struct xm_instrument_header, vibrato_rate)) == 238); +static_assert((offsetof (struct xm_instrument_header, volume_fadeout)) == 239); + +typedef struct __attribute__((packed)) xm_sample_header { + int32_t sample_length; + int32_t sample_loop_start; + int32_t sample_loop_length; + uint8_t volume; + uint8_t finetune; + uint8_t type; + uint8_t panning; + int8_t relative_note_number; + uint8_t sample_data_type; + int8_t sample_name[22]; +} xm_sample_header_t; + +static_assert((offsetof (struct xm_sample_header, sample_length)) == 0); +static_assert((offsetof (struct xm_sample_header, sample_loop_start)) == 4); +static_assert((offsetof (struct xm_sample_header, sample_loop_length)) == 8); +static_assert((offsetof (struct xm_sample_header, volume)) == 12); +static_assert((offsetof (struct xm_sample_header, finetune)) == 13); +static_assert((offsetof (struct xm_sample_header, type)) == 14); +static_assert((offsetof (struct xm_sample_header, panning)) == 15); +static_assert((offsetof (struct xm_sample_header, relative_note_number)) == 16); +static_assert((offsetof (struct xm_sample_header, sample_data_type)) == 17); +static_assert((offsetof (struct xm_sample_header, sample_name)) == 18); + +typedef struct __attribute__((packed)) xm_pattern_format { + uint8_t note; + uint8_t instrument; + uint8_t volume_column_byte; + uint8_t effect_type; + uint8_t effect_parameter; +} xm_pattern_format_t; + +static_assert((offsetof (struct xm_pattern_format, note)) == 0); +static_assert((offsetof (struct xm_pattern_format, instrument)) == 1); +static_assert((offsetof (struct xm_pattern_format, volume_column_byte)) == 2); +static_assert((offsetof (struct xm_pattern_format, effect_type)) == 3); +static_assert((offsetof (struct xm_pattern_format, effect_parameter)) == 4);