scene/logo: simultaneous jingle and animation

This commit is contained in:
Zack Buhman 2025-06-28 13:36:24 -05:00
parent 5eaadbb1c9
commit 9b2d91e2e6
17 changed files with 235 additions and 95 deletions

Binary file not shown.

BIN
pcm/jingle.adpcm Normal file

Binary file not shown.

15
pcm/jingle.adpcm.h Normal file
View File

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

BIN
pcm/jingle.ogg Normal file

Binary file not shown.

BIN
pcm/jingle.pcm Normal file

Binary file not shown.

View File

@ -20,11 +20,9 @@
#include "holly/video_output.hpp" #include "holly/video_output.hpp"
#include "framebuffer.hpp" #include "framebuffer.hpp"
#include "scene/scene.hpp"
#include "scene/tracker/scene.hpp"
#include "scene/logo/scene.hpp"
#include "graphics.hpp" #include "graphics.hpp"
#include "texture.hpp" #include "texture.hpp"
#include "scene/scene.hpp"
static volatile int ta_in_use = 0; static volatile int ta_in_use = 0;
static volatile int core_in_use = 0; static volatile int core_in_use = 0;
@ -32,13 +30,6 @@ static volatile int next_frame = 0;
static volatile int framebuffer_ix = 0; static volatile int framebuffer_ix = 0;
static volatile int next_frame_ix = 0; static volatile int next_frame_ix = 0;
static const scene::scene scenes[] = {
scene::tracker::scene,
scene::logo::scene,
};
static const scene::scene * current_scene = &scenes[1];
void graphics_interrupt(uint32_t istnrm) void graphics_interrupt(uint32_t istnrm)
{ {
if (istnrm & istnrm::v_blank_in) { if (istnrm & istnrm::v_blank_in) {
@ -103,11 +94,11 @@ void graphics_init()
transfer_palettes(); transfer_palettes();
} }
void graphics_scene_init() void graphics_scene_init(const opb_size * opb_size)
{ {
region_array_multipass(framebuffer.tile_width(), region_array_multipass(framebuffer.tile_width(),
framebuffer.tile_height(), framebuffer.tile_height(),
&current_scene->opb_size, opb_size,
ta_cont_count, ta_cont_count,
texture_memory_alloc.region_array.start, texture_memory_alloc.region_array.start,
texture_memory_alloc.object_list.start); texture_memory_alloc.object_list.start);
@ -117,7 +108,7 @@ void graphics_event(ta_parameter_writer& writer)
{ {
writer.offset = 0; writer.offset = 0;
current_scene->transfer(writer); scene::current_scene->transfer(writer);
while (ta_in_use); while (ta_in_use);
while (core_in_use); while (core_in_use);
@ -126,8 +117,8 @@ void graphics_event(ta_parameter_writer& writer)
texture_memory_alloc.isp_tsp_parameters.end, texture_memory_alloc.isp_tsp_parameters.end,
texture_memory_alloc.object_list.start, texture_memory_alloc.object_list.start,
texture_memory_alloc.object_list.end, texture_memory_alloc.object_list.end,
current_scene->opb_size.total(), scene::current_scene->opb_size.total(),
current_scene->ta_alloc, scene::current_scene->ta_alloc,
framebuffer.tile_width(), framebuffer.tile_width(),
framebuffer.tile_height()); framebuffer.tile_height());
ta_polygon_converter_writeback(writer.buf, writer.offset); ta_polygon_converter_writeback(writer.buf, writer.offset);

View File

@ -3,10 +3,11 @@
#include <stdint.h> #include <stdint.h>
#include "holly/ta_parameter.hpp" #include "holly/ta_parameter.hpp"
#include "holly/region_array.hpp"
constexpr int ta_cont_count = 1; constexpr int ta_cont_count = 1;
void graphics_interrupt(uint32_t istnrm); void graphics_interrupt(uint32_t istnrm);
void graphics_init(); void graphics_init();
void graphics_event(ta_parameter_writer& writer); void graphics_event(ta_parameter_writer& writer);
void graphics_scene_init(); void graphics_scene_init(const opb_size * opb_size);

View File

@ -12,6 +12,8 @@
#include "graphics.hpp" #include "graphics.hpp"
#include "interpreter.hpp" #include "interpreter.hpp"
#include "sound.hpp" #include "sound.hpp"
#include "scene/scene.hpp"
#include "scene/logo/sound.hpp"
#include "xm/milkypack01.xm.h" #include "xm/milkypack01.xm.h"
#include "xm.h" #include "xm.h"
@ -51,12 +53,8 @@ void vbr600()
aica::tactl_tima::TACTL(0) // increment once every sample aica::tactl_tima::TACTL(0) // increment once every sample
| aica::tactl_tima::TIMA(0xffff) // interrupt after 1 counts | 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
| tmu::tcr0::tpsc::p_phi_256; // clear underflow
scene::logo::sound::interrupt();
//interpreter::interrupt(); //interpreter::interrupt();
} else { } else {
serial::string("vbr600\n"); serial::string("vbr600\n");
@ -197,7 +195,7 @@ void load_xm(float clock_multiplier)
sample_data, sample_data,
sample_data_length); sample_data_length);
interpreter::init(clock_multiplier); interpreter::init(clock_multiplier);
sound_init(sample_data, sample_data_ix); sound::transfer(sample_data, sample_data_ix);
printf("tick_rate %d\n", state.tick_rate); printf("tick_rate %d\n", state.tick_rate);
} }
@ -235,13 +233,13 @@ void main()
{ {
serial::init(0); serial::init(0);
//const float tmu_clock_multiplier = 195.32; sound::init();
const float aica_clock_multiplier = 44.1;
load_xm(aica_clock_multiplier);
graphics_init(); graphics_init();
graphics_scene_init(); scene::scene_init();
//const float aica_clock_multiplier = 44.1;
//load_xm(aica_clock_multiplier);
test_pattern(); //test_pattern();
interrupt_init(); interrupt_init();
//channel_sandbox_defaults(); //channel_sandbox_defaults();

View File

@ -4,6 +4,7 @@
#include "ta_parameter.hpp" #include "ta_parameter.hpp"
#include "scene/logo/scene.hpp" #include "scene/logo/scene.hpp"
#include "scene/logo/sound.hpp"
#include "texture.hpp" #include "texture.hpp"
#include "framebuffer.hpp" #include "framebuffer.hpp"
@ -18,7 +19,7 @@ static vec3 screen_transform(const vec3& v)
static inline float light_intensity(vec3 n, vec3 l) static inline float light_intensity(vec3 n, vec3 l)
{ {
float ambient = 0.2f; float ambient = 0.4f;
float diffuse_strength = 0.7f; float diffuse_strength = 0.7f;
float n_dot_l = dot(n, l); float n_dot_l = dot(n, l);
@ -31,7 +32,7 @@ static inline float light_intensity(vec3 n, vec3 l)
vec3 light_vec = (vec3){-1, -1, -1} - (vec3){0, 0, 0}; vec3 light_vec = (vec3){-1, -1, -1} - (vec3){0, 0, 0};
static void render_mesh(ta_parameter_writer& writer, const mesh& mesh, const mat4x4& trans, bool wireframe) static void render_mesh(ta_parameter_writer& writer, const mesh& mesh, const mat4x4& trans, float base_intensity, bool wireframe)
{ {
if (wireframe) { if (wireframe) {
global_polygon_untextured(writer, global_polygon_untextured(writer,
@ -57,7 +58,7 @@ static void render_mesh(ta_parameter_writer& writer, const mesh& mesh, const mat
normal_cache[i] = normal_multiply(trans, mesh.polygon_normal[i]); normal_cache[i] = normal_multiply(trans, mesh.polygon_normal[i]);
} }
const int green = 0x00ff00; const int green = (int)(255.f * base_intensity) << 8;
for (int i = 0; i < mesh.polygons_length; i++) { for (int i = 0; i < mesh.polygons_length; i++) {
const polygon& p = mesh.polygons[i]; const polygon& p = mesh.polygons[i];
@ -118,26 +119,37 @@ namespace scene::logo {
.punch_through = 0 .punch_through = 0
}, },
.transfer = transfer, .transfer = transfer,
.init = init
}; };
static int tick = 0; static int tick = 0;
static int last_tick = 0; static int last_tick = 0;
struct keyframe { struct keyframe {
float rx; float i; // intensity
float ry; float rx; // rotate_x
float s; float ry; // rotate_y
float s; // scale
float duration; float duration;
}; };
const struct keyframe keyframes[] = { const struct keyframe keyframes[] = {
{ {
.i = 0,
.rx = 0, .rx = 0,
.ry = 0, .ry = pi,
.s = 0.1, .s = 0.01,
.duration = 1.0 / (5 * 60), .duration = 1.0 / (5.2 * 60),
}, },
{ {
.i = 1,
.rx = 0,
.ry = pi,
.s = 0.1,
.duration = 1.0 / (4.5 * 60),
},
{
.i = 1,
.rx = pi / 4, .rx = pi / 4,
.ry = pi + pi / 4, .ry = pi + pi / 4,
.s = 0.7, .s = 0.7,
@ -145,6 +157,8 @@ namespace scene::logo {
}, },
}; };
const int keyframes_length = (sizeof (keyframes)) / (sizeof (keyframes[0]));
static inline float clamp(float f) static inline float clamp(float f)
{ {
if (f > 1.0) if (f > 1.0)
@ -159,14 +173,16 @@ namespace scene::logo {
{ {
float ratio = clamp(dt * a.duration); float ratio = clamp(dt * a.duration);
float di = b.i - a.i;
float drx = b.rx - a.rx; float drx = b.rx - a.rx;
float dry = b.ry - a.ry; float dry = b.ry - a.ry;
float drs = b.s - a.s; float ds = b.s - a.s;
return { return {
.i = a.i + di * ratio,
.rx = a.rx + drx * ratio, .rx = a.rx + drx * ratio,
.ry = a.ry + dry * ratio, .ry = a.ry + dry * ratio,
.s = a.s + drs * ratio, .s = a.s + ds * ratio,
.duration = a.duration, .duration = a.duration,
}; };
} }
@ -176,7 +192,17 @@ namespace scene::logo {
vec3 t = {framebuffer.px_width / 2.f, framebuffer.px_height / 2.f, 0}; vec3 t = {framebuffer.px_width / 2.f, framebuffer.px_height / 2.f, 0};
float s = framebuffer.px_height / 3.f; float s = framebuffer.px_height / 3.f;
keyframe k = interpolate(keyframes[0], keyframes[1], tick - last_tick); static int keyframe_ix = 0;
float dt = tick - last_tick;
if (dt * keyframes[keyframe_ix].duration >= 1) {
if (keyframe_ix < (keyframes_length - 2)) {
last_tick = tick;
dt = 0;
keyframe_ix += 1;
}
}
keyframe k = interpolate(keyframes[keyframe_ix], keyframes[keyframe_ix + 1], dt);
mat4x4 trans mat4x4 trans
= translate(t) = translate(t)
@ -189,13 +215,20 @@ namespace scene::logo {
* rotate_y(k.ry) * rotate_y(k.ry)
* scale((vec3){-1, -1, 1}); * scale((vec3){-1, -1, 1});
render_mesh(writer, mesh_thirty_two, trans, tick < (6 * 60)); render_mesh(writer, mesh_thirty_two, trans, k.i, tick < (9.85 * 60));
render_mesh(writer, mesh_bit, trans, tick < (7 * 60)); if (keyframe_ix > 0) {
render_mesh(writer, mesh_jam, trans, tick < (8 * 60)); render_mesh(writer, mesh_bit, trans, k.i, tick < (10.85 * 60));
render_mesh(writer, mesh_jam, trans, k.i, tick < (11.85 * 60));
}
writer.append<ta_global_parameter::end_of_list>() = writer.append<ta_global_parameter::end_of_list>() =
ta_global_parameter::end_of_list(para_control::para_type::end_of_list); ta_global_parameter::end_of_list(para_control::para_type::end_of_list);
tick += 1; tick += 1;
} }
void init()
{
::scene::logo::sound::init();
}
} }

View File

@ -7,5 +7,7 @@ namespace scene::logo {
void transfer(ta_parameter_writer& writer); void transfer(ta_parameter_writer& writer);
void init();
extern const struct scene::scene scene; extern const struct scene::scene scene;
} }

74
src/scene/logo/sound.cpp Normal file
View File

@ -0,0 +1,74 @@
#include "aica/aica.hpp"
#include "../../sound.hpp"
#include "scene/logo/sound.hpp"
#include "pcm/jingle.adpcm.h"
#include "printf/printf.h"
namespace scene::logo::sound {
const static void * start = reinterpret_cast<void *>(&_binary_pcm_jingle_adpcm_start);
const static int size = reinterpret_cast<int>(&_binary_pcm_jingle_adpcm_size);
const static int sample_count = size * 2;
const static int loop_length = 65528;
const static int segment_count = sample_count / loop_length;
void init()
{
printf("init\n");
::sound::transfer(start, size);
wait(); aica_sound.common.afsel_mslc_mobuf
= aica::afsel_mslc_mobuf::AFSEL(0)
| aica::afsel_mslc_mobuf::MSLC(0);
wait(); aica_sound.channel[0].LPCTL(1);
wait(); aica_sound.channel[0].PCMS(2); // adpcm
wait(); aica_sound.channel[0].LSA(0);
wait(); aica_sound.channel[0].LEA(loop_length);
wait(); aica_sound.channel[0].D2R(0x0);
wait(); aica_sound.channel[0].D1R(0x0);
wait(); aica_sound.channel[0].RR(0x1f);
wait(); aica_sound.channel[0].AR(0x1f);
wait(); aica_sound.channel[0].OCT(0);
wait(); aica_sound.channel[0].FNS(0);
wait(); aica_sound.channel[0].DISDL(0xf);
wait(); aica_sound.channel[0].DIPAN(0x0);
wait(); aica_sound.channel[0].KYONB(0);
wait(); aica_sound.channel[0].KYONEX(1);
}
void interrupt()
{
static int segment = 0;
wait();
int lp_sgc_eg = aica_sound.common.lp_sgc_eg;
if (aica::lp_sgc_eg::SGC(lp_sgc_eg) == 3) { // release
if (segment != 0)
return;
printf("start\n");
wait(); aica_sound.channel[0].SA(0);
wait(); aica_sound.channel[0].KYONB(1);
wait(); aica_sound.channel[0].KYONEX(1);
return;
}
if (aica::lp_sgc_eg::LP(lp_sgc_eg)) {
segment += 1;
if (segment >= segment_count) {
wait(); aica_sound.channel[0].KYONB(0);
wait(); aica_sound.channel[0].KYONEX(1);
return;
}
printf("loop %d\n", segment);
int sa = 0 + (loop_length / 2) * segment;
wait(); aica_sound.channel[0].SA(sa);
}
}
}

8
src/scene/logo/sound.hpp Normal file
View File

@ -0,0 +1,8 @@
#pragma once
namespace scene::logo::sound {
void init();
void interrupt();
}

22
src/scene/scene.cpp Normal file
View File

@ -0,0 +1,22 @@
#include "graphics.hpp"
#include "scene/scene.hpp"
#include "scene/tracker/scene.hpp"
#include "scene/logo/scene.hpp"
static const scene::scene scenes[] = {
scene::tracker::scene,
scene::logo::scene,
};
namespace scene {
const scene * current_scene = &scenes[1];
void scene_init()
{
graphics_scene_init(&current_scene->opb_size);
if (current_scene->init != nullptr)
current_scene->init();
}
}

View File

@ -1,13 +1,18 @@
#pragma once #pragma once
#include "holly/ta_parameter.hpp"
#include "holly/region_array.hpp" #include "holly/region_array.hpp"
namespace scene { namespace scene {
struct scene { struct scene {
const uint32_t ta_alloc; const uint32_t ta_alloc;
const struct opb_size opb_size; const struct opb_size opb_size;
void (* const transfer)(ta_parameter_writer& writer); void (* const transfer)(ta_parameter_writer& writer);
}; void (* const init)();
};
extern const scene * current_scene;
void scene_init();
} }

View File

@ -17,7 +17,7 @@
#include "assert.h" #include "assert.h"
void g2_aica_dma(uint32_t g2_address, uint32_t system_address, int length) static void g2_aica_dma(uint32_t g2_address, uint32_t system_address, int length)
{ {
using namespace dmac; using namespace dmac;
@ -44,7 +44,7 @@ void g2_aica_dma(uint32_t g2_address, uint32_t system_address, int length)
g2_if.ADST = 1; // start G2-AICA-DMA g2_if.ADST = 1; // start G2-AICA-DMA
} }
void g2_aica_dma_wait_complete() static void g2_aica_dma_wait_complete()
{ {
// wait for maple DMA completion // wait for maple DMA completion
while ((system.ISTNRM & istnrm::end_of_dma_aica_dma) == 0); while ((system.ISTNRM & istnrm::end_of_dma_aica_dma) == 0);
@ -52,7 +52,7 @@ void g2_aica_dma_wait_complete()
assert(g2_if.ADST == 0); assert(g2_if.ADST == 0);
} }
void writeback(void const * const buf, uint32_t size) static void writeback(void const * const buf, uint32_t size)
{ {
uint8_t const * const buf8 = reinterpret_cast<uint8_t const * const>(buf); uint8_t const * const buf8 = reinterpret_cast<uint8_t const * const>(buf);
@ -67,12 +67,18 @@ void writeback(void const * const buf, uint32_t size)
static uint8_t __attribute__((aligned(32))) zero[0x28c0] = {}; static uint8_t __attribute__((aligned(32))) zero[0x28c0] = {};
void sound_init(uint8_t * sample_data, int sample_data_ix) namespace sound {
void init()
{ {
printf("sound::init\n");
wait(); aica_sound.common.vreg_armrst = aica::vreg_armrst::ARMRST(1); wait(); aica_sound.common.vreg_armrst = aica::vreg_armrst::ARMRST(1);
wait(); aica_sound.common.dmea0_mrwinh = aica::dmea0_mrwinh::MRWINH(0b0111); wait(); aica_sound.common.dmea0_mrwinh = aica::dmea0_mrwinh::MRWINH(0b0111);
system.ISTNRM = istnrm::end_of_dma_aica_dma; system.ISTNRM = istnrm::end_of_dma_aica_dma;
writeback(zero, (sizeof (zero)));
// slot/common: 00700000 - 007028c0 (excludes vreg_armrst) // slot/common: 00700000 - 007028c0 (excludes vreg_armrst)
g2_aica_dma((uint32_t)0x00700000, (int)zero, 0x28c0); g2_aica_dma((uint32_t)0x00700000, (int)zero, 0x28c0);
g2_aica_dma_wait_complete(); g2_aica_dma_wait_complete();
@ -81,34 +87,6 @@ void sound_init(uint8_t * sample_data, int sample_data_ix)
g2_aica_dma((uint32_t)0x00703000, (int)zero, 0x15e0); g2_aica_dma((uint32_t)0x00703000, (int)zero, 0x15e0);
g2_aica_dma_wait_complete(); g2_aica_dma_wait_complete();
for (int i = 0; i < 16; i++) {
serial::hexlify(&sample_data[i * 16], 16);
}
printf("transfer %08x %08x %d\n", (int)aica_wave_memory, (int)sample_data, sample_data_ix);
// wave memory
int size = (sample_data_ix + 31) & (~31);
writeback(sample_data, size);
system.ISTERR = 0xffffffff;
g2_aica_dma((int)aica_wave_memory, (int)sample_data, size);
g2_aica_dma_wait_complete();
printf("sar0 %08x\n", sh7091.DMAC.SAR0);
printf("dar0 %08x\n", sh7091.DMAC.DAR0);
printf("dmatcr0 %08x\n", sh7091.DMAC.DMATCR0);
printf("chcr0 %08x\n", sh7091.DMAC.CHCR0);
printf("isterr %08x\n", system.ISTERR);
for (int i = 0; i < 16; i++) {
volatile uint8_t * s = &((volatile uint8_t*)aica_wave_memory)[i * 16];
for (int j = 0; j < 16; j++) {
wait();
serial::hexlify(s[j]);
serial::character(' ');
}
serial::character('\n');
}
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++) {
@ -142,6 +120,8 @@ void sound_init(uint8_t * sample_data, int sample_data_ix)
wait(); aica_sound.channel[i].LPOFF(1); wait(); aica_sound.channel[i].LPOFF(1);
} }
wait(); aica_sound.channel[0].KYONEX(1);
wait(); aica_sound.common.mono_mem8mb_dac18b_ver_mvol = wait(); aica_sound.common.mono_mem8mb_dac18b_ver_mvol =
aica::mono_mem8mb_dac18b_ver_mvol::MONO(0) // enable panpots 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::MEM8MB(0) // 16Mbit SDRAM
@ -149,19 +129,6 @@ void sound_init(uint8_t * sample_data, int sample_data_ix)
| aica::mono_mem8mb_dac18b_ver_mvol::MVOL(0xc) // volume | 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;
sh7091.TMU.TCR0
= tmu::tcr0::UNIE
| tmu::tcr0::tpsc::p_phi_256; // 256 / 50MHz = 5.12 μs ; underflows in ~1 hour
sh7091.TMU.TCNT0 = 0;
sh7091.TMU.TSTR = tmu::tstr::str0::counter_start;
sh7091.INTC.IPRA = intc::ipra::TMU0(1);
*/
wait(); aica_sound.common.tactl_tima = wait(); aica_sound.common.tactl_tima =
aica::tactl_tima::TACTL(0) // increment once every sample aica::tactl_tima::TACTL(0) // increment once every sample
| aica::tactl_tima::TIMA(0xffff) // interrupt after 1 counts | aica::tactl_tima::TIMA(0xffff) // interrupt after 1 counts
@ -170,3 +137,16 @@ void sound_init(uint8_t * sample_data, int sample_data_ix)
wait(); aica_sound.common.mcieb = (1 << 6); // interrupt timer A wait(); aica_sound.common.mcieb = (1 << 6); // interrupt timer A
wait(); aica_sound.common.mcire = (1 << 6); // interrupt timer A wait(); aica_sound.common.mcire = (1 << 6); // interrupt timer A
} }
void transfer(const void * sample_data, int sample_data_ix)
{
printf("aica transfer 0x%08x 0x%08x 0x%x\n", (int)aica_wave_memory, (int)sample_data, sample_data_ix);
int size = (sample_data_ix + 31) & (~31);
writeback(sample_data, size);
g2_aica_dma((int)aica_wave_memory, (int)sample_data, size);
g2_aica_dma_wait_complete();
}
}

View File

@ -13,4 +13,10 @@ static inline void wait()
}; };
} }
void sound_init(uint8_t * sample_data, int sample_data_ix); namespace sound {
void init();
void transfer(const void * sample_data, int sample_data_ix);
}

View File

@ -5,6 +5,9 @@ TEXTURE_OBJ = \
font/tandy1k.data.o \ font/tandy1k.data.o \
model/32bitlogo/colors.data.o model/32bitlogo/colors.data.o
PCM_OBJ = \
pcm/jingle.adpcm.o
XM_PLAYER_OBJ = \ XM_PLAYER_OBJ = \
$(LIB)/holly/core.o \ $(LIB)/holly/core.o \
$(LIB)/holly/region_array.o \ $(LIB)/holly/region_array.o \
@ -26,10 +29,12 @@ XM_PLAYER_OBJ = \
src/framebuffer.o \ src/framebuffer.o \
src/texture.o \ src/texture.o \
src/graphics_primitive.o \ src/graphics_primitive.o \
src/scene/scene.o \
src/scene/tracker/scene.o \ src/scene/tracker/scene.o \
src/scene/tracker/channel_status.o \ src/scene/tracker/channel_status.o \
src/scene/tracker/notes.o \ src/scene/tracker/notes.o \
src/scene/logo/scene.o src/scene/logo/scene.o \
src/scene/logo/sound.o
xm_player.elf: LDSCRIPT = $(LIB)/main.lds xm_player.elf: LDSCRIPT = $(LIB)/main.lds
xm_player.elf: $(START_OBJ) $(XM_PLAYER_OBJ) $(TEXTURE_OBJ) $(XM_OBJ) $(LIBGCC) xm_player.elf: $(START_OBJ) $(XM_PLAYER_OBJ) $(TEXTURE_OBJ) $(XM_OBJ) $(PCM_OBJ) $(LIBGCC)