example: reorganize aica examples

This commit is contained in:
Zack Buhman 2025-05-06 20:59:18 -05:00
parent 60d7dd8c1c
commit dcffc7854e
13 changed files with 645 additions and 31 deletions

View File

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

37
example/aica/aica.mk Normal file
View File

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

65
example/aica/aica_xm.cpp Normal file
View File

@ -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<uint32_t *>(&_binary_start);
const uint32_t binary_size = reinterpret_cast<uint32_t>(&_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<uint8_t>(aica_sound.common.MRWINH());
while (1) {
wait_read();
serial::string("sgc: ");
serial::integer<uint8_t>(aica_sound.common.SGC(), ' ');
serial::string("; ca: ");
serial::integer<uint8_t>(aica_sound.common.CA(), ' ');
serial::string("; eg: ");
serial::integer<uint8_t>(aica_sound.common.EG(), ' ');
serial::string("; lp: ");
serial::integer<uint8_t>(aica_sound.common.LP(), ' ');
serial::character('\n');
for (int i = 0; i < 10000000; i++) {
asm volatile ("nop");
}
serial::integer<uint32_t>(aica_wave_memory[0], ' ');
serial::integer<uint32_t>(aica_wave_memory[1], '\n');
}
while (1);
}

View File

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

75
example/arm/xm.cpp Normal file
View File

@ -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<uint32_t>(&_sine_start);
volatile uint32_t * slot = reinterpret_cast<volatile uint32_t*>(0x00800000);
for (uint32_t i = 0; i < (sizeof (struct aica_channel)) * 64 / 4; i++) {
slot[i] = 0;
}
volatile uint32_t * dsp = reinterpret_cast<volatile uint32_t*>(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;
}
}
}

View File

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

322
xm/debug.c Normal file
View File

@ -0,0 +1,322 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#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);
}

BIN
xm/milkypack01.xm Normal file

Binary file not shown.

1
xm/tune.txt Normal file
View File

@ -0,0 +1 @@
11025 F-4

141
xm/xm.h Normal file
View File

@ -0,0 +1,141 @@
#pragma once
#include <stdint.h>
#include <assert.h>
#include <stddef.h>
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);