add emulator detection, xm player
This commit is contained in:
parent
efb03b9370
commit
bb10713143
25
demo.mk
25
demo.mk
@ -1,5 +1,6 @@
|
||||
FONT_OBJ = \
|
||||
font/ter_u12n.data.o
|
||||
font/ter_u12n.data.o \
|
||||
font/ter_u32n.data.o
|
||||
|
||||
TEXTURE_OBJ = \
|
||||
texture/igh25_box_top_32.data.o \
|
||||
@ -21,6 +22,7 @@ TEXTURE_OBJ = \
|
||||
texture/turning/frame0006_128.data.o
|
||||
DEMO_OBJ = \
|
||||
$(LIB)/holly/core.o \
|
||||
$(LIB)/holly/video_output.o \
|
||||
$(LIB)/holly/region_array.o \
|
||||
$(LIB)/holly/background.o \
|
||||
$(LIB)/holly/ta_fifo_polygon_converter.o \
|
||||
@ -37,6 +39,8 @@ DEMO_OBJ = \
|
||||
src/platform/input.o \
|
||||
src/platform/texture.o \
|
||||
src/platform/font.o \
|
||||
src/platform/detect_emulator.o \
|
||||
reference/reference_render.data.o \
|
||||
src/demo/ballistics.o \
|
||||
src/demo/bridge.o \
|
||||
src/demo/sailboat.o \
|
||||
@ -48,7 +52,21 @@ DEMO_OBJ = \
|
||||
src/physics/particle_contact.o \
|
||||
src/physics/body.o \
|
||||
src/physics/force_generator.o \
|
||||
src/physics/collide.o
|
||||
src/physics/collide.o \
|
||||
src/xm_player/sound.o \
|
||||
src/xm_player/interpreter.o \
|
||||
src/xm_player/playlist.o \
|
||||
src/xm_player/cover.o \
|
||||
src/xm_player/xm.o \
|
||||
src/xm_player/malloc.o \
|
||||
xm/CloudsAhead.xm.o \
|
||||
xm/CottageFantasy.xm.o \
|
||||
xm/ForestAtTwilight.xm.o \
|
||||
xm/RedBlossom.xm.o \
|
||||
xm/SpringWaltz.xm.o \
|
||||
xm/SummerDreamsDemoTrackv4.xm.o \
|
||||
xm/TheClockOfElery.xm.o \
|
||||
xm/TheMountainsOfElmindeer.xm.o
|
||||
|
||||
demo.elf: LDSCRIPT = $(LIB)/main.lds
|
||||
demo.elf: $(START_OBJ) $(DEMO_OBJ) $(FONT_OBJ) $(TEXTURE_OBJ) $(LIBGCC)
|
||||
@ -67,3 +85,6 @@ texture/turning/%.data: texture/turning/%.png
|
||||
|
||||
font/ter_u12n.data:
|
||||
$(LIB)/tools/ttf_bitmap2 20 7f 128 64 /usr/share/fonts/terminus/ter-u12n.otb $@ > /dev/null
|
||||
|
||||
font/ter_u32n.data:
|
||||
$(LIB)/tools/ttf_bitmap2 20 7f 256 256 /usr/share/fonts/terminus/ter-u32n.otb $@ > /dev/null
|
||||
|
15
font/ter_u32n.data.h
Normal file
15
font/ter_u32n.data.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern uint32_t _binary_font_ter_u32n_data_start __asm("_binary_font_ter_u32n_data_start");
|
||||
extern uint32_t _binary_font_ter_u32n_data_end __asm("_binary_font_ter_u32n_data_end");
|
||||
extern uint32_t _binary_font_ter_u32n_data_size __asm("_binary_font_ter_u32n_data_size");
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
15
reference/reference_render.data.h
Normal file
15
reference/reference_render.data.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern uint32_t _binary_reference_reference_render_data_start __asm("_binary_reference_reference_render_data_start");
|
||||
extern uint32_t _binary_reference_reference_render_data_end __asm("_binary_reference_reference_render_data_end");
|
||||
extern uint32_t _binary_reference_reference_render_data_size __asm("_binary_reference_reference_render_data_size");
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -25,10 +25,10 @@ namespace demo {
|
||||
}
|
||||
|
||||
mat4x4 view_trans
|
||||
= translate(vec3(-0.5, 0.5, 0.75))
|
||||
* rotate_x(pi / 4)
|
||||
* rotate_y(pi / 4)
|
||||
* scale(vec3(-1, -1, 1));
|
||||
= translate(vec3(0.5, -0.5, 0.75))
|
||||
* rotate_x(-pi / 4)
|
||||
* rotate_y(-pi / 4)
|
||||
* scale(vec3(1, 1, 1));
|
||||
|
||||
return view_trans;
|
||||
}
|
||||
|
@ -86,9 +86,9 @@ namespace demo {
|
||||
|
||||
mat4x4 view_trans
|
||||
= translate(vec3(0.0, 0.0, 0.5))
|
||||
* rotate_x(pi / 4)
|
||||
* rotate_y(pi / 12)
|
||||
* scale(vec3(-1, -1, 1));
|
||||
* rotate_x(-pi / 4)
|
||||
* rotate_y(-pi / 12)
|
||||
* scale(vec3(1, 1, 1));
|
||||
|
||||
return view_trans;
|
||||
}
|
||||
|
@ -3,12 +3,15 @@
|
||||
|
||||
#include "platform/graphics_primitive.hpp"
|
||||
#include "platform/font.hpp"
|
||||
#include "platform/emulator_detected.hpp"
|
||||
|
||||
#include "demo/lizard/main.hpp"
|
||||
#include "demo/lizard/levels.hpp"
|
||||
|
||||
#include "texture/igh25_box_top_32.data.h"
|
||||
|
||||
#include "xm_player/playlist.hpp"
|
||||
|
||||
#include "assert.h"
|
||||
|
||||
extern float alpha_mul;
|
||||
@ -45,19 +48,43 @@ namespace demo {
|
||||
}
|
||||
}
|
||||
|
||||
world::platform * find_end_platform(world::level& level)
|
||||
{
|
||||
float end_x = 81;
|
||||
float end_z = 212;
|
||||
for (int i = 0; i < level.platforms_length; i++) {
|
||||
world::platform * p = &level.platforms[i];
|
||||
if (p->position.x == end_x && p->position.z == end_z) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mat4x4 lizard::init()
|
||||
{
|
||||
vx = -pi / 4;
|
||||
vy = -pi / 4;
|
||||
platform_touch_count = 0;
|
||||
|
||||
vx = -pi / 3.5;
|
||||
vy = -pi / 8;
|
||||
|
||||
current_level = &demo::igh25_map1_level;
|
||||
|
||||
world::table_build(*current_level);
|
||||
end_platform = find_end_platform(*current_level);
|
||||
|
||||
lizard_position = {2.5, 1, 2.5};
|
||||
//lizard_position = {218.5, 0, 65.5};
|
||||
//lizard_position = {81, 100, 212};
|
||||
lizard_velocity = {0, 0, 0};
|
||||
|
||||
if (!emulator_detected) {
|
||||
emulator_detected_hud_frames = 60 * 30;
|
||||
playlist::next();
|
||||
} else {
|
||||
emulator_detected_hud_frames = 0;
|
||||
}
|
||||
|
||||
return view_trans;
|
||||
}
|
||||
|
||||
@ -73,7 +100,7 @@ namespace demo {
|
||||
{
|
||||
lizard_rotation *= 0.8;
|
||||
|
||||
lizard_turning_frame += lizard_rotation * 10;
|
||||
lizard_turning_frame += lizard_rotation * 5;
|
||||
|
||||
lizard_heading += lizard_rotation;
|
||||
|
||||
@ -88,14 +115,21 @@ namespace demo {
|
||||
lizard_velocity.y *= 0.99;
|
||||
lizard_velocity.z *= 0.8;
|
||||
|
||||
lizard_walking_frame += magnitude(lizard_velocity) * 15;
|
||||
vec2 frame_velocity = vec2(lizard_velocity.x, lizard_velocity.z);
|
||||
lizard_walking_frame += magnitude(frame_velocity) * 5;
|
||||
|
||||
world::platform * p = lizard_collide();
|
||||
collided = (p != nullptr);
|
||||
if (!collided) {
|
||||
lizard_velocity.y -= 0.01;
|
||||
} else {
|
||||
last_platform = p;
|
||||
if (p->touched == false) {
|
||||
platform_touch_count += 1;
|
||||
last_platform1 = last_platform;
|
||||
last_platform = p;
|
||||
}
|
||||
|
||||
p->touched = true;
|
||||
//lizard_position.y -= -lizard_velocity.y;
|
||||
float pp = p->position.y + p->scale.y * 0.5;
|
||||
//lizard_velocity.y *= 0.1;
|
||||
@ -115,7 +149,17 @@ namespace demo {
|
||||
|
||||
void lizard::y()
|
||||
{
|
||||
//lizard_velocity.y += 0.01;
|
||||
if (last_platform1 == nullptr)
|
||||
init();
|
||||
else {
|
||||
lizard_velocity = {0, 0, 0};
|
||||
lizard_position = last_platform1->position;
|
||||
lizard_position.y += 0.5;
|
||||
if (last_platform->touched == true)
|
||||
platform_touch_count -= 1;
|
||||
last_platform->touched = false;
|
||||
last_platform = last_platform1;
|
||||
}
|
||||
}
|
||||
|
||||
void lizard::a()
|
||||
@ -128,13 +172,6 @@ namespace demo {
|
||||
|
||||
void lizard::start()
|
||||
{
|
||||
if (last_platform == nullptr)
|
||||
init();
|
||||
else {
|
||||
lizard_velocity = {0, 0, 0};
|
||||
lizard_position = last_platform->position;
|
||||
lizard_position.y += 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
void lizard::ra()
|
||||
@ -201,44 +238,113 @@ namespace demo {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char * emulator_speech[] = {
|
||||
"This counterfeit Dreamcast failed a",
|
||||
"CORE rasterization test.",
|
||||
"",
|
||||
"Dreamcast emulator behavior is highly",
|
||||
"divergent from genuine Sega Dreamcast",
|
||||
"hardware.",
|
||||
"",
|
||||
"Some emulator authors deliberately",
|
||||
"choose to forgo accuracy, and instead ",
|
||||
"develop a distinct and unrelated",
|
||||
"fantasy-platform."
|
||||
};
|
||||
const int emulator_speech_lines = (sizeof (emulator_speech)) / (sizeof (emulator_speech[0]));
|
||||
|
||||
void lizard::draw_hud(ta_parameter_writer& writer)
|
||||
{
|
||||
const int title_length = 8;
|
||||
const int title_width_2 = (font::ter_u12n.hori_advance * title_length) >> 1;
|
||||
const int framebuffer_width_2 = framebuffer::framebuffer.px_width >> 1;
|
||||
const int x = framebuffer_width_2 - title_width_2;
|
||||
vec3 center_p = vec3(x, 5, 10);
|
||||
const int framebuffer_height_2 = framebuffer::framebuffer.px_height >> 1;
|
||||
vec3 center_p = vec3(0, 5, 10);
|
||||
|
||||
font::ter_u12n.global(writer);
|
||||
font::ter_u12n.draw_string(writer, center_p, "platform", 0xffffffff);
|
||||
{
|
||||
const int title_length = 12;
|
||||
const int title_width_2 = (font::ter_u12n.hori_advance * title_length) >> 1;
|
||||
const int x = framebuffer_width_2 - title_width_2;
|
||||
center_p.x = x;
|
||||
font::ter_u12n.draw_string(writer, center_p, "demo: lizard", 0xffffffff);
|
||||
}
|
||||
center_p.y += font::ter_u12n.height;
|
||||
{
|
||||
const int title_length = 46;
|
||||
const int title_width_2 = (font::ter_u12n.hori_advance * title_length) >> 1;
|
||||
const int x = framebuffer_width_2 - title_width_2;
|
||||
center_p.x = x;
|
||||
font::ter_u12n.draw_string(writer, center_p, "objective: find and touch the glowing platform", 0xffffffff);
|
||||
}
|
||||
|
||||
vec3 status_p = vec3(10, 10, 10);
|
||||
/*
|
||||
if (collided)
|
||||
font::ter_u12n.draw_string(writer, status_p, "collide", 0xffffffff);
|
||||
else
|
||||
font::ter_u12n.draw_string(writer, status_p, "air", 0xffffffff);
|
||||
*/
|
||||
|
||||
font::ter_u12n.draw_float(writer, status_p, (float)lizard_position.x, 0xffffffff, 10);
|
||||
status_p.y += 12;
|
||||
font::ter_u12n.draw_float(writer, status_p, (float)lizard_position.y, 0xffffffff, 10);
|
||||
status_p.y += 12;
|
||||
font::ter_u12n.draw_float(writer, status_p, (float)lizard_position.z, 0xffffffff, 10);
|
||||
vec3 status_p = vec3(10, framebuffer::framebuffer.px_height - 24, 10);
|
||||
font::ter_u12n.draw_string(writer, status_p, "score:", 0xffffffff);
|
||||
//font::ter_u12n.draw_float(writer, status_p, (float)last_drawn_frame, 0xffffffff, 10);
|
||||
status_p.x += font::ter_u12n.hori_advance * 7;
|
||||
font::ter_u12n.draw_int(writer, status_p, (float)platform_touch_count, 0xffffffff, 4);
|
||||
|
||||
const int height_2 = (font::ter_u32n.height * (emulator_speech_lines + 2)) >> 1;
|
||||
const int y = framebuffer_height_2 - height_2;
|
||||
vec3 center_e = vec3(8, y, 10);
|
||||
if (emulator_detected_hud_frames < 60 * 30) {
|
||||
font::ter_u32n.global(writer);;
|
||||
for (int i = 0; i < emulator_speech_lines; i++) {
|
||||
font::ter_u32n.draw_string(writer, center_e, emulator_speech[i], 0xffffffff);
|
||||
font::ter_u32n.draw_string(writer, {center_e.x + 2, center_e.y + 0, center_e.z - 1}, emulator_speech[i], 0x00000000);
|
||||
font::ter_u32n.draw_string(writer, {center_e.x - 2, center_e.y + 0, center_e.z - 1}, emulator_speech[i], 0x00000000);
|
||||
font::ter_u32n.draw_string(writer, {center_e.x + 0, center_e.y + 2, center_e.z - 1}, emulator_speech[i], 0x00000000);
|
||||
font::ter_u32n.draw_string(writer, {center_e.x + 0, center_e.y - 2, center_e.z - 1}, emulator_speech[i], 0x00000000);
|
||||
center_e.y += font::ter_u32n.height;
|
||||
}
|
||||
center_e.y += font::ter_u32n.height;
|
||||
int timeout = 30 - (emulator_detected_hud_frames / 60);
|
||||
font::ter_u32n.draw_int(writer, center_e, timeout, 0xffffffff, 0);
|
||||
emulator_detected_hud_frames += 1;
|
||||
if (emulator_detected_hud_frames >= 60 * 30) {
|
||||
playlist::next();
|
||||
}
|
||||
} else if (end_platform != nullptr && end_platform->touched) {
|
||||
font::ter_u32n.global(writer);
|
||||
const int title_length = 18;
|
||||
const int title_width_2 = (font::ter_u12n.hori_advance * title_length) >> 1;
|
||||
const int x = framebuffer_width_2 - title_width_2;
|
||||
center_e.x = x;
|
||||
|
||||
const char * s = "objective complete";
|
||||
font::ter_u32n.draw_string(writer, center_e, s, 0xffffffff);
|
||||
font::ter_u32n.draw_string(writer, {center_e.x + 2, center_e.y + 0, center_e.z - 1}, s, 0x00000000);
|
||||
font::ter_u32n.draw_string(writer, {center_e.x - 2, center_e.y + 0, center_e.z - 1}, s, 0x00000000);
|
||||
font::ter_u32n.draw_string(writer, {center_e.x + 0, center_e.y + 2, center_e.z - 1}, s, 0x00000000);
|
||||
font::ter_u32n.draw_string(writer, {center_e.x + 0, center_e.y - 2, center_e.z - 1}, s, 0x00000000);
|
||||
}
|
||||
}
|
||||
|
||||
void lizard::draw_platform(ta_parameter_writer& writer, const mat4x4& trans, const world::platform& p)
|
||||
void lizard::draw_platform(ta_parameter_writer& writer, const mat4x4& trans, const world::platform * p)
|
||||
{
|
||||
mat4x4 t
|
||||
= trans
|
||||
* translate(p.position)
|
||||
* scale(p.scale);
|
||||
* translate(p->position)
|
||||
* scale(p->scale);
|
||||
|
||||
float intensity_offset;
|
||||
vec3 base_color;
|
||||
if (p == end_platform) {
|
||||
base_color = vec3(0.5, 1, 0.5);
|
||||
intensity_offset = sin(end_platform_tick * 0.1f) * 0.4f + 0.5f;
|
||||
end_platform_tick += 1;
|
||||
} else if (p->touched) {
|
||||
intensity_offset = 0.5;
|
||||
base_color = vec3(1, 1, 1);
|
||||
} else {
|
||||
intensity_offset = 0;
|
||||
base_color = vec3(1, 0.5, 0.5);
|
||||
}
|
||||
|
||||
draw_textured_cube(writer,
|
||||
t,
|
||||
p.scale,
|
||||
texture::cube_type_1);
|
||||
p->scale,
|
||||
texture::cube_type_1,
|
||||
base_color,
|
||||
intensity_offset);
|
||||
}
|
||||
|
||||
void lizard::draw_lizard(ta_parameter_writer& writer, const mat4x4& trans)
|
||||
@ -253,7 +359,7 @@ namespace demo {
|
||||
t,
|
||||
vec3(1, 0.5, 0));
|
||||
*/
|
||||
if (abs(lizard_rotation) > 0.1) {
|
||||
if (abs(lizard_rotation) > 0.01) {
|
||||
int frame = ((int)lizard_turning_frame) % lizard_turning_frames_count;
|
||||
if (frame < 0)
|
||||
frame = lizard_turning_frames_count + frame;
|
||||
@ -290,7 +396,7 @@ namespace demo {
|
||||
//draw_axis(writer, trans * translate(lizard_position));
|
||||
|
||||
for (int i = 0; i < current_level->platforms_length; i++) {
|
||||
draw_platform(writer, trans, current_level->platforms[i]);
|
||||
draw_platform(writer, trans, ¤t_level->platforms[i]);
|
||||
}
|
||||
|
||||
writer.append<ta_global_parameter::end_of_list>() =
|
||||
|
@ -7,14 +7,19 @@ namespace demo {
|
||||
|
||||
struct lizard : scene {
|
||||
world::level * current_level;
|
||||
world::platform * last_platform1;
|
||||
world::platform * last_platform;
|
||||
world::platform * end_platform;
|
||||
vec3 lizard_position;
|
||||
vec3 lizard_velocity;
|
||||
float lizard_heading;
|
||||
float lizard_rotation;
|
||||
float lizard_walking_frame;
|
||||
float lizard_turning_frame;
|
||||
int platform_touch_count;
|
||||
int end_platform_tick;
|
||||
|
||||
int emulator_detected_hud_frames;
|
||||
bool collided;
|
||||
|
||||
mat4x4 view_trans;
|
||||
@ -43,7 +48,7 @@ namespace demo {
|
||||
world::platform * lizard_collide();
|
||||
|
||||
void draw_hud(ta_parameter_writer& writer);
|
||||
void draw_platform(ta_parameter_writer& writer, const mat4x4& trans, const world::platform& p);
|
||||
void draw_platform(ta_parameter_writer& writer, const mat4x4& trans, const world::platform * p);
|
||||
void draw_lizard(ta_parameter_writer& writer, const mat4x4& trans);
|
||||
void draw(ta_parameter_writer& writer, const mat4x4& trans) override;
|
||||
};
|
||||
|
@ -79,6 +79,7 @@ namespace demo::world {
|
||||
int scale_z = (int)level.platforms[i].scale.z;
|
||||
float scale_x_2 = level.platforms[i].scale.x * 0.5;
|
||||
float scale_z_2 = level.platforms[i].scale.z * 0.5;
|
||||
level.platforms[i].touched = false;
|
||||
|
||||
for (int xo = 0; xo < scale_x; xo++) {
|
||||
for (int zo = 0; zo < scale_z; zo++) {
|
||||
|
@ -9,6 +9,7 @@ namespace demo::world {
|
||||
struct platform {
|
||||
vec3 position;
|
||||
vec3 scale;
|
||||
bool touched;
|
||||
};
|
||||
|
||||
struct level {
|
||||
|
254
src/platform/detect_emulator.cpp
Normal file
254
src/platform/detect_emulator.cpp
Normal file
@ -0,0 +1,254 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "memorymap.hpp"
|
||||
|
||||
#include "holly/background.hpp"
|
||||
#include "holly/core.hpp"
|
||||
#include "holly/isp_tsp.hpp"
|
||||
#include "holly/object_list_data.hpp"
|
||||
#include "holly/region_array.hpp"
|
||||
#include "holly/texture_memory_alloc9.hpp"
|
||||
|
||||
#include "math/float_types.hpp"
|
||||
|
||||
#include "reference/reference_render.data.h"
|
||||
|
||||
const struct opb_size opb_size = { .opaque = 0
|
||||
, .opaque_modifier = 8 * 4
|
||||
, .translucent = 0
|
||||
, .translucent_modifier = 0
|
||||
, .punch_through = 8 * 4
|
||||
};
|
||||
|
||||
static const int framebuffer_width = 32;
|
||||
static const int framebuffer_height = 32;
|
||||
static const int tile_width = framebuffer_width / 32;
|
||||
static const int tile_height = framebuffer_height / 32;
|
||||
|
||||
struct triangle_parameter_vertex {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
uint32_t color1;
|
||||
uint32_t color2;
|
||||
};
|
||||
|
||||
struct triangle_parameter {
|
||||
uint32_t isp_tsp_instruction_word;
|
||||
uint32_t tsp_instruction_word_0;
|
||||
uint32_t texture_control_word_0;
|
||||
uint32_t tsp_instruction_word_1;
|
||||
uint32_t texture_control_word_1;
|
||||
triangle_parameter_vertex a;
|
||||
triangle_parameter_vertex b;
|
||||
triangle_parameter_vertex c;
|
||||
};
|
||||
|
||||
struct modifier_volume_parameter_vertex {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
};
|
||||
|
||||
struct modifier_volume_parameter {
|
||||
uint32_t isp_tsp_instruction_word;
|
||||
uint32_t pad1;
|
||||
uint32_t pad2;
|
||||
modifier_volume_parameter_vertex a;
|
||||
modifier_volume_parameter_vertex b;
|
||||
modifier_volume_parameter_vertex c;
|
||||
};
|
||||
|
||||
template <int N>
|
||||
struct object_pointer_block {
|
||||
uint32_t pointer[N];
|
||||
};
|
||||
static_assert((sizeof (object_pointer_block<8>)) == 32);
|
||||
|
||||
using vec2i = vec<2, int>;
|
||||
|
||||
static const int opaque_modifier_start = 0;
|
||||
static const int punch_through_start = (sizeof (triangle_parameter));
|
||||
|
||||
static volatile uint8_t * const object_list = (volatile uint8_t *)(&texture_memory32[texture_memory_alloc.object_list.start / 4]);
|
||||
static volatile uint8_t * const isp_tsp_parameters = (volatile uint8_t *)(&texture_memory32[texture_memory_alloc.isp_tsp_parameters.start / 4]);
|
||||
|
||||
static inline void transfer_object_list(volatile uint8_t * mem)
|
||||
{
|
||||
auto blocks = reinterpret_cast<volatile object_pointer_block<8> *>(mem);
|
||||
|
||||
{ // opaque modifier
|
||||
auto& block = blocks[0];
|
||||
block.pointer[0] = object_list_data::pointer_type::triangle_array
|
||||
| object_list_data::triangle_array::number_of_triangles(0)
|
||||
| object_list_data::triangle_array::skip(0)
|
||||
| object_list_data::triangle_array::start(opaque_modifier_start / 4);
|
||||
|
||||
block.pointer[1] = object_list_data::pointer_type::object_pointer_block_link
|
||||
| object_list_data::object_pointer_block_link::end_of_list;
|
||||
}
|
||||
|
||||
{ // punch through
|
||||
auto& block = blocks[1];
|
||||
block.pointer[0] = object_list_data::pointer_type::triangle_array
|
||||
| object_list_data::triangle_array::number_of_triangles(0)
|
||||
| object_list_data::triangle_array::shadow
|
||||
| object_list_data::triangle_array::skip(1)
|
||||
| object_list_data::triangle_array::start(punch_through_start / 4);
|
||||
|
||||
block.pointer[1] = object_list_data::pointer_type::object_pointer_block_link
|
||||
| object_list_data::object_pointer_block_link::end_of_list;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void transfer_isp_tsp_parameters(volatile uint8_t * mem,
|
||||
const vec3& ap, const vec2i& ac,
|
||||
const vec3& bp, const vec2i& bc,
|
||||
const vec3& cp, const vec2i& cc)
|
||||
{
|
||||
auto params = reinterpret_cast<volatile triangle_parameter *>(mem);
|
||||
params->isp_tsp_instruction_word = isp_tsp_instruction_word::depth_compare_mode::greater
|
||||
| isp_tsp_instruction_word::culling_mode::no_culling
|
||||
| isp_tsp_instruction_word::gouraud_shading;
|
||||
|
||||
params->tsp_instruction_word_0 = tsp_instruction_word::src_alpha_instr::one
|
||||
| tsp_instruction_word::dst_alpha_instr::zero
|
||||
| tsp_instruction_word::fog_control::no_fog;
|
||||
|
||||
params->texture_control_word_0 = 0;
|
||||
|
||||
params->tsp_instruction_word_1 = tsp_instruction_word::src_alpha_instr::one
|
||||
| tsp_instruction_word::dst_alpha_instr::one
|
||||
| tsp_instruction_word::fog_control::no_fog;
|
||||
|
||||
params->texture_control_word_1 = 0;
|
||||
|
||||
params->a.x = ap.x;
|
||||
params->a.y = ap.y;
|
||||
params->a.z = ap.z;
|
||||
params->a.color1 = ac.x;
|
||||
params->a.color2 = ac.y;
|
||||
|
||||
params->b.x = bp.x;
|
||||
params->b.y = bp.y;
|
||||
params->b.z = bp.z;
|
||||
params->b.color1 = bc.x;
|
||||
params->b.color2 = bc.y;
|
||||
|
||||
params->c.x = cp.x;
|
||||
params->c.y = cp.y;
|
||||
params->c.z = cp.z;
|
||||
params->c.color1 = cc.x;
|
||||
params->c.color2 = cc.y;
|
||||
}
|
||||
|
||||
static inline void transfer_modifier_volume_isp_tsp_parameters(volatile uint8_t * mem,
|
||||
const vec3& ap,
|
||||
const vec3& bp,
|
||||
const vec3& cp)
|
||||
{
|
||||
auto params = reinterpret_cast<volatile modifier_volume_parameter *>(mem);
|
||||
params->isp_tsp_instruction_word = isp_tsp_instruction_word::volume_instruction::inside_last_polygon
|
||||
| isp_tsp_instruction_word::culling_mode::no_culling;
|
||||
|
||||
params->pad1 = 0;
|
||||
params->pad2 = 0;
|
||||
|
||||
params->a.x = ap.x;
|
||||
params->a.y = ap.y;
|
||||
params->a.z = ap.z;
|
||||
|
||||
params->b.x = bp.x;
|
||||
params->b.y = bp.y;
|
||||
params->b.z = bp.z;
|
||||
|
||||
params->c.x = cp.x;
|
||||
params->c.y = cp.y;
|
||||
params->c.z = cp.z;
|
||||
}
|
||||
|
||||
static void transfer_punch_through()
|
||||
{
|
||||
vec3 ap = {18.2192f, 1.0f, 0.01f};
|
||||
vec3 bp = {27.8808f, 25.4219f, 0.01f};
|
||||
vec3 cp = { 1.9f, 21.5781f, 0.01f};
|
||||
|
||||
vec2i ac = {0x0000ff, 0xff0000};
|
||||
vec2i bc = {0x00ff00, 0x0000ff};
|
||||
vec2i cc = {0xff0000, 0x00ff00};
|
||||
|
||||
transfer_isp_tsp_parameters(&isp_tsp_parameters[punch_through_start],
|
||||
ap, ac,
|
||||
bp, bc,
|
||||
cp, cc);
|
||||
}
|
||||
|
||||
static void transfer_opaque_modifier()
|
||||
{
|
||||
vec3 ap = { 0.0f, -50.0f, 0.1f};
|
||||
vec3 bp = {100.0f, 0.0f, 0.1f};
|
||||
vec3 cp = { 0.0f, 50.0f, 0.1f};
|
||||
|
||||
transfer_modifier_volume_isp_tsp_parameters(&isp_tsp_parameters[opaque_modifier_start],
|
||||
ap,
|
||||
bp,
|
||||
cp);
|
||||
}
|
||||
|
||||
static void init_texture_memory()
|
||||
{
|
||||
region_array_multipass(tile_width,
|
||||
tile_height,
|
||||
&opb_size,
|
||||
1,
|
||||
texture_memory_alloc.region_array.start,
|
||||
texture_memory_alloc.object_list.start);
|
||||
|
||||
background_parameter2(texture_memory_alloc.background[0].start,
|
||||
0xff000000);
|
||||
|
||||
transfer_object_list(object_list);
|
||||
|
||||
transfer_punch_through();
|
||||
|
||||
transfer_opaque_modifier();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline bool compare_equal(T a, T b, int length)
|
||||
{
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (a[i] != b[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool detect_emulator()
|
||||
{
|
||||
init_texture_memory();
|
||||
|
||||
bool dither = true;
|
||||
core_start_render2(texture_memory_alloc.region_array.start,
|
||||
texture_memory_alloc.isp_tsp_parameters.start,
|
||||
texture_memory_alloc.background[0].start,
|
||||
texture_memory_alloc.framebuffer[0].start,
|
||||
framebuffer_width,
|
||||
dither);
|
||||
core_wait_end_of_render_video();
|
||||
|
||||
uint32_t * a = (uint32_t *)&_binary_reference_reference_render_data_start;
|
||||
uint32_t * b = (uint32_t *)&texture_memory32[texture_memory_alloc.framebuffer[0].start / 4];
|
||||
int length = 32 * 32 * 2 / 4;
|
||||
|
||||
int equal = compare_equal(a, b, length);
|
||||
|
||||
for (uint32_t i = 0; i < (8 * 1024 * 1024 / 32); i++) {
|
||||
asm volatile ("ocbp @%0"
|
||||
: // output
|
||||
: "r" (((uint32_t)texture_memory32) + (32 * i)) // input
|
||||
: "memory");
|
||||
}
|
||||
|
||||
return !equal;
|
||||
}
|
3
src/platform/detect_emulator.hpp
Normal file
3
src/platform/detect_emulator.hpp
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
bool detect_emulator();
|
3
src/platform/emulator_detected.hpp
Normal file
3
src/platform/emulator_detected.hpp
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
extern bool emulator_detected;
|
@ -107,6 +107,36 @@ namespace font {
|
||||
return length >= offset ? length : offset;
|
||||
}
|
||||
|
||||
static inline int format_int(char * s, int num)
|
||||
{
|
||||
int offset = 0;
|
||||
bool negative = num < 0;
|
||||
if (negative) {
|
||||
s[offset++] = '-';
|
||||
num = -num;
|
||||
}
|
||||
int whole = num;
|
||||
offset += unparse_base10_unsigned(&s[offset], whole, 0, 0);
|
||||
return offset;
|
||||
}
|
||||
|
||||
int face::draw_int(ta_parameter_writer& writer,
|
||||
const vec3& p,
|
||||
int num,
|
||||
uint32_t base_color,
|
||||
int length) const
|
||||
{
|
||||
char s[20];
|
||||
int offset = format_int(s, num);
|
||||
s[offset] = 0;
|
||||
float x = p.x;
|
||||
if (offset < length) {
|
||||
x += hori_advance * (length - offset);
|
||||
}
|
||||
draw_string(writer, {x, p.y, p.z}, s, base_color);
|
||||
return length >= offset ? length : offset;
|
||||
}
|
||||
|
||||
void face::draw_mat4(ta_parameter_writer& writer,
|
||||
const vec3& p,
|
||||
const mat4x4& mat,
|
||||
@ -139,4 +169,16 @@ namespace font {
|
||||
.height = 12,
|
||||
.row_stride = 21,
|
||||
};
|
||||
|
||||
const face ter_u32n = {
|
||||
.texture_size = tsp_instruction_word::texture_u_size::from_int(256)
|
||||
| tsp_instruction_word::texture_v_size::from_int(256),
|
||||
.texture_offset = texture::offset::ter_u32n,
|
||||
.texture_width = 256,
|
||||
.texture_height = 256,
|
||||
.hori_advance = 16,
|
||||
.width = 16,
|
||||
.height = 32,
|
||||
.row_stride = 16,
|
||||
};
|
||||
};
|
||||
|
@ -37,6 +37,12 @@ namespace font {
|
||||
}
|
||||
}
|
||||
|
||||
int draw_int(ta_parameter_writer& writer,
|
||||
const vec3& p,
|
||||
int num,
|
||||
uint32_t base_color,
|
||||
int length) const;
|
||||
|
||||
int draw_float(ta_parameter_writer& writer,
|
||||
const vec3& p,
|
||||
float num,
|
||||
@ -51,4 +57,5 @@ namespace font {
|
||||
};
|
||||
|
||||
extern const face ter_u12n;
|
||||
extern const face ter_u32n;
|
||||
}
|
||||
|
@ -13,6 +13,8 @@
|
||||
#include "holly/texture_memory_alloc9.hpp"
|
||||
#include "holly/ta_fifo_texture_memory_transfer.hpp"
|
||||
#include "holly/ta_fifo_polygon_converter.hpp"
|
||||
#include "holly/video_output.hpp"
|
||||
#include "dve.hpp"
|
||||
|
||||
#include "math/float_types.hpp"
|
||||
#include "math/transform.hpp"
|
||||
@ -22,6 +24,7 @@
|
||||
#include "platform/graphics.hpp"
|
||||
#include "platform/input.hpp"
|
||||
#include "platform/font.hpp"
|
||||
#include "platform/emulator_detected.hpp"
|
||||
|
||||
#include "demo/ballistics.hpp"
|
||||
#include "demo/bridge.hpp"
|
||||
@ -95,14 +98,21 @@ namespace graphics {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
demo::ballistics _ballistics;
|
||||
demo::bridge _bridge;
|
||||
demo::sailboat _sailboat;
|
||||
demo::collision _collision;
|
||||
*/
|
||||
//demo::ballistics _ballistics;
|
||||
//demo::bridge _bridge;
|
||||
//demo::sailboat _sailboat;
|
||||
//demo::collision _collision;
|
||||
demo::lizard _lizard;
|
||||
demo::scene * current_scene = &_lizard;
|
||||
|
||||
demo::scene * scenes[] = {
|
||||
&_lizard,
|
||||
//&_bridge,
|
||||
//&_ballistics
|
||||
};
|
||||
|
||||
int current_scene_ix = 0;
|
||||
|
||||
demo::scene * current_scene = scenes[current_scene_ix];
|
||||
|
||||
void draw()
|
||||
{
|
||||
@ -115,12 +125,10 @@ namespace graphics {
|
||||
{
|
||||
core_init();
|
||||
framebuffer::scaler_init();
|
||||
|
||||
view_trans = current_scene->init();
|
||||
|
||||
// read
|
||||
while (spg_status::vsync(holly.SPG_STATUS));
|
||||
while (!spg_status::vsync(holly.SPG_STATUS));
|
||||
//while (spg_status::vsync(holly.SPG_STATUS));
|
||||
//while (!spg_status::vsync(holly.SPG_STATUS));
|
||||
|
||||
system.LMMODE0 = 1; // 32-bit address space
|
||||
system.LMMODE1 = 1; // 32-bit address space
|
||||
@ -133,11 +141,28 @@ namespace graphics {
|
||||
720 * 480 * 2,
|
||||
0xc5f7c5f7);
|
||||
|
||||
framebuffer::spg_set_mode_640x480_vga();
|
||||
//framebuffer::spg_set_mode_640x480_vga();
|
||||
//framebuffer::spg_set_mode_720x480_vga();
|
||||
//framebuffer::init(720, 480,
|
||||
framebuffer::init(640, 480,
|
||||
texture_memory_alloc.framebuffer[0].start);
|
||||
uint32_t cable_type = video_output::get_cable_type();
|
||||
if (emulator_detected) {
|
||||
framebuffer::spg_set_mode_640x480_vga();
|
||||
framebuffer::init(640, 480,
|
||||
texture_memory_alloc.framebuffer[0].start);
|
||||
} else {
|
||||
switch (cable_type) {
|
||||
case pdtra::cable_type::vga:
|
||||
framebuffer::spg_set_mode_640x480_vga();
|
||||
framebuffer::init(640, 480,
|
||||
texture_memory_alloc.framebuffer[0].start);
|
||||
break;
|
||||
default:
|
||||
framebuffer::spg_set_mode_320x240_ntsc_ni();
|
||||
framebuffer::init(320, 240,
|
||||
texture_memory_alloc.framebuffer[0].start);
|
||||
break;
|
||||
}
|
||||
}
|
||||
core_param_init(texture_memory_alloc.region_array.start,
|
||||
texture_memory_alloc.isp_tsp_parameters.start,
|
||||
texture_memory_alloc.background[0].start,
|
||||
@ -165,6 +190,7 @@ namespace graphics {
|
||||
uint8_t b;
|
||||
uint8_t x;
|
||||
uint8_t y;
|
||||
uint8_t start;
|
||||
};
|
||||
static button_state last[4] = {};
|
||||
|
||||
@ -197,7 +223,7 @@ namespace graphics {
|
||||
|
||||
if (last[port_ix].a != a && a) current_scene->a();
|
||||
if (b) current_scene->b();
|
||||
if (x) current_scene->x();
|
||||
if (last[port_ix].x != x && x) current_scene->x();
|
||||
if (y) current_scene->y();
|
||||
|
||||
if (ra) current_scene->ra();
|
||||
@ -205,16 +231,23 @@ namespace graphics {
|
||||
if (da) current_scene->da();
|
||||
if (ua) current_scene->ua();
|
||||
|
||||
if (start) current_scene->start();
|
||||
//if (start) current_scene->start();
|
||||
|
||||
current_scene->analog(dl, dr, dx, dy);
|
||||
|
||||
//view_trans = rotate_y(dy) * view_trans;// * rotate_x(dx);
|
||||
|
||||
if (last[port_ix].start != start && start && x) {
|
||||
//current_scene_ix += 1;
|
||||
//current_scene = scenes[current_scene_ix % 3];
|
||||
//view_trans = current_scene->init(emulator);
|
||||
}
|
||||
|
||||
last[port_ix].a = a;
|
||||
last[port_ix].b = b;
|
||||
last[port_ix].x = x;
|
||||
last[port_ix].y = y;
|
||||
|
||||
current_scene->analog(dl, dr, dx, dy);
|
||||
|
||||
//view_trans = rotate_y(dy) * view_trans;// * rotate_x(dx);
|
||||
last[port_ix].start = start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ static inline float _intensity(const mat4x4& trans, const vec3& normal)
|
||||
|
||||
vec3 n = normal_multiply(trans, normal);
|
||||
float n_dot_l = dot(n, light_vec);
|
||||
float intensity = 0.5f;
|
||||
float intensity = 0.2f;
|
||||
if (n_dot_l > 0)
|
||||
intensity += 0.5f * n_dot_l * (inverse_length(n) * inverse_length(light_vec));
|
||||
return intensity;
|
||||
@ -265,7 +265,9 @@ void draw_halfspace(ta_parameter_writer& writer,
|
||||
void draw_textured_cube(ta_parameter_writer& writer,
|
||||
const mat4x4& trans,
|
||||
const vec3& scale,
|
||||
const texture::cube_texture_offsets& offsets)
|
||||
const texture::cube_texture_offsets& offsets,
|
||||
const vec3& base_color,
|
||||
float intensity_offset)
|
||||
{
|
||||
static const vec3 position[] = {
|
||||
{-0.5, -0.5, 0.5},
|
||||
@ -323,7 +325,7 @@ void draw_textured_cube(ta_parameter_writer& writer,
|
||||
}
|
||||
|
||||
int texture_uv_size
|
||||
= tsp_instruction_word::texture_shading_instruction::decal
|
||||
= tsp_instruction_word::texture_shading_instruction::modulate
|
||||
| tsp_instruction_word::src_alpha_instr::one
|
||||
| tsp_instruction_word::dst_alpha_instr::zero
|
||||
| tsp_instruction_word::texture_u_size::from_int(32)
|
||||
@ -332,7 +334,7 @@ void draw_textured_cube(ta_parameter_writer& writer,
|
||||
int pixel_format
|
||||
= texture_control_word::pixel_format::_565;
|
||||
|
||||
const vec4 color = {0, 0, 0, 0};
|
||||
vec4 color = {base_color.x, base_color.y, base_color.z, 1};
|
||||
|
||||
global_polygon_textured_intensity(writer,
|
||||
color,
|
||||
@ -367,7 +369,7 @@ void draw_textured_cube(ta_parameter_writer& writer,
|
||||
const vec2& ct = texture[1] * uv_scale[i];
|
||||
const vec2& dt = texture[2] * uv_scale[i];
|
||||
|
||||
const float base_intensity = _intensity(trans, normal[i]);
|
||||
const float base_intensity = _intensity(trans, normal[i]) + intensity_offset;
|
||||
|
||||
quad_type_7_maybe_clip(writer,
|
||||
ap, at,
|
||||
|
@ -83,7 +83,9 @@ void draw_cube(ta_parameter_writer& writer,
|
||||
void draw_textured_cube(ta_parameter_writer& writer,
|
||||
const mat4x4& trans,
|
||||
const vec3& scale,
|
||||
const texture::cube_texture_offsets& offsets);
|
||||
const texture::cube_texture_offsets& offsets,
|
||||
const vec3& base_color,
|
||||
float intensity_offset);
|
||||
|
||||
void draw_icosphere(ta_parameter_writer& writer,
|
||||
const mat4x4& trans,
|
||||
|
@ -1,12 +1,19 @@
|
||||
#include "assert.h"
|
||||
#include "interrupt.hpp"
|
||||
//#include "aica/aica.hpp"
|
||||
#include "aica/aica.hpp"
|
||||
|
||||
#include "platform/graphics.hpp"
|
||||
#include "platform/input.hpp"
|
||||
#include "platform/texture.hpp"
|
||||
#include "platform/detect_emulator.hpp"
|
||||
#include "xm_player/sound.hpp"
|
||||
#include "xm_player/interpreter.hpp"
|
||||
#include "printf/printf.h"
|
||||
|
||||
#include "platform/emulator_detected.hpp"
|
||||
|
||||
bool emulator_detected;
|
||||
|
||||
void vbr100()
|
||||
{
|
||||
if (sh7091.CCN.EXPEVT == 0xe0) {
|
||||
@ -45,9 +52,14 @@ 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.mcire = (1 << 6); // interrupt timer A
|
||||
|
||||
//scene::current_scene->interrupt();
|
||||
wait(); aica_sound.common.tactl_tima
|
||||
= aica::tactl_tima::TACTL(0) // increment once every sample
|
||||
| aica::tactl_tima::TIMA(0xfffd) // interrupt after 3 counts
|
||||
;
|
||||
|
||||
interpreter::interrupt();
|
||||
} else {
|
||||
serial::string("vbr600\n");
|
||||
interrupt_exception();
|
||||
@ -62,15 +74,20 @@ void main()
|
||||
{
|
||||
serial::init(0);
|
||||
|
||||
emulator_detected = detect_emulator();
|
||||
|
||||
input::init();
|
||||
graphics::init();
|
||||
texture::init();
|
||||
sound::init();
|
||||
|
||||
interrupt_init();
|
||||
system.IML6NRM = istnrm::end_of_render_tsp
|
||||
| istnrm::v_blank_in
|
||||
| istnrm::end_of_transferring_opaque_list;
|
||||
|
||||
system.IML4EXT = istext::aica;
|
||||
|
||||
while (1) {
|
||||
input::update();
|
||||
graphics::step();
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "holly/ta_fifo_texture_memory_transfer.hpp"
|
||||
|
||||
#include "font/ter_u12n.data.h"
|
||||
#include "font/ter_u32n.data.h"
|
||||
#include "texture/igh25_box_top_32.data.h"
|
||||
#include "texture/igh25_box_bottom_32.data.h"
|
||||
#include "texture/igh25_box_side_32.data.h"
|
||||
@ -39,6 +40,11 @@ namespace texture {
|
||||
.size = reinterpret_cast<int>(&_binary_font_ter_u12n_data_size),
|
||||
.offset = offset::ter_u12n,
|
||||
},
|
||||
{
|
||||
.start = reinterpret_cast<void *>(&_binary_font_ter_u32n_data_start),
|
||||
.size = reinterpret_cast<int>(&_binary_font_ter_u32n_data_size),
|
||||
.offset = offset::ter_u32n,
|
||||
},
|
||||
{
|
||||
.start = reinterpret_cast<void *>(&_binary_texture_igh25_box_top_32_data_start),
|
||||
.size = reinterpret_cast<int>(&_binary_texture_igh25_box_top_32_data_size),
|
||||
|
@ -3,7 +3,8 @@
|
||||
namespace texture {
|
||||
namespace offset {
|
||||
constexpr int ter_u12n = 0;
|
||||
constexpr int igh25_box_top_32 = ter_u12n + 4096;
|
||||
constexpr int ter_u32n = ter_u12n + 4096;
|
||||
constexpr int igh25_box_top_32 = ter_u32n + 32768;
|
||||
constexpr int igh25_box_bottom_32 = igh25_box_top_32 + 2048;
|
||||
constexpr int igh25_box_side_32 = igh25_box_bottom_32 + 2048;
|
||||
|
||||
|
5
src/xm_player/cover.cpp
Normal file
5
src/xm_player/cover.cpp
Normal file
@ -0,0 +1,5 @@
|
||||
#include "xm_player/cover.hpp"
|
||||
|
||||
namespace cover {
|
||||
int cover_ix;
|
||||
}
|
16
src/xm_player/cover.hpp
Normal file
16
src/xm_player/cover.hpp
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
namespace cover {
|
||||
extern int cover_ix;
|
||||
|
||||
enum cover_type {
|
||||
thebeach,
|
||||
silvertrees,
|
||||
redtree,
|
||||
mountain,
|
||||
mossycottage,
|
||||
clocks,
|
||||
tree,
|
||||
moonmountains,
|
||||
};
|
||||
}
|
413
src/xm_player/interpreter.cpp
Normal file
413
src/xm_player/interpreter.cpp
Normal file
@ -0,0 +1,413 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "printf/printf.h"
|
||||
#include "aica/aica.hpp"
|
||||
|
||||
#include "xm_player/interpreter.hpp"
|
||||
#include "xm_player/sound.hpp"
|
||||
#include "xm_player/playlist.hpp"
|
||||
#include "xm_player/cover.hpp"
|
||||
|
||||
namespace interpreter {
|
||||
|
||||
struct interpreter_state state = {};
|
||||
|
||||
// quater-semitones
|
||||
//
|
||||
// for i in range(48):
|
||||
// round(1024 * (2 ** (i / 48) - 1))
|
||||
//
|
||||
const static int16_t cent_to_fns[] = {
|
||||
0, 15, 30, 45, 61, 77, 93, 109, 125, 142, 159, 176,
|
||||
194, 211, 229, 248, 266, 285, 304, 323, 343, 363, 383, 403,
|
||||
424, 445, 467, 488, 510, 533, 555, 578, 601, 625, 649, 673,
|
||||
698, 723, 749, 774, 801, 827, 854, 881, 909, 937, 966, 995
|
||||
};
|
||||
const int cent_to_fns_length = (sizeof (cent_to_fns)) / (sizeof (cent_to_fns[0]));
|
||||
|
||||
uint16_t
|
||||
note_to_oct_fns(const int8_t note)
|
||||
{
|
||||
// log(8363 / 44100) / log(2)
|
||||
const float base_ratio = -2.3986861877015477;
|
||||
|
||||
float c4_note = (float)note - 49.0;
|
||||
float ratio = base_ratio + (c4_note / 12.0);
|
||||
|
||||
float whole = (int)ratio;
|
||||
float fraction;
|
||||
if (ratio < 0) {
|
||||
if (whole > ratio)
|
||||
whole -= 1;
|
||||
fraction = -(whole - ratio);
|
||||
} else {
|
||||
fraction = ratio - whole;
|
||||
}
|
||||
|
||||
assert(fraction >= 0.0);
|
||||
assert(fraction < 1.0);
|
||||
|
||||
int fns = cent_to_fns[(int)(fraction * cent_to_fns_length)];
|
||||
|
||||
return aica::oct_fns::OCT((int)whole) | aica::oct_fns::FNS((int)fns);
|
||||
}
|
||||
|
||||
const static int8_t volume_table[] = {
|
||||
0, 3, 5, 6, 7, 8, 8, 9, 9, 9, 10, 10, 10, 10, 11, 11,
|
||||
11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13,
|
||||
13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14,
|
||||
14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
||||
15
|
||||
};
|
||||
|
||||
void _play_note(int ch, const xm_pattern_format_t * pf)
|
||||
{
|
||||
int instrument = (pf->instrument != 0) ? pf->instrument : state.channel[ch].instrument;
|
||||
if (instrument == 0)
|
||||
instrument = 1;
|
||||
state.channel[ch].instrument = instrument;
|
||||
|
||||
xm_sample_header_t * sample_header = state.xm.sample_header[instrument - 1];
|
||||
int start = state.xm.sample_data_offset[instrument - 1];
|
||||
|
||||
int sample_type = ((sample_header->type & (1 << 4)) != 0);
|
||||
int bytes_per_sample = 1 + sample_type;
|
||||
|
||||
int loop_type = sample_header->type & 0b11;
|
||||
int lpctl = (loop_type == 0) ? 0 : 1;
|
||||
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 (loop_type == 2) // bidirectional
|
||||
len += len - 2;
|
||||
|
||||
int volume_column = state.channel[ch].volume;
|
||||
if (pf->volume_column_byte >= 0x10 && pf->volume_column_byte <= 0x50) {
|
||||
volume_column = pf->volume_column_byte - 0x10;
|
||||
state.channel[ch].volume = volume_column;
|
||||
}
|
||||
|
||||
assert(sample_header->volume >= 0 && sample_header->volume <= 64);
|
||||
int volume = (sample_header->volume * volume_column) / 64;
|
||||
assert(volume >= 0 && volume <= 64);
|
||||
int disdl = volume_table[volume];
|
||||
bool pcms = !sample_type;
|
||||
wait(); aica_sound.channel[ch].PCMS(pcms);
|
||||
wait(); aica_sound.channel[ch].SA(start);
|
||||
wait(); aica_sound.channel[ch].LPCTL(lpctl);
|
||||
wait(); aica_sound.channel[ch].LSA((lsa) & ~(0b11));
|
||||
wait(); aica_sound.channel[ch].LEA((lsa + len) & ~(0b11));
|
||||
wait(); aica_sound.channel[ch].DISDL(disdl);
|
||||
wait(); aica_sound.channel[ch].oct_fns = note_to_oct_fns(pf->note + sample_header->relative_note_number);
|
||||
|
||||
if (pf->effect_type == 0x04) { // vibrato
|
||||
wait(); aica_sound.channel[ch].LFOF(0x12);
|
||||
wait(); aica_sound.channel[ch].ALFOWS(2);
|
||||
wait(); aica_sound.channel[ch].PLFOWS(2);
|
||||
wait(); aica_sound.channel[ch].ALFOS(0);
|
||||
wait(); aica_sound.channel[ch].PLFOS(4);
|
||||
} else {
|
||||
//wait(); aica_sound.channel[ch].LFOF(0x11);
|
||||
//wait(); aica_sound.channel[ch].ALFOWS(2);
|
||||
//wait(); aica_sound.channel[ch].PLFOWS(2);
|
||||
wait(); aica_sound.channel[ch].ALFOS(0);
|
||||
wait(); aica_sound.channel[ch].PLFOS(0);
|
||||
}
|
||||
|
||||
state.channel[ch].keyon = 255;
|
||||
wait(); aica_sound.channel[ch].KYONB(0);
|
||||
}
|
||||
|
||||
void play_note_effect(int ch, const xm_pattern_format_t * pf)
|
||||
{
|
||||
int effect_tick = state.tick % state.ticks_per_line;
|
||||
|
||||
switch (pf->effect_type) {
|
||||
case 0x04: // 4 vibrato
|
||||
wait(); aica_sound.channel[ch].LFOF(0x12);
|
||||
wait(); aica_sound.channel[ch].ALFOWS(2);
|
||||
wait(); aica_sound.channel[ch].PLFOWS(2);
|
||||
wait(); aica_sound.channel[ch].ALFOS(0);
|
||||
wait(); aica_sound.channel[ch].PLFOS(4);
|
||||
break;
|
||||
case 0x0d: // D pattern break
|
||||
state.pattern_break = pf->effect_parameter;
|
||||
break;
|
||||
case 0x0e: // E
|
||||
switch (pf->effect_parameter & 0xf0) {
|
||||
case 0xd0: // ED note delay
|
||||
if (effect_tick == (pf->effect_parameter & 0x0f)) {
|
||||
_play_note(ch, pf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x14: // K delayed tick
|
||||
if (effect_tick == pf->effect_parameter) {
|
||||
wait(); aica_sound.channel[ch].KYONB(0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void play_note(int ch, const xm_pattern_format_t * pf)
|
||||
{
|
||||
if (pf->note == 97) {
|
||||
wait(); aica_sound.channel[ch].KYONB(0);
|
||||
} else if (pf->note != 0) {
|
||||
bool note_delay = (pf->effect_type == 0xe) && ((pf->effect_parameter & 0xf0) == 0xd0); // ED note delay
|
||||
if (!note_delay)
|
||||
_play_note(ch, pf);
|
||||
}
|
||||
|
||||
play_note_effect(ch, pf);
|
||||
}
|
||||
|
||||
/*
|
||||
void play_debug_note(int ch, xm_pattern_format_t * pf)
|
||||
{
|
||||
debug_note(ch, pf);
|
||||
play_note(ch, pf);
|
||||
}
|
||||
*/
|
||||
|
||||
void rekey_note(int ch, const xm_pattern_format_t * pf)
|
||||
{
|
||||
if (pf->note == 97) {
|
||||
} else if (pf->note != 0) {
|
||||
wait(); aica_sound.channel[ch].KYONB(0);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void next_pattern()
|
||||
{
|
||||
if (state.reverse)
|
||||
state.pattern_order_table_index -= 1;
|
||||
else
|
||||
state.pattern_order_table_index += 1;
|
||||
|
||||
if (state.pattern_order_table_index < 0)
|
||||
state.pattern_order_table_index = state.xm.song_length - 1;
|
||||
if (state.pattern_order_table_index >= state.xm.song_length) {
|
||||
if (state.repeat) {
|
||||
state.pattern_order_table_index = 0;
|
||||
} else if (state.reverse) {
|
||||
if (playlist::prev(false))
|
||||
return;
|
||||
} else {
|
||||
if (playlist::next(false))
|
||||
return;
|
||||
}
|
||||
}
|
||||
printf("pattern_order_table_index: %d\n", state.pattern_order_table_index);
|
||||
|
||||
state.pattern_index = state.xm.header->pattern_order_table[state.pattern_order_table_index];
|
||||
|
||||
if (state.pattern_break >= 0) {
|
||||
state.next_line_index = state.pattern_break;
|
||||
} else {
|
||||
int line_count = state.xm.pattern_note_count[state.pattern_index] / state.xm.number_of_channels;
|
||||
if (state.reverse)
|
||||
state.next_line_index = line_count - 1;
|
||||
else
|
||||
state.next_line_index = 0;
|
||||
}
|
||||
state.pattern_break = -1;
|
||||
|
||||
printf("note_count: %d\n", state.xm.pattern_note_count[state.pattern_index]);
|
||||
}
|
||||
|
||||
template <void (*F)(int, const xm_pattern_format_t *)>
|
||||
void execute_line(int line_index)
|
||||
{
|
||||
int line_pattern_index = line_index * state.xm.number_of_channels;
|
||||
for (int ch = 0; ch < state.xm.number_of_channels; ch++) {
|
||||
xm_pattern_format_t * pattern = state.xm.pattern[state.pattern_index];
|
||||
F(ch, &pattern[line_pattern_index + ch]);
|
||||
}
|
||||
}
|
||||
|
||||
void interrupt()
|
||||
{
|
||||
if (state.deferred_load_tick == 1) {
|
||||
deferred_load_finish();
|
||||
}
|
||||
if (state.deferred_load_tick > 0) {
|
||||
state.deferred_load_tick -= 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.paused)
|
||||
return;
|
||||
|
||||
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;
|
||||
}
|
||||
*/
|
||||
const int keyon_tick = 254;
|
||||
if (state.channel[ch].keyon > keyon_tick) {
|
||||
state.channel[ch].keyon -= 1;
|
||||
}
|
||||
else if (state.channel[ch].keyon == keyon_tick) {
|
||||
wait(); aica_sound.channel[ch].KYONB(1);
|
||||
state.channel[ch].keyon -= 1;
|
||||
}
|
||||
}
|
||||
wait(); aica_sound.channel[0].KYONEX(1);
|
||||
|
||||
if ((state.interrupt_clock % state.current_tick_rate) != 0) {
|
||||
return;
|
||||
}
|
||||
for (int ch = 0; ch < 64; ch++) {
|
||||
int keyon = state.channel[ch].keyon;
|
||||
if (keyon != 0) {
|
||||
state.channel[ch].keyon -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
if (state.reverse)
|
||||
state.next_line_index -= 1;
|
||||
else
|
||||
state.next_line_index += 1;
|
||||
|
||||
execute_line<play_note>(state.line_index);
|
||||
} 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 < 0
|
||||
|| note_index >= state.xm.pattern_note_count[state.pattern_index];
|
||||
|
||||
if (end_of_pattern) {
|
||||
printf("end_of_pattern\n");
|
||||
next_pattern();
|
||||
}
|
||||
}
|
||||
|
||||
state.tick += 1;
|
||||
}
|
||||
|
||||
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 = clock_multiplier * 2500 / default_bpm;
|
||||
|
||||
printf("default_bpm %d\n", default_bpm);
|
||||
printf("default_tempo %d\n", default_tempo);
|
||||
printf("tick_rate %d\n", tick_rate);
|
||||
|
||||
state.current_tick_rate = tick_rate;
|
||||
state.default_tick_rate = tick_rate;
|
||||
state.ticks_per_line = default_tempo;
|
||||
state.tick = 0;
|
||||
state.line_index = 0;
|
||||
state.pattern_order_table_index = -1;
|
||||
next_pattern();
|
||||
for (int ch = 0; ch < 64; ch++) {
|
||||
state.channel[ch].keyon = 0;
|
||||
state.channel[ch].volume = 64;
|
||||
}
|
||||
state.paused = false;
|
||||
|
||||
printf("tick_rate %d\n", state.current_tick_rate);
|
||||
}
|
||||
|
||||
void stop_sound()
|
||||
{
|
||||
for (int ch = 0; ch < 64; ch++) {
|
||||
wait();
|
||||
//bool kyonb = aica_sound.channel[ch].KYONB() != 0;
|
||||
wait(); aica_sound.channel[ch].KYONB(0);
|
||||
//state.channel[ch].keyon = kyonb ? 255 : 0;
|
||||
}
|
||||
wait(); aica_sound.channel[0].KYONEX(1);
|
||||
}
|
||||
|
||||
void resume_sound()
|
||||
{
|
||||
for (int ch = 0; ch < 64; ch++) {
|
||||
wait(); aica_sound.channel[ch].RR(0xa);
|
||||
}
|
||||
}
|
||||
|
||||
void pause()
|
||||
{
|
||||
stop_sound();
|
||||
state.paused = true;
|
||||
}
|
||||
|
||||
void unpause()
|
||||
{
|
||||
state.paused = false;
|
||||
}
|
||||
|
||||
static uint8_t __attribute__((aligned(32))) sample_data[1024 * 1024 * 2];
|
||||
const int sample_data_length = (sizeof (sample_data));
|
||||
|
||||
void deferred_load(int buf, int cover_ix)
|
||||
{
|
||||
const float aica_clock_multiplier = 44.1 / 3;
|
||||
state.deferred_cover_ix = cover_ix;
|
||||
|
||||
state.deferred_load_tick = aica_clock_multiplier * 1000 / 2;
|
||||
|
||||
state.sample_data_ix = xm_init(&interpreter::state.xm,
|
||||
buf,
|
||||
sample_data,
|
||||
sample_data_length);
|
||||
interpreter::init(aica_clock_multiplier);
|
||||
}
|
||||
|
||||
void deferred_load_finish()
|
||||
{
|
||||
sound::transfer(sample_data, state.sample_data_ix);
|
||||
|
||||
cover::cover_ix = state.deferred_cover_ix;
|
||||
|
||||
resume_sound();
|
||||
}
|
||||
|
||||
}
|
50
src/xm_player/interpreter.hpp
Normal file
50
src/xm_player/interpreter.hpp
Normal file
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "xm_player/xm.h"
|
||||
|
||||
namespace interpreter {
|
||||
|
||||
constexpr int max_channels = 64;
|
||||
|
||||
struct channel_state {
|
||||
uint8_t instrument;
|
||||
uint8_t keyon;
|
||||
uint8_t volume;
|
||||
};
|
||||
|
||||
struct interpreter_state {
|
||||
int interrupt_clock;
|
||||
int current_tick_rate;
|
||||
int default_tick_rate;
|
||||
int ticks_per_line;
|
||||
int tick;
|
||||
int pattern_order_table_index;
|
||||
int pattern_break;
|
||||
int pattern_index;
|
||||
int line_index;
|
||||
int next_line_index; // within the current pattern
|
||||
bool paused;
|
||||
bool reverse;
|
||||
bool repeat;
|
||||
|
||||
int deferred_load_tick;
|
||||
int sample_data_ix;
|
||||
int deferred_cover_ix;
|
||||
|
||||
struct xm_state xm;
|
||||
|
||||
struct channel_state channel[max_channels];
|
||||
};
|
||||
|
||||
extern struct interpreter_state state;
|
||||
void interrupt();
|
||||
void init(float clock_multiplier);
|
||||
void pause();
|
||||
void unpause();
|
||||
|
||||
void resume_sound();
|
||||
void stop_sound();
|
||||
|
||||
void deferred_load(int buf, int cover_ix);
|
||||
void deferred_load_finish();
|
||||
}
|
33
src/xm_player/malloc.c
Normal file
33
src/xm_player/malloc.c
Normal file
@ -0,0 +1,33 @@
|
||||
#include "assert.h"
|
||||
#include "malloc.h"
|
||||
|
||||
struct arena {
|
||||
uint8_t * mem;
|
||||
uint32_t size;
|
||||
uint32_t ix;
|
||||
};
|
||||
|
||||
static uint8_t arena_mem[0x100000];
|
||||
|
||||
static struct arena arena = {
|
||||
.mem = arena_mem,
|
||||
.size = (sizeof (arena_mem)),
|
||||
.ix = 0,
|
||||
};
|
||||
|
||||
void malloc_arena_reset()
|
||||
{
|
||||
arena.ix = 0;
|
||||
}
|
||||
|
||||
void * malloc_arena(uint32_t size)
|
||||
{
|
||||
if (size == 0)
|
||||
return nullptr;
|
||||
|
||||
assert((arena.ix & (~3)) == arena.ix);
|
||||
void * ptr = &arena.mem[arena.ix];
|
||||
size = (size + 3) & (~3);
|
||||
arena.ix += size;
|
||||
return ptr;
|
||||
}
|
14
src/xm_player/malloc.h
Normal file
14
src/xm_player/malloc.h
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void malloc_arena_reset();
|
||||
void * malloc_arena(uint32_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
105
src/xm_player/playlist.cpp
Normal file
105
src/xm_player/playlist.cpp
Normal file
@ -0,0 +1,105 @@
|
||||
#include "interpreter.hpp"
|
||||
#include "sound.hpp"
|
||||
#include "playlist.hpp"
|
||||
|
||||
#include "xm_player/xm.h"
|
||||
#include "xm_player/cover.hpp"
|
||||
|
||||
#include "xm/CloudsAhead.xm.h"
|
||||
#include "xm/CottageFantasy.xm.h"
|
||||
#include "xm/RedBlossom.xm.h"
|
||||
#include "xm/TheClockOfElery.xm.h"
|
||||
#include "xm/ForestAtTwilight.xm.h"
|
||||
#include "xm/SpringWaltz.xm.h"
|
||||
#include "xm/TheMountainsOfElmindeer.xm.h"
|
||||
|
||||
namespace playlist {
|
||||
|
||||
struct state state = {
|
||||
.playlist_ix = -1,
|
||||
.loops = -1,
|
||||
};
|
||||
|
||||
const playlist_item playlist[] = {
|
||||
{
|
||||
.artist = "Shiroiii",
|
||||
.title = "Clouds Ahead",
|
||||
.start = (int)&_binary_xm_CloudsAhead_xm_start,
|
||||
.cover_ix = cover::thebeach,
|
||||
},
|
||||
{
|
||||
.artist = "Shiroiii",
|
||||
.title = "Cottage Fantasy",
|
||||
.start = (int)&_binary_xm_CottageFantasy_xm_start,
|
||||
.cover_ix = cover::mossycottage,
|
||||
},
|
||||
{
|
||||
.artist = "Shiroiii",
|
||||
.title = "Red Blossom",
|
||||
.start = (int)&_binary_xm_RedBlossom_xm_start,
|
||||
.cover_ix = cover::redtree,
|
||||
},
|
||||
{
|
||||
.artist = "Shiroiii",
|
||||
.title = "The Clock Of Elery",
|
||||
.start = (int)&_binary_xm_TheClockOfElery_xm_start,
|
||||
.cover_ix = cover::clocks,
|
||||
},
|
||||
{
|
||||
.artist = "Shiroiii",
|
||||
.title = "Forest At Twilight",
|
||||
.start = (int)&_binary_xm_ForestAtTwilight_xm_start,
|
||||
.cover_ix = cover::silvertrees,
|
||||
},
|
||||
{
|
||||
.artist = "Shiroiii",
|
||||
.title = "Spring Waltz",
|
||||
.start = (int)&_binary_xm_SpringWaltz_xm_start,
|
||||
.cover_ix = cover::tree,
|
||||
},
|
||||
{
|
||||
.artist = "Shiroiii",
|
||||
.title = "Mountains of Elmindeer",
|
||||
.start = (int)&_binary_xm_TheMountainsOfElmindeer_xm_start,
|
||||
.cover_ix = cover::mountain,
|
||||
},
|
||||
};
|
||||
|
||||
const int playlist_length = (sizeof (playlist)) / (sizeof (playlist[0]));
|
||||
|
||||
bool next(bool stop_sound)
|
||||
{
|
||||
if (state.loops < 0 || state.loops >= 2) {
|
||||
state.playlist_ix += 1;
|
||||
state.loops = 0;
|
||||
|
||||
if (state.playlist_ix >= playlist_length)
|
||||
state.playlist_ix = 0;
|
||||
|
||||
printf("next deferred_load playlist_ix %d\n", state.playlist_ix);
|
||||
interpreter::stop_sound();
|
||||
|
||||
const playlist_item& item = playlist[state.playlist_ix];
|
||||
interpreter::deferred_load(item.start, item.cover_ix);
|
||||
return true;
|
||||
} else {
|
||||
state.loops += 1;
|
||||
interpreter::state.pattern_order_table_index = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool prev(bool stop_sound)
|
||||
{
|
||||
state.playlist_ix -= 1;
|
||||
if (state.playlist_ix < 0)
|
||||
state.playlist_ix = playlist_length - 1;
|
||||
|
||||
printf("prev deferred_load playlist_ix %d\n", state.playlist_ix);
|
||||
interpreter::stop_sound();
|
||||
const playlist_item& item = playlist[state.playlist_ix];
|
||||
interpreter::deferred_load(item.start, item.cover_ix);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
22
src/xm_player/playlist.hpp
Normal file
22
src/xm_player/playlist.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
namespace playlist {
|
||||
struct playlist_item {
|
||||
const char * const artist;
|
||||
const char * const title;
|
||||
const int start;
|
||||
const int cover_ix;
|
||||
};
|
||||
|
||||
struct state {
|
||||
int playlist_ix;
|
||||
int loops;
|
||||
};
|
||||
|
||||
bool next(bool stop_sound=true);
|
||||
bool prev(bool stop_sound=true);
|
||||
|
||||
extern struct state state;
|
||||
extern const playlist_item playlist[];
|
||||
extern const int playlist_length;
|
||||
}
|
152
src/xm_player/sound.cpp
Normal file
152
src/xm_player/sound.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "memorymap.hpp"
|
||||
|
||||
#include "systembus.hpp"
|
||||
#include "systembus_bits.hpp"
|
||||
|
||||
#include "sh7091/sh7091.hpp"
|
||||
#include "sh7091/sh7091_bits.hpp"
|
||||
#include "sh7091/serial.hpp"
|
||||
|
||||
#include "printf/printf.h"
|
||||
|
||||
#include "aica/aica.hpp"
|
||||
|
||||
#include "sound.hpp"
|
||||
|
||||
#include "assert.h"
|
||||
|
||||
static void g2_aica_dma(uint32_t g2_address, uint32_t system_address, int length)
|
||||
{
|
||||
using namespace dmac;
|
||||
|
||||
constexpr uint32_t dma_address_mask = 0x1fffffe0;
|
||||
|
||||
length = (length + 31) & (~31);
|
||||
|
||||
// is DMAOR needed?
|
||||
sh7091.DMAC.DMAOR = dmaor::ddt::on_demand_data_transfer_mode /* on-demand data transfer mode */
|
||||
| dmaor::pr::ch2_ch0_ch1_ch3 /* priority mode; CH2 > CH0 > CH1 > CH3 */
|
||||
| dmaor::dme::operation_enabled_on_all_channels; /* DMAC master enable */
|
||||
|
||||
|
||||
g2_if.ADEN = 0; // disable G2-AICA-DMA
|
||||
|
||||
g2_if.G2APRO = 0x4659007f; // disable protection
|
||||
|
||||
g2_if.ADSTAG = dma_address_mask & g2_address; // G2 address
|
||||
g2_if.ADSTAR = dma_address_mask & system_address; // system memory address
|
||||
g2_if.ADLEN = length;
|
||||
g2_if.ADDIR = 0; // from root bus to G2 device
|
||||
g2_if.ADTSEL = 0; // CPU controlled trigger
|
||||
g2_if.ADEN = 1; // enable G2-AICA-DMA
|
||||
g2_if.ADST = 1; // start G2-AICA-DMA
|
||||
}
|
||||
|
||||
static void g2_aica_dma_wait_complete()
|
||||
{
|
||||
// wait for maple DMA completion
|
||||
while ((system.ISTNRM & istnrm::end_of_dma_aica_dma) == 0);
|
||||
system.ISTNRM = istnrm::end_of_dma_aica_dma;
|
||||
assert(g2_if.ADST == 0);
|
||||
}
|
||||
|
||||
static void writeback(void const * const buf, uint32_t size)
|
||||
{
|
||||
uint8_t const * const buf8 = reinterpret_cast<uint8_t const * const>(buf);
|
||||
|
||||
for (uint32_t i = 0; i < size / (32); i++) {
|
||||
asm volatile ("ocbwb @%0"
|
||||
: // output
|
||||
: "r" (&buf8[i * 32]) // input
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t __attribute__((aligned(32))) zero[0x28c0] = {};
|
||||
|
||||
namespace sound {
|
||||
|
||||
void init()
|
||||
{
|
||||
printf("sound::init\n");
|
||||
|
||||
wait(); aica_sound.common.vreg_armrst = aica::vreg_armrst::ARMRST(1);
|
||||
wait(); aica_sound.common.dmea0_mrwinh = aica::dmea0_mrwinh::MRWINH(0b0111);
|
||||
system.ISTNRM = istnrm::end_of_dma_aica_dma;
|
||||
|
||||
writeback(zero, (sizeof (zero)));
|
||||
|
||||
// slot/common: 00700000 - 007028c0 (excludes vreg_armrst)
|
||||
g2_aica_dma((uint32_t)0x00700000, (int)zero, 0x28c0);
|
||||
g2_aica_dma_wait_complete();
|
||||
|
||||
// dsp : 00703000 - 007045c8
|
||||
g2_aica_dma((uint32_t)0x00703000, (int)zero, 0x15e0);
|
||||
g2_aica_dma_wait_complete();
|
||||
|
||||
wait(); aica_sound.common.dmea0_mrwinh = aica::dmea0_mrwinh::MRWINH(0b0001);
|
||||
|
||||
for (int i = 0; i < 64; i++) {
|
||||
wait(); aica_sound.channel[i].KYONB(0);
|
||||
wait(); aica_sound.channel[i].LPCTL(0);
|
||||
wait(); aica_sound.channel[i].PCMS(0);
|
||||
wait(); aica_sound.channel[i].LSA(0);
|
||||
wait(); aica_sound.channel[i].LEA(0);
|
||||
|
||||
wait(); aica_sound.channel[i].D2R(0xa);
|
||||
wait(); aica_sound.channel[i].D1R(0xa);
|
||||
wait(); aica_sound.channel[i].RR(0xa);
|
||||
wait(); aica_sound.channel[i].AR(0x1f);
|
||||
/*
|
||||
wait(); aica_sound.channel[i].D2R(0);
|
||||
wait(); aica_sound.channel[i].D1R(0);
|
||||
wait(); aica_sound.channel[i].RR(0x1f);
|
||||
wait(); aica_sound.channel[i].AR(0x1f);
|
||||
*/
|
||||
|
||||
wait(); aica_sound.channel[i].ALFOS(0);
|
||||
wait(); aica_sound.channel[i].PLFOS(0);
|
||||
|
||||
wait(); aica_sound.channel[i].OCT(0);
|
||||
wait(); aica_sound.channel[i].FNS(0);
|
||||
wait(); aica_sound.channel[i].DISDL(0);
|
||||
wait(); aica_sound.channel[i].DIPAN(0);
|
||||
|
||||
wait(); aica_sound.channel[i].Q(0b00100);
|
||||
wait(); aica_sound.channel[i].TL(0);
|
||||
wait(); aica_sound.channel[i].LPOFF(1);
|
||||
}
|
||||
|
||||
wait(); aica_sound.channel[0].KYONEX(1);
|
||||
|
||||
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::MEM8MB(0) // 16Mbit SDRAM
|
||||
| aica::mono_mem8mb_dac18b_ver_mvol::DAC18B(0) // 16-bit DAC
|
||||
| aica::mono_mem8mb_dac18b_ver_mvol::MVOL(0xc) // volume
|
||||
;
|
||||
|
||||
wait(); aica_sound.common.tactl_tima =
|
||||
aica::tactl_tima::TACTL(0) // increment once every sample
|
||||
| aica::tactl_tima::TIMA(0xfffd) // interrupt after 3 counts
|
||||
;
|
||||
|
||||
wait(); aica_sound.common.mcieb = (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();
|
||||
}
|
||||
|
||||
}
|
22
src/xm_player/sound.hpp
Normal file
22
src/xm_player/sound.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "systembus.hpp"
|
||||
#include "systembus_bits.hpp"
|
||||
|
||||
static inline void wait()
|
||||
{
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
namespace sound {
|
||||
|
||||
void init();
|
||||
|
||||
void transfer(const void * sample_data, int sample_data_ix);
|
||||
|
||||
}
|
223
src/xm_player/xm.c
Normal file
223
src/xm_player/xm.c
Normal file
@ -0,0 +1,223 @@
|
||||
#include "xm/xm.h"
|
||||
#include "printf/printf.h"
|
||||
#include "xm.h"
|
||||
#include "malloc.h"
|
||||
|
||||
static int xm_unpack_sample(int buf,
|
||||
int offset,
|
||||
xm_sample_header_t * sample_header,
|
||||
uint8_t * sample_data,
|
||||
int sample_data_ix)
|
||||
{
|
||||
int size = s32(&sample_header->sample_length);
|
||||
int loop_start = s32(&sample_header->sample_loop_start);
|
||||
int loop_length = s32(&sample_header->sample_loop_length);
|
||||
|
||||
int loop_type = sample_header->type & 0b11;
|
||||
|
||||
if (sample_header->type & (1 << 4)) { // 16-bit samples
|
||||
int num_samples = size / 2;
|
||||
int lsa = loop_start / 2;
|
||||
int len = loop_length / 2;
|
||||
|
||||
int old = 0;
|
||||
volatile int16_t * out = (volatile int16_t *)(&sample_data[sample_data_ix]);
|
||||
int16_t * in = (int16_t *)(buf + offset);
|
||||
for (int i = 0; i < num_samples; i++) {
|
||||
old += s16(&in[i]);
|
||||
out[i] = old;
|
||||
}
|
||||
|
||||
if (loop_type == 2) { // bidirectional
|
||||
for (int i = 0; i < len - 2; i++) {
|
||||
out[num_samples + i] = out[lsa + (len - i - 2)];
|
||||
}
|
||||
|
||||
size += (len - 2) * 2;
|
||||
}
|
||||
|
||||
} else { // 8-bit
|
||||
int num_samples = size;
|
||||
int lsa = loop_start;
|
||||
int len = loop_length;
|
||||
|
||||
int old = 0;
|
||||
volatile int8_t * out = (volatile int8_t *)(&sample_data[sample_data_ix]);
|
||||
int8_t * in = (int8_t *)(buf + offset);
|
||||
for (int i = 0; i < num_samples; i++) {
|
||||
old += in[i];
|
||||
out[i] = old;
|
||||
}
|
||||
|
||||
if (loop_type == 2) { // bidirectional
|
||||
for (int i = 0; i < len - 2; i++) {
|
||||
out[num_samples + i] = out[lsa + (len - i - 2)];
|
||||
}
|
||||
|
||||
size += (len - 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (size & 1) {
|
||||
size += 1;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static int xm_samples_init(xm_state_t * xm,
|
||||
int buf,
|
||||
int offset,
|
||||
int instrument_ix,
|
||||
int number_of_samples,
|
||||
uint8_t * sample_data,
|
||||
int sample_data_length,
|
||||
int * sample_data_ix)
|
||||
{
|
||||
xm_sample_header_t * sample_header[number_of_samples];
|
||||
xm->sample_header[instrument_ix] = (xm_sample_header_t *)(buf + offset);
|
||||
//if (instrument_ix <= 12)
|
||||
//debug_xm_sample_header(instrument_ix, xm->sample_header[instrument_ix]);
|
||||
|
||||
for (int i = 0; i < number_of_samples; i++) {
|
||||
sample_header[i] = (xm_sample_header_t *)(buf + offset);
|
||||
offset += (sizeof (xm_sample_header_t));
|
||||
}
|
||||
|
||||
for (int i = 0; i < number_of_samples; i++) {
|
||||
int sample_length = s32(&sample_header[i]->sample_length);
|
||||
if (sample_length > 0) {
|
||||
//printf(" sample_length % 6d\n", sample_length);
|
||||
xm->sample_data_offset[instrument_ix] = *sample_data_ix;
|
||||
*sample_data_ix += xm_unpack_sample(buf,
|
||||
offset,
|
||||
sample_header[i],
|
||||
sample_data,
|
||||
*sample_data_ix);
|
||||
assert(*sample_data_ix <= sample_data_length);
|
||||
}
|
||||
offset += sample_length;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
static inline xm_pattern_format_t parse_pattern_line(uint8_t * pattern, int * note_offset)
|
||||
{
|
||||
int offset = *note_offset;
|
||||
|
||||
int p = pattern[offset];
|
||||
if (p & 0x80) {
|
||||
offset += 1;
|
||||
xm_pattern_format_t pf = {};
|
||||
if (p & (1 << 0))
|
||||
pf.note = pattern[offset++];
|
||||
if (p & (1 << 1))
|
||||
pf.instrument = pattern[offset++];
|
||||
if (p & (1 << 2))
|
||||
pf.volume_column_byte = pattern[offset++];
|
||||
if (p & (1 << 3))
|
||||
pf.effect_type = pattern[offset++];
|
||||
if (p & (1 << 4))
|
||||
pf.effect_parameter = pattern[offset++];
|
||||
*note_offset = offset;
|
||||
return pf;
|
||||
} else {
|
||||
xm_pattern_format_t * pf = (xm_pattern_format_t *)&pattern[offset];
|
||||
offset += 5;
|
||||
*note_offset = offset;
|
||||
return *pf;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int count_pattern_notes(uint8_t * pattern, int pattern_data_size)
|
||||
{
|
||||
int note_offset = 0;
|
||||
int note_count = 0;
|
||||
|
||||
while (note_offset < pattern_data_size) {
|
||||
parse_pattern_line(pattern, ¬e_offset);
|
||||
note_count += 1;
|
||||
}
|
||||
assert(note_offset == pattern_data_size);
|
||||
|
||||
return note_count;
|
||||
}
|
||||
|
||||
void xm_unpack_pattern(xm_state_t * xm,
|
||||
int pattern_index)
|
||||
{
|
||||
xm_pattern_header_t * pattern_header = xm->pattern_header[pattern_index];
|
||||
uint8_t * pattern = (uint8_t *)(((int)pattern_header) + s32(&pattern_header->pattern_header_length));
|
||||
|
||||
int pattern_data_size = s16(&pattern_header->packed_pattern_data_size);
|
||||
|
||||
int note_count = count_pattern_notes(pattern, pattern_data_size);
|
||||
|
||||
xm_pattern_format_t * pf = (xm_pattern_format_t *)malloc_arena((sizeof (xm_pattern_format_t)) * note_count);
|
||||
|
||||
xm->pattern[pattern_index] = pf;
|
||||
xm->pattern_note_count[pattern_index] = note_count;
|
||||
|
||||
int note_offset = 0;
|
||||
for (int i = 0; i < note_count; i++) {
|
||||
pf[i] = parse_pattern_line(pattern, ¬e_offset);
|
||||
}
|
||||
assert(note_offset == pattern_data_size);
|
||||
}
|
||||
|
||||
int xm_init(xm_state_t * xm,
|
||||
int buf,
|
||||
uint8_t * sample_data,
|
||||
int sample_data_length)
|
||||
{
|
||||
int sample_data_ix = 0;
|
||||
|
||||
xm->header = (xm_header_t *)(buf);
|
||||
|
||||
int offset = s32(&xm->header->header_size) + (offsetof (struct xm_header, header_size));
|
||||
int number_of_patterns = s16(&xm->header->number_of_patterns);
|
||||
printf("number_of_patterns: %d\n", number_of_patterns);
|
||||
|
||||
for (int i = 0; i < number_of_patterns; i++) {
|
||||
xm_pattern_header_t * pattern_header = (xm_pattern_header_t *)(buf + offset);
|
||||
xm->pattern_header[i] = pattern_header;
|
||||
offset += s32(&pattern_header->pattern_header_length) + s16(&pattern_header->packed_pattern_data_size);
|
||||
}
|
||||
printf("end_of_patterns: %d\n", offset);
|
||||
|
||||
int number_of_instruments = s16(&xm->header->number_of_instruments);
|
||||
for (int instrument_ix = 0; instrument_ix < number_of_instruments; instrument_ix++) {
|
||||
xm_instrument_header_t * instrument_header = (xm_instrument_header_t *)(buf + offset);
|
||||
|
||||
xm->instrument_header[instrument_ix] = instrument_header;
|
||||
offset += s32(&instrument_header->instrument_size);
|
||||
|
||||
int number_of_samples = s16(&instrument_header->number_of_samples);
|
||||
offset = xm_samples_init(xm,
|
||||
buf,
|
||||
offset,
|
||||
instrument_ix,
|
||||
number_of_samples,
|
||||
sample_data,
|
||||
sample_data_length,
|
||||
&sample_data_ix);
|
||||
}
|
||||
printf("end_of_instruments: %d\n", offset);
|
||||
|
||||
int number_of_channels = s16(&xm->header->number_of_channels);
|
||||
xm->number_of_channels = number_of_channels;
|
||||
printf("number_of_channels: %d\n", number_of_channels);
|
||||
|
||||
int song_length = s16(&xm->header->song_length);
|
||||
xm->song_length = song_length;
|
||||
printf("song_length: %d\n", song_length);
|
||||
|
||||
// reset arena
|
||||
malloc_arena_reset();
|
||||
|
||||
for (int pattern_index = 0; pattern_index < number_of_patterns; pattern_index++) {
|
||||
xm_unpack_pattern(xm, pattern_index);
|
||||
}
|
||||
|
||||
return sample_data_ix;
|
||||
}
|
46
src/xm_player/xm.h
Normal file
46
src/xm_player/xm.h
Normal file
@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "xm/xm.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define xm_max_patterns 64
|
||||
#define xm_max_instruments 128
|
||||
|
||||
typedef struct xm_state {
|
||||
xm_header_t * header;
|
||||
xm_pattern_header_t * pattern_header[xm_max_patterns];
|
||||
xm_instrument_header_t * instrument_header[xm_max_instruments];
|
||||
xm_sample_header_t * sample_header[xm_max_instruments]; // array
|
||||
int sample_data_offset[xm_max_instruments];
|
||||
|
||||
int number_of_channels;
|
||||
int song_length;
|
||||
xm_pattern_format_t * pattern[xm_max_patterns];
|
||||
int pattern_note_count[xm_max_patterns];
|
||||
} xm_state_t;
|
||||
|
||||
int xm_init(xm_state_t * xm,
|
||||
int buf,
|
||||
uint8_t * sample_data,
|
||||
int sample_data_length);
|
||||
|
||||
static inline int s16(void * buf)
|
||||
{
|
||||
uint8_t * b = (uint8_t *)buf;
|
||||
int16_t v = (b[0] << 0) | (b[1] << 8);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline 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;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
BIN
xm/CloudsAhead.xm
Normal file
BIN
xm/CloudsAhead.xm
Normal file
Binary file not shown.
15
xm/CloudsAhead.xm.h
Normal file
15
xm/CloudsAhead.xm.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern uint32_t _binary_xm_CloudsAhead_xm_start __asm("_binary_xm_CloudsAhead_xm_start");
|
||||
extern uint32_t _binary_xm_CloudsAhead_xm_end __asm("_binary_xm_CloudsAhead_xm_end");
|
||||
extern uint32_t _binary_xm_CloudsAhead_xm_size __asm("_binary_xm_CloudsAhead_xm_size");
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
BIN
xm/CottageFantasy.xm
Normal file
BIN
xm/CottageFantasy.xm
Normal file
Binary file not shown.
15
xm/CottageFantasy.xm.h
Normal file
15
xm/CottageFantasy.xm.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern uint32_t _binary_xm_CottageFantasy_xm_start __asm("_binary_xm_CottageFantasy_xm_start");
|
||||
extern uint32_t _binary_xm_CottageFantasy_xm_end __asm("_binary_xm_CottageFantasy_xm_end");
|
||||
extern uint32_t _binary_xm_CottageFantasy_xm_size __asm("_binary_xm_CottageFantasy_xm_size");
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
BIN
xm/ForestAtTwilight.xm
Normal file
BIN
xm/ForestAtTwilight.xm
Normal file
Binary file not shown.
15
xm/ForestAtTwilight.xm.h
Normal file
15
xm/ForestAtTwilight.xm.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern uint32_t _binary_xm_ForestAtTwilight_xm_start __asm("_binary_xm_ForestAtTwilight_xm_start");
|
||||
extern uint32_t _binary_xm_ForestAtTwilight_xm_end __asm("_binary_xm_ForestAtTwilight_xm_end");
|
||||
extern uint32_t _binary_xm_ForestAtTwilight_xm_size __asm("_binary_xm_ForestAtTwilight_xm_size");
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
BIN
xm/RedBlossom.xm
Normal file
BIN
xm/RedBlossom.xm
Normal file
Binary file not shown.
15
xm/RedBlossom.xm.h
Normal file
15
xm/RedBlossom.xm.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern uint32_t _binary_xm_RedBlossom_xm_start __asm("_binary_xm_RedBlossom_xm_start");
|
||||
extern uint32_t _binary_xm_RedBlossom_xm_end __asm("_binary_xm_RedBlossom_xm_end");
|
||||
extern uint32_t _binary_xm_RedBlossom_xm_size __asm("_binary_xm_RedBlossom_xm_size");
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
BIN
xm/SpringWaltz.xm
Normal file
BIN
xm/SpringWaltz.xm
Normal file
Binary file not shown.
15
xm/SpringWaltz.xm.h
Normal file
15
xm/SpringWaltz.xm.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern uint32_t _binary_xm_SpringWaltz_xm_start __asm("_binary_xm_SpringWaltz_xm_start");
|
||||
extern uint32_t _binary_xm_SpringWaltz_xm_end __asm("_binary_xm_SpringWaltz_xm_end");
|
||||
extern uint32_t _binary_xm_SpringWaltz_xm_size __asm("_binary_xm_SpringWaltz_xm_size");
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
BIN
xm/SummerDreamsDemoTrackv4.xm
Normal file
BIN
xm/SummerDreamsDemoTrackv4.xm
Normal file
Binary file not shown.
15
xm/SummerDreamsDemoTrackv4.xm.h
Normal file
15
xm/SummerDreamsDemoTrackv4.xm.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern uint32_t _binary_xm_SummerDreamsDemoTrackv4_xm_start __asm("_binary_xm_SummerDreamsDemoTrackv4_xm_start");
|
||||
extern uint32_t _binary_xm_SummerDreamsDemoTrackv4_xm_end __asm("_binary_xm_SummerDreamsDemoTrackv4_xm_end");
|
||||
extern uint32_t _binary_xm_SummerDreamsDemoTrackv4_xm_size __asm("_binary_xm_SummerDreamsDemoTrackv4_xm_size");
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
BIN
xm/TheClockOfElery.xm
Normal file
BIN
xm/TheClockOfElery.xm
Normal file
Binary file not shown.
15
xm/TheClockOfElery.xm.h
Normal file
15
xm/TheClockOfElery.xm.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern uint32_t _binary_xm_TheClockOfElery_xm_start __asm("_binary_xm_TheClockOfElery_xm_start");
|
||||
extern uint32_t _binary_xm_TheClockOfElery_xm_end __asm("_binary_xm_TheClockOfElery_xm_end");
|
||||
extern uint32_t _binary_xm_TheClockOfElery_xm_size __asm("_binary_xm_TheClockOfElery_xm_size");
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
BIN
xm/TheMountainsOfElmindeer.xm
Normal file
BIN
xm/TheMountainsOfElmindeer.xm
Normal file
Binary file not shown.
15
xm/TheMountainsOfElmindeer.xm.h
Normal file
15
xm/TheMountainsOfElmindeer.xm.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern uint32_t _binary_xm_TheMountainsOfElmindeer_xm_start __asm("_binary_xm_TheMountainsOfElmindeer_xm_start");
|
||||
extern uint32_t _binary_xm_TheMountainsOfElmindeer_xm_end __asm("_binary_xm_TheMountainsOfElmindeer_xm_end");
|
||||
extern uint32_t _binary_xm_TheMountainsOfElmindeer_xm_size __asm("_binary_xm_TheMountainsOfElmindeer_xm_size");
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user