tracker_scene: factor out tracker -specific drawing functions

This commit is contained in:
Zack Buhman 2025-06-26 13:31:47 -05:00
parent 5020f5030b
commit b455badc4c
13 changed files with 812 additions and 687 deletions

149
src/framebuffer.cpp Normal file
View File

@ -0,0 +1,149 @@
#include "holly/holly.hpp"
#include "holly/core_bits.hpp"
#include "holly/texture_memory_alloc9.hpp"
#include "framebuffer.hpp"
struct framebuffer framebuffer(640, 480);
void framebuffer_init()
{
int x_size = framebuffer.px_width;
int y_size = framebuffer.px_height;
// write
holly.FB_X_CLIP = fb_x_clip::fb_x_clip_max(x_size - 1)
| fb_x_clip::fb_x_clip_min(0);
holly.FB_Y_CLIP = fb_y_clip::fb_y_clip_max(y_size - 1)
| fb_y_clip::fb_y_clip_min(0);
// read
holly.FB_R_SIZE = fb_r_size::fb_modulus(1)
| fb_r_size::fb_y_size(y_size - 1)
| fb_r_size::fb_x_size((x_size * bytes_per_pixel) / 4 - 1);
holly.FB_R_CTRL = fb_r_ctrl::vclk_div::pclk_vclk_1
| fb_r_ctrl::fb_depth::_565_rgb_16bit
| fb_r_ctrl::fb_enable;
}
void scaler_init()
{
holly.Y_COEFF = y_coeff::coefficient_1(0x80)
| y_coeff::coefficient_0_2(0x40);
// in 6.10 fixed point; 0x0400 is 1x vertical scale
holly.SCALER_CTL = scaler_ctl::vertical_scale_factor(0x0400);
holly.FB_BURSTCTRL = fb_burstctrl::wr_burst(0x09)
| fb_burstctrl::vid_lat(0x3f)
| fb_burstctrl::vid_burst(0x39);
}
void spg_set_mode_720x480()
{
holly.SPG_CONTROL
= spg_control::sync_direction::output;
holly.SPG_LOAD
= spg_load::vcount(525 - 1) // number of lines per field
| spg_load::hcount(858 - 1); // number of video clock cycles per line
holly.SPG_HBLANK
= spg_hblank::hbend(117) // H Blank ending position
| spg_hblank::hbstart(837); // H Blank starting position
holly.SPG_VBLANK
= spg_vblank::vbend(40) // V Blank ending position
| spg_vblank::vbstart(520); // V Blank starting position
holly.SPG_WIDTH
= spg_width::eqwidth(16 - 1) // Specify the equivalent pulse width (number of video clock cycles - 1)
| spg_width::bpwidth(794 - 1) // Specify the broad pulse width (number of video clock cycles - 1)
| spg_width::vswidth(3) // V Sync width (number of lines)
| spg_width::hswidth(64 - 1); // H Sync width (number of video clock cycles - 1)
holly.VO_STARTX
= vo_startx::horizontal_start_position(117);
holly.VO_STARTY
= vo_starty::vertical_start_position_on_field_2(40)
| vo_starty::vertical_start_position_on_field_1(40);
holly.VO_CONTROL
= vo_control::pclk_delay(22);
holly.SPG_HBLANK_INT
= spg_hblank_int::line_comp_val(837);
holly.SPG_VBLANK_INT
= spg_vblank_int::vblank_out_interrupt_line_number(21)
| spg_vblank_int::vblank_in_interrupt_line_number(520);
}
void spg_set_mode_640x480()
{
holly.SPG_CONTROL
= spg_control::sync_direction::output;
holly.SPG_LOAD
= spg_load::vcount(525 - 1) // number of lines per field
| spg_load::hcount(858 - 1); // number of video clock cycles per line
holly.SPG_HBLANK
= spg_hblank::hbend(126) // H Blank ending position
| spg_hblank::hbstart(837); // H Blank starting position
holly.SPG_VBLANK
= spg_vblank::vbend(40) // V Blank ending position
| spg_vblank::vbstart(520); // V Blank starting position
holly.SPG_WIDTH
= spg_width::eqwidth(16 - 1) // Specify the equivalent pulse width (number of video clock cycles - 1)
| spg_width::bpwidth(794 - 1) // Specify the broad pulse width (number of video clock cycles - 1)
| spg_width::vswidth(3) // V Sync width (number of lines)
| spg_width::hswidth(64 - 1); // H Sync width (number of video clock cycles - 1)
holly.VO_STARTX
= vo_startx::horizontal_start_position(168);
holly.VO_STARTY
= vo_starty::vertical_start_position_on_field_2(40)
| vo_starty::vertical_start_position_on_field_1(40);
holly.VO_CONTROL
= vo_control::pclk_delay(22);
holly.SPG_HBLANK_INT
= spg_hblank_int::line_comp_val(837);
holly.SPG_VBLANK_INT
= spg_vblank_int::vblank_out_interrupt_line_number(21)
| spg_vblank_int::vblank_in_interrupt_line_number(520);
}
void core_param_init()
{
uint32_t region_array_start = texture_memory_alloc.region_array.start;
uint32_t isp_tsp_parameters_start = texture_memory_alloc.isp_tsp_parameters.start;
uint32_t background_start = texture_memory_alloc.framebuffer[0].start;
holly.REGION_BASE = region_array_start;
holly.PARAM_BASE = isp_tsp_parameters_start;
uint32_t background_offset = background_start - isp_tsp_parameters_start;
holly.ISP_BACKGND_T
= isp_backgnd_t::tag_address(background_offset / 4)
| isp_backgnd_t::tag_offset(0)
| isp_backgnd_t::skip(1);
holly.ISP_BACKGND_D = _i(1.f/100000.f);
holly.FB_W_CTRL
= fb_w_ctrl::fb_packmode::_565_rgb_16bit;
holly.FB_W_LINESTRIDE = (framebuffer.px_width * bytes_per_pixel) / 8;
}

View File

@ -1,6 +1,6 @@
const int bytes_per_pixel = 2;
#pragma once
constexpr int div(int n, int d)
constexpr inline int div(int n, int d)
{
return (n + 32 - 1) / 32;
}
@ -20,146 +20,13 @@ struct framebuffer {
return div(px_height, 32);
}
};
struct framebuffer framebuffer(640, 480);
void framebuffer_init()
{
int x_size = framebuffer.px_width;
int y_size = framebuffer.px_height;
const int bytes_per_pixel = 2;
// write
extern struct framebuffer framebuffer;
holly.FB_X_CLIP = fb_x_clip::fb_x_clip_max(x_size - 1)
| fb_x_clip::fb_x_clip_min(0);
holly.FB_Y_CLIP = fb_y_clip::fb_y_clip_max(y_size - 1)
| fb_y_clip::fb_y_clip_min(0);
// read
holly.FB_R_SIZE = fb_r_size::fb_modulus(1)
| fb_r_size::fb_y_size(y_size - 1)
| fb_r_size::fb_x_size((x_size * bytes_per_pixel) / 4 - 1);
holly.FB_R_CTRL = fb_r_ctrl::vclk_div::pclk_vclk_1
| fb_r_ctrl::fb_depth::_565_rgb_16bit
| fb_r_ctrl::fb_enable;
}
void scaler_init()
{
holly.Y_COEFF = y_coeff::coefficient_1(0x80)
| y_coeff::coefficient_0_2(0x40);
// in 6.10 fixed point; 0x0400 is 1x vertical scale
holly.SCALER_CTL = scaler_ctl::vertical_scale_factor(0x0400);
holly.FB_BURSTCTRL = fb_burstctrl::wr_burst(0x09)
| fb_burstctrl::vid_lat(0x3f)
| fb_burstctrl::vid_burst(0x39);
}
void spg_set_mode_720x480()
{
holly.SPG_CONTROL
= spg_control::sync_direction::output;
holly.SPG_LOAD
= spg_load::vcount(525 - 1) // number of lines per field
| spg_load::hcount(858 - 1); // number of video clock cycles per line
holly.SPG_HBLANK
= spg_hblank::hbend(117) // H Blank ending position
| spg_hblank::hbstart(837); // H Blank starting position
holly.SPG_VBLANK
= spg_vblank::vbend(40) // V Blank ending position
| spg_vblank::vbstart(520); // V Blank starting position
holly.SPG_WIDTH
= spg_width::eqwidth(16 - 1) // Specify the equivalent pulse width (number of video clock cycles - 1)
| spg_width::bpwidth(794 - 1) // Specify the broad pulse width (number of video clock cycles - 1)
| spg_width::vswidth(3) // V Sync width (number of lines)
| spg_width::hswidth(64 - 1); // H Sync width (number of video clock cycles - 1)
holly.VO_STARTX
= vo_startx::horizontal_start_position(117);
holly.VO_STARTY
= vo_starty::vertical_start_position_on_field_2(40)
| vo_starty::vertical_start_position_on_field_1(40);
holly.VO_CONTROL
= vo_control::pclk_delay(22);
holly.SPG_HBLANK_INT
= spg_hblank_int::line_comp_val(837);
holly.SPG_VBLANK_INT
= spg_vblank_int::vblank_out_interrupt_line_number(21)
| spg_vblank_int::vblank_in_interrupt_line_number(520);
}
void spg_set_mode_640x480()
{
holly.SPG_CONTROL
= spg_control::sync_direction::output;
holly.SPG_LOAD
= spg_load::vcount(525 - 1) // number of lines per field
| spg_load::hcount(858 - 1); // number of video clock cycles per line
holly.SPG_HBLANK
= spg_hblank::hbend(126) // H Blank ending position
| spg_hblank::hbstart(837); // H Blank starting position
holly.SPG_VBLANK
= spg_vblank::vbend(40) // V Blank ending position
| spg_vblank::vbstart(520); // V Blank starting position
holly.SPG_WIDTH
= spg_width::eqwidth(16 - 1) // Specify the equivalent pulse width (number of video clock cycles - 1)
| spg_width::bpwidth(794 - 1) // Specify the broad pulse width (number of video clock cycles - 1)
| spg_width::vswidth(3) // V Sync width (number of lines)
| spg_width::hswidth(64 - 1); // H Sync width (number of video clock cycles - 1)
holly.VO_STARTX
= vo_startx::horizontal_start_position(168);
holly.VO_STARTY
= vo_starty::vertical_start_position_on_field_2(40)
| vo_starty::vertical_start_position_on_field_1(40);
holly.VO_CONTROL
= vo_control::pclk_delay(22);
holly.SPG_HBLANK_INT
= spg_hblank_int::line_comp_val(837);
holly.SPG_VBLANK_INT
= spg_vblank_int::vblank_out_interrupt_line_number(21)
| spg_vblank_int::vblank_in_interrupt_line_number(520);
}
void core_param_init()
{
uint32_t region_array_start = texture_memory_alloc.region_array.start;
uint32_t isp_tsp_parameters_start = texture_memory_alloc.isp_tsp_parameters.start;
uint32_t background_start = texture_memory_alloc.framebuffer[0].start;
holly.REGION_BASE = region_array_start;
holly.PARAM_BASE = isp_tsp_parameters_start;
uint32_t background_offset = background_start - isp_tsp_parameters_start;
holly.ISP_BACKGND_T
= isp_backgnd_t::tag_address(background_offset / 4)
| isp_backgnd_t::tag_offset(0)
| isp_backgnd_t::skip(1);
holly.ISP_BACKGND_D = _i(1.f/100000.f);
holly.FB_W_CTRL
= fb_w_ctrl::fb_packmode::_565_rgb_16bit;
holly.FB_W_LINESTRIDE = (framebuffer.px_width * bytes_per_pixel) / 8;
}
void framebuffer_init();
void scaler_init();
void spg_set_mode_720x480();
void spg_set_mode_640x480();
void core_param_init();

View File

@ -24,14 +24,8 @@
#include "font/tandy1k.data.h"
#include "math/float_types.hpp"
#include "printf/unparse.h"
#include "ta_parameter.hpp"
#include "framebuffer.hpp"
#include "interpreter.hpp"
#include "src/tracker_scene/scene.hpp"
constexpr uint32_t ta_alloc = 0
| ta_alloc_ctrl::pt_opb::_32x4byte
@ -169,545 +163,11 @@ void graphics_init()
transfer_palettes();
}
struct vertex {
vec3 p;
vec2 t;
};
const vertex quad_vertices[] = {
{ { 0, 0, 0.001f }, {0, 0} },
{ { 1, 0, 0.001f }, {1, 0} },
{ { 1, 1, 0.001f }, {1, 1} },
{ { 0, 1, 0.001f }, {0, 1} },
};
const int texture_width = 128;
const int texture_height = 256;
const int glyph_width = 9;
const int glyph_height = 12;
const int glyphs_per_row = texture_width / glyph_width;
const int glyph_hori_advance = 8;
const int glyph_vert_advance = 9;
static inline vec2 transform_glyph_texture(const vec2& t, int char_code)
{
int row = char_code / glyphs_per_row;
int col = char_code % glyphs_per_row;
return {
(float)(col * glyph_width + t.x * glyph_width) / (float)(texture_width),
(float)(row * glyph_height + t.y * glyph_height) / (float)(texture_height),
};
}
static inline vec3 transform_glyph_position(const vec3& p, float x, float y)
{
return {
p.x * glyph_width + x,
p.y * glyph_height + y,
p.z
};
}
void transfer_glyph(ta_parameter_writer& writer, int c, int x, int y, int base_color)
{
vec3 ap = transform_glyph_position(quad_vertices[0].p, x, y);
vec3 bp = transform_glyph_position(quad_vertices[1].p, x, y);
vec3 cp = transform_glyph_position(quad_vertices[2].p, x, y);
vec3 dp = transform_glyph_position(quad_vertices[3].p, x, y);
vec2 at = transform_glyph_texture(quad_vertices[0].t, c);
vec2 bt = transform_glyph_texture(quad_vertices[1].t, c);
vec2 ct = transform_glyph_texture(quad_vertices[2].t, c);
vec2 dt = transform_glyph_texture(quad_vertices[3].t, c);
quad_type_3(writer,
ap, at,
bp, bt,
cp, ct,
dp, dt,
base_color);
}
int transfer_string(ta_parameter_writer& writer, const char * s, int x, int y,
int base_color)
{
const uint8_t * u8 = (const uint8_t *)s;
int len = 0;
while (*u8) {
len += 1;
transfer_glyph(writer, *u8++, x, y, base_color);
x += glyph_hori_advance;
}
return len;
}
int transfer_integer(ta_parameter_writer& writer, int n, int x, int y,
int length, int fill,
int base_color)
{
char buf[16];
int len = unparse_base10(buf, n, length, fill);
buf[len] = 0;
transfer_string(writer, buf, x, y, base_color);
return len;
}
int transfer_hex_integer(ta_parameter_writer& writer, int n, int x, int y,
int length, int fill,
int base_color)
{
char buf[16];
int len = unparse_base16(buf, n, length, fill);
buf[len] = 0;
transfer_string(writer, buf, x, y, base_color);
return len;
}
const char note_names[12][2] = {
{'C', '-'},
{'C', '#'},
{'D', '-'},
{'D', '#'},
{'E', '-'},
{'F', '-'},
{'F', '#'},
{'G', '-'},
{'G', '#'},
{'A', '-'},
{'A', '#'},
{'B', '-'},
};
const int horizontal_bar = 0x16;
const int interpunct = 0xb7;
int transfer_fill(ta_parameter_writer& writer, int count, int x, int y,
int base_color)
{
for (int i = 0; i < count; i++) {
transfer_glyph(writer, interpunct, x, y, base_color);
x += glyph_hori_advance;
}
return x;
}
static inline int effect_type_char(int effect_type)
{
if (effect_type >= 0 && effect_type <= 9)
return effect_type + '0';
if (effect_type >= 10 && effect_type <= 36)
return (effect_type - 10) + 'a';
return 128;
}
static inline int transfer_note(ta_parameter_writer& writer, int note, int x, int y)
{
int base_color = 0xffffff;
if (note == 97) {
transfer_glyph(writer, horizontal_bar, x, y, base_color);
x += glyph_hori_advance;
transfer_glyph(writer, horizontal_bar, x, y, base_color);
x += glyph_hori_advance;
transfer_glyph(writer, horizontal_bar, x, y, base_color);
x += glyph_hori_advance;
} else if (note != 0) {
int c0_note = note - 1;
const char * note_name = note_names[c0_note % 12];
int octave = c0_note / 12;
transfer_glyph(writer, note_name[0], x, y, base_color);
x += glyph_hori_advance;
transfer_glyph(writer, note_name[1], x, y, base_color);
x += glyph_hori_advance;
transfer_glyph(writer, '0' + octave, x, y, base_color);
x += glyph_hori_advance;
} else {
x = transfer_fill(writer, 3, x, y, base_color);
}
return x;
}
static inline int transfer_instrument(ta_parameter_writer& writer, int instrument, int x, int y)
{
int base_color = 0x9393ff;
if (instrument != 0) {
int len = transfer_hex_integer(writer, instrument, x, y, 2, interpunct, base_color);
x += glyph_hori_advance * len;
} else {
x = transfer_fill(writer, 2, x, y, base_color);
}
return x;
}
static inline int transfer_volume_column_byte(ta_parameter_writer& writer, int volume_column_byte, int x, int y)
{
int base_color = 0xa7c9f1;
if (volume_column_byte != 0) {
int len = transfer_hex_integer(writer, volume_column_byte, x, y, 2, interpunct, base_color);
x += glyph_hori_advance * len;
} else {
x = transfer_fill(writer, 2, x, y, base_color);
}
return x;
}
static inline int transfer_effect_type(ta_parameter_writer& writer, int effect_type, int x, int y)
{
int base_color = 0xffffff;
if (effect_type != 0) {
int c = effect_type_char(effect_type);
transfer_glyph(writer, c, x, y, base_color);
x += glyph_hori_advance;
} else {
x = transfer_fill(writer, 1, x, y, base_color);
}
return x;
}
static inline int transfer_effect_parameter(ta_parameter_writer& writer, int effect_parameter, int x, int y)
{
int base_color = 0x7f7f80;
if (effect_parameter != 0) {
int len = transfer_hex_integer(writer, effect_parameter, x, y, 2, interpunct, base_color);
x += glyph_hori_advance * len;
} else {
x = transfer_fill(writer, 2, x, y, base_color);
}
return x;
}
static inline int transfer_line_index(ta_parameter_writer& writer, int line_index, int x, int y)
{
const int dark = 0x5d646b;
const int light = 0xa7a7a7;
int base_color = ((line_index % 4) == 0) ? dark : light;
int len = transfer_integer(writer, line_index, x, y, 2, ' ', base_color);
x += glyph_hori_advance * len;
return x;
}
void transfer_line(ta_parameter_writer& writer, int line_index, int x, int y)
{
using namespace interpreter;
x += 3;
x += 1;
x = transfer_line_index(writer, line_index, x, y);
x += 1;
x += 3;
int line_pattern_index = line_index * state.xm.number_of_channels;
const xm_pattern_format_t * pattern = state.xm.pattern[state.pattern_index];
for (int ch = 0; ch < state.xm.number_of_channels; ch++) {
const xm_pattern_format_t& pf = pattern[line_pattern_index + ch];
x += 1;
x = transfer_note(writer, pf.note, x, y);
x += 1;
x = transfer_instrument(writer, pf.instrument, x, y);
x += 1;
x = transfer_volume_column_byte(writer, pf.volume_column_byte, x, y);
x += 1;
x = transfer_effect_type(writer, pf.effect_type, x, y);
x += 1;
x = transfer_effect_parameter(writer, pf.effect_parameter, x, y);
x += 1;
x += 3;
}
}
void transfer_vertical_border(ta_parameter_writer& writer, float x, float y, int length)
{
quad_type_0(writer,
{x + 0, y + 0, 1.0/7.0},
{x + 1, y + 0, 1.0/7.0},
{x + 1, y + length, 1.0/7.0},
{x + 0, y + length, 1.0/7.0},
0x2a3536);
quad_type_0(writer,
{x + 1, y + 0, 1.0/5.0},
{x + 2, y + 0, 1.0/5.0},
{x + 2, y + length, 1.0/5.0},
{x + 1, y + length, 1.0/5.0},
0x202829);
quad_type_0(writer,
{x + 2, y + 0, 1.0/10.0},
{x + 3, y + 0, 1.0/10.0},
{x + 3, y + length, 1.0/10.0},
{x + 2, y + length, 1.0/10.0},
0x101414);
}
void transfer_horizontal_border(ta_parameter_writer& writer, float x, float y, int length)
{
quad_type_0(writer,
{x + 0, y + 0, 1.0/7.0},
{x + 0, y + 1, 1.0/7.0},
{x + length, y + 1, 1.0/7.0},
{x + length, y + 0, 1.0/7.0},
0x2a3536);
quad_type_0(writer,
{x + 0, y + 1, 1.0/5.0},
{x + 0, y + 2, 1.0/5.0},
{x + length, y + 2, 1.0/5.0},
{x + length, y + 1, 1.0/5.0},
0x202829);
quad_type_0(writer,
{x + 0, y + 2, 1.0/10.0},
{x + 0, y + 3, 1.0/10.0},
{x + length, y + 3, 1.0/10.0},
{x + length, y + 2, 1.0/10.0},
0x101414);
}
const int line_column_total_advance = (3 + 2 + 2 + 1 + 2) * glyph_hori_advance;
const int line_column_width = line_column_total_advance + 6 + 3;
const int line_rows_half = 15;
const int line_rows = line_rows_half * 2 + 1;
const int line_column_height = line_rows * glyph_vert_advance + 3 + 1;
void transfer_borders(ta_parameter_writer& writer, int x, int y)
{
using namespace interpreter;
int x0 = x;
transfer_vertical_border(writer, x, y, line_column_height);
x += 3 + glyph_hori_advance * 2 + 2;
for (int ch = 0; ch < (state.xm.number_of_channels); ch++) {
transfer_vertical_border(writer, x, y, line_column_height);
x += line_column_width;
}
transfer_vertical_border(writer, x, y, line_column_height);
transfer_horizontal_border(writer, x0, y,
x - x0);
transfer_horizontal_border(writer, x0, y + line_column_height,
x - x0);
}
void transfer_lines(ta_parameter_writer& writer, int x, int y)
{
using namespace interpreter;
y += 3 + 1;
int pattern_line_count = state.xm.pattern_note_count[state.pattern_index] / state.xm.number_of_channels;
for (int i = 0; i < line_rows; i++) {
int line_ix = state.line_index - line_rows_half + i;
if (line_ix >= 0 && line_ix < pattern_line_count)
transfer_line(writer, line_ix, x, y);
y += glyph_vert_advance;
}
}
void transfer_middle_line(ta_parameter_writer& writer, float x, float y)
{
using namespace interpreter;
int middle_width = line_column_width * (state.xm.number_of_channels) + (2 * glyph_hori_advance) + 2 + 3;
int middle_height = glyph_vert_advance - 1;
y += 3;
y += glyph_vert_advance * line_rows_half;
quad_type_0(writer,
{x + 0, y + 0, 1.0/15.0},
{x + 0, y + 1, 1.0/15.0},
{x + middle_width, y + 1, 1.0/15.0},
{x + middle_width, y + 0, 1.0/15.0},
0x555555);
y += 1;
quad_type_0(writer,
{x + 0, y + 0, 1.0/15.0},
{x + 0, y + middle_height, 1.0/15.0},
{x + middle_width, y + middle_height, 1.0/15.0},
{x + middle_width, y + 0, 1.0/15.0},
0x404040);
y += middle_width;
quad_type_0(writer,
{x + 0, y + 0, 1.0/15.0},
{x + 0, y + 1, 1.0/15.0},
{x + middle_width, y + 1, 1.0/15.0},
{x + middle_width, y + 0, 1.0/15.0},
0x202020);
}
static inline int round_up_div(int n, int m)
{
int div = n / m;
int rem = n % m;
if (rem == 0)
return div;
return div + 1;
}
static inline int round_up(int n, int m)
{
int rem = n % m;
if (rem == 0)
return n;
return n + m - rem;
}
void transfer_rectangle(ta_parameter_writer& writer,
float x, float y, float z,
float width, float height,
uint32_t base_color)
{
quad_type_0(writer,
{x + 0, y + 0, z},
{x + width, y + 0, z},
{x + width, y + height, z},
{x + 0, y + height, z},
base_color);
}
void transfer_channel_status(ta_parameter_writer& writer, int x, int y)
{
using namespace interpreter;
const int channel_status_height = 50;
int max_channels_per_row = framebuffer.px_width / 40;
int rows = round_up_div(state.xm.number_of_channels, max_channels_per_row);
assert(rows > 0);
int number_of_channels = round_up(state.xm.number_of_channels, rows);
int channels_per_row = number_of_channels / rows;
int width_per_col = (framebuffer.px_width - 3) / channels_per_row;
int offset = (framebuffer.px_width - (width_per_col * channels_per_row)) / 2;
int inner_width = width_per_col - 3;
int height_per_row = channel_status_height / rows;
int vert_center = height_per_row / 2 - glyph_vert_advance / 2;
int ch = 0;
for (int row = 0; row < rows; row++) {
int xi = x + offset - 2;
global_polygon_untextured(writer,
para_control::list_type::opaque,
tsp_instruction_word::dst_alpha_instr::zero);
transfer_horizontal_border(writer, xi, y, width_per_col * channels_per_row);
for (int col = 0; col < channels_per_row; col++) {
global_polygon_untextured(writer,
para_control::list_type::opaque,
tsp_instruction_word::dst_alpha_instr::zero);
transfer_vertical_border(writer, xi, y, height_per_row);
int keyon = 128 * (state.channel[ch].keyon - 224) / 16;
if (keyon < 0) keyon = 0;
uint32_t base_color = (keyon << 16) | (keyon << 8) | (keyon << 0);
transfer_rectangle(writer,
xi, y, 1.0 / 10000.0,
width_per_col, height_per_row,
base_color);
if (ch < state.xm.number_of_channels) {
global_polygon_textured(writer,
para_control::list_type::opaque);
int hori_center = inner_width / 2 - (glyph_hori_advance * (ch >= 10)) / 2;
transfer_integer(writer, ch, xi + hori_center, y + vert_center,
0, 0,
0xa7a7a7);
}
xi += width_per_col;
ch += 1;
}
global_polygon_untextured(writer,
para_control::list_type::opaque,
tsp_instruction_word::dst_alpha_instr::zero);
transfer_vertical_border(writer, xi, y, height_per_row);
y += height_per_row;
}
int xi = x + offset - 2;
transfer_horizontal_border(writer, xi, y, width_per_col * channels_per_row);
}
void transfer_scene(ta_parameter_writer& writer)
{
const int x = 3;
const int y = 100;
{ // punch-through
global_polygon_textured(writer,
para_control::list_type::punch_through);
transfer_lines(writer, x, y);
writer.append<ta_global_parameter::end_of_list>() =
ta_global_parameter::end_of_list(para_control::para_type::end_of_list);
}
{ // translucent
global_polygon_untextured(writer,
para_control::list_type::translucent,
tsp_instruction_word::dst_alpha_instr::one);
transfer_middle_line(writer, x, y);
writer.append<ta_global_parameter::end_of_list>() =
ta_global_parameter::end_of_list(para_control::para_type::end_of_list);
}
{ // opaque
global_polygon_untextured(writer,
para_control::list_type::opaque,
tsp_instruction_word::dst_alpha_instr::zero);
transfer_borders(writer, x, y);
transfer_channel_status(writer, 0, 0);
writer.append<ta_global_parameter::end_of_list>() =
ta_global_parameter::end_of_list(para_control::para_type::end_of_list);
}
}
void graphics_event(ta_parameter_writer& writer)
{
writer.offset = 0;
transfer_scene(writer);
tracker::scene::transfer(writer);
while (ta_in_use);
while (core_in_use);

159
src/graphics_primitive.cpp Normal file
View File

@ -0,0 +1,159 @@
#include "printf/unparse.h"
#include "ta_parameter.hpp"
#include "graphics_primitive.hpp"
struct vertex {
vec3 p;
vec2 t;
};
const vertex quad_vertices[] = {
{ { 0, 0, 0.001f }, {0, 0} },
{ { 1, 0, 0.001f }, {1, 0} },
{ { 1, 1, 0.001f }, {1, 1} },
{ { 0, 1, 0.001f }, {0, 1} },
};
static inline vec2 transform_glyph_texture(const vec2& t, int char_code)
{
int row = char_code / glyph::row_stride;
int col = char_code % glyph::row_stride;
return {
(float)(col * glyph::width + t.x * glyph::width) / (float)(glyph::texture_width),
(float)(row * glyph::height + t.y * glyph::height) / (float)(glyph::texture_height),
};
}
static inline vec3 transform_glyph_position(const vec3& p, float x, float y)
{
return {
p.x * glyph::width + x,
p.y * glyph::height + y,
p.z
};
}
void transfer_glyph(ta_parameter_writer& writer, int c, int x, int y,
int base_color)
{
vec3 ap = transform_glyph_position(quad_vertices[0].p, x, y);
vec3 bp = transform_glyph_position(quad_vertices[1].p, x, y);
vec3 cp = transform_glyph_position(quad_vertices[2].p, x, y);
vec3 dp = transform_glyph_position(quad_vertices[3].p, x, y);
vec2 at = transform_glyph_texture(quad_vertices[0].t, c);
vec2 bt = transform_glyph_texture(quad_vertices[1].t, c);
vec2 ct = transform_glyph_texture(quad_vertices[2].t, c);
vec2 dt = transform_glyph_texture(quad_vertices[3].t, c);
quad_type_3(writer,
ap, at,
bp, bt,
cp, ct,
dp, dt,
base_color);
}
int transfer_string(ta_parameter_writer& writer, const char * s, int x, int y,
int base_color)
{
const uint8_t * u8 = (const uint8_t *)s;
int len = 0;
while (*u8) {
len += 1;
transfer_glyph(writer, *u8++, x, y, base_color);
x += glyph::hori_advance;
}
return len;
}
int transfer_integer(ta_parameter_writer& writer, int n, int x, int y,
int length, int fill,
int base_color)
{
char buf[16];
int len = unparse_base10(buf, n, length, fill);
buf[len] = 0;
transfer_string(writer, buf, x, y, base_color);
return len;
}
int transfer_hex_integer(ta_parameter_writer& writer, int n, int x, int y,
int length, int fill,
int base_color)
{
char buf[16];
int len = unparse_base16(buf, n, length, fill);
buf[len] = 0;
transfer_string(writer, buf, x, y, base_color);
return len;
}
void transfer_rectangle(ta_parameter_writer& writer,
float x, float y, float z,
float width, float height,
int base_color)
{
quad_type_0(writer,
{x + 0, y + 0, z},
{x + width, y + 0, z},
{x + width, y + height, z},
{x + 0, y + height, z},
base_color);
}
void transfer_vertical_border(ta_parameter_writer& writer, float x, float y, int length)
{
quad_type_0(writer,
{x + 0, y + 0, 1.0/7.0},
{x + 1, y + 0, 1.0/7.0},
{x + 1, y + length, 1.0/7.0},
{x + 0, y + length, 1.0/7.0},
0x2a3536);
quad_type_0(writer,
{x + 1, y + 0, 1.0/5.0},
{x + 2, y + 0, 1.0/5.0},
{x + 2, y + length, 1.0/5.0},
{x + 1, y + length, 1.0/5.0},
0x202829);
quad_type_0(writer,
{x + 2, y + 0, 1.0/10.0},
{x + 3, y + 0, 1.0/10.0},
{x + 3, y + length, 1.0/10.0},
{x + 2, y + length, 1.0/10.0},
0x101414);
}
void transfer_horizontal_border(ta_parameter_writer& writer, float x, float y, int length)
{
quad_type_0(writer,
{x + 0, y + 0, 1.0/7.0},
{x + 0, y + 1, 1.0/7.0},
{x + length, y + 1, 1.0/7.0},
{x + length, y + 0, 1.0/7.0},
0x2a3536);
quad_type_0(writer,
{x + 0, y + 1, 1.0/5.0},
{x + 0, y + 2, 1.0/5.0},
{x + length, y + 2, 1.0/5.0},
{x + length, y + 1, 1.0/5.0},
0x202829);
quad_type_0(writer,
{x + 0, y + 2, 1.0/10.0},
{x + 0, y + 3, 1.0/10.0},
{x + length, y + 3, 1.0/10.0},
{x + length, y + 2, 1.0/10.0},
0x101414);
}

View File

@ -0,0 +1,39 @@
#pragma once
#include "holly/ta_parameter.hpp"
namespace glyph {
constexpr int texture_width = 128;
constexpr int texture_height = 256;
constexpr int width = 9;
constexpr int height = 12;
constexpr int hori_advance = 8;
constexpr int vert_advance = 9;
constexpr int row_stride = glyph::texture_width / glyph::width;
const int horizontal_bar = 0x16;
const int interpunct = 0xb7;
};
void transfer_glyph(ta_parameter_writer& writer, int c, int x, int y,
int base_color);
int transfer_string(ta_parameter_writer& writer, const char * s, int x, int y,
int base_color);
int transfer_integer(ta_parameter_writer& writer, int n, int x, int y,
int length, int fill,
int base_color);
int transfer_hex_integer(ta_parameter_writer& writer, int n, int x, int y,
int length, int fill,
int base_color);
void transfer_rectangle(ta_parameter_writer& writer,
float x, float y, float z,
float width, float height,
int base_color);
void transfer_vertical_border(ta_parameter_writer& writer, float x, float y, int length);
void transfer_horizontal_border(ta_parameter_writer& writer, float x, float y, int length);

View File

@ -1,4 +1,14 @@
void global_polygon_textured(ta_parameter_writer& writer, uint32_t list_type)
#pragma once
#include "holly/ta_global_parameter.hpp"
#include "holly/ta_vertex_parameter.hpp"
#include "holly/ta_parameter.hpp"
#include "holly/ta_global_parameter.hpp"
#include "holly/isp_tsp.hpp"
#include "holly/texture_memory_alloc9.hpp"
#include "math/float_types.hpp"
static inline void global_polygon_textured(ta_parameter_writer& writer, uint32_t list_type)
{
const uint32_t parameter_control_word = para_control::para_type::polygon_or_modifier_volume
| list_type
@ -32,7 +42,7 @@ void global_polygon_textured(ta_parameter_writer& writer, uint32_t list_type)
}
void global_polygon_untextured(ta_parameter_writer& writer, uint32_t list_type, uint32_t dst_alpha)
static inline void global_polygon_untextured(ta_parameter_writer& writer, uint32_t list_type, uint32_t dst_alpha)
{
const uint32_t parameter_control_word = para_control::para_type::polygon_or_modifier_volume
| list_type

View File

@ -0,0 +1,104 @@
#include "../ta_parameter.hpp"
#include "../graphics_primitive.hpp"
#include "../interpreter.hpp"
#include "../framebuffer.hpp"
#include "channel_status.hpp"
static inline int round_up_div(int n, int m)
{
int div = n / m;
int rem = n % m;
if (rem == 0)
return div;
return div + 1;
}
static inline int round_up(int n, int m)
{
int rem = n % m;
if (rem == 0)
return n;
return n + m - rem;
}
namespace tracker {
namespace channel_status {
void transfer(ta_parameter_writer& writer, int x, int y)
{
using namespace interpreter;
const int channel_status_height = 50;
int max_channels_per_row = framebuffer.px_width / 40;
int rows = round_up_div(state.xm.number_of_channels, max_channels_per_row);
assert(rows > 0);
int number_of_channels = round_up(state.xm.number_of_channels, rows);
int channels_per_row = number_of_channels / rows;
int width_per_col = (framebuffer.px_width - 3) / channels_per_row;
int offset = (framebuffer.px_width - (width_per_col * channels_per_row)) / 2;
int inner_width = width_per_col - 3;
int height_per_row = channel_status_height / rows;
int vert_center = height_per_row / 2 - glyph::vert_advance / 2;
int ch = 0;
for (int row = 0; row < rows; row++) {
int xi = x + offset - 2;
global_polygon_untextured(writer,
para_control::list_type::opaque,
tsp_instruction_word::dst_alpha_instr::zero);
transfer_horizontal_border(writer, xi, y, width_per_col * channels_per_row);
for (int col = 0; col < channels_per_row; col++) {
global_polygon_untextured(writer,
para_control::list_type::opaque,
tsp_instruction_word::dst_alpha_instr::zero);
transfer_vertical_border(writer, xi, y, height_per_row);
int keyon = 128 * (state.channel[ch].keyon - 224) / 16;
if (keyon < 0) keyon = 0;
uint32_t base_color = (keyon << 16) | (keyon << 8) | (keyon << 0);
transfer_rectangle(writer,
xi, y, 1.0 / 10000.0,
width_per_col, height_per_row,
base_color);
if (ch < state.xm.number_of_channels) {
global_polygon_textured(writer,
para_control::list_type::opaque);
int hori_center = inner_width / 2 - (glyph::hori_advance * (ch >= 10)) / 2;
transfer_integer(writer, ch, xi + hori_center, y + vert_center,
0, 0,
0xa7a7a7);
}
xi += width_per_col;
ch += 1;
}
global_polygon_untextured(writer,
para_control::list_type::opaque,
tsp_instruction_word::dst_alpha_instr::zero);
transfer_vertical_border(writer, xi, y, height_per_row);
y += height_per_row;
}
int xi = x + offset - 2;
transfer_horizontal_border(writer, xi, y, width_per_col * channels_per_row);
//borders(writer, x, y);
}
}
}

View File

@ -0,0 +1,9 @@
#pragma once
#include "holly/ta_parameter.hpp"
namespace tracker {
namespace channel_status {
void transfer(ta_parameter_writer& writer, int x, int y);
}
}

253
src/tracker_scene/notes.cpp Normal file
View File

@ -0,0 +1,253 @@
#include "../ta_parameter.hpp"
#include "../graphics_primitive.hpp"
#include "../interpreter.hpp"
#include "notes.hpp"
const int line_column_total_advance = (3 + 2 + 2 + 1 + 2) * glyph::hori_advance;
const int line_column_width = line_column_total_advance + 6 + 3;
const int line_rows_half = 15;
const int line_rows = line_rows_half * 2 + 1;
const int line_column_height = line_rows * glyph::vert_advance + 3 + 1;
static inline int transfer_line_index(ta_parameter_writer& writer, int line_index, int x, int y)
{
const int dark = 0x5d646b;
const int light = 0xa7a7a7;
int base_color = ((line_index % 4) == 0) ? dark : light;
int len = transfer_integer(writer, line_index, x, y, 2, ' ', base_color);
x += glyph::hori_advance * len;
return x;
}
static inline int transfer_fill(ta_parameter_writer& writer, int count, int x, int y,
int base_color)
{
for (int i = 0; i < count; i++) {
transfer_glyph(writer, glyph::interpunct, x, y, base_color);
x += glyph::hori_advance;
}
return x;
}
static inline int transfer_note(ta_parameter_writer& writer, int note, int x, int y)
{
static const char note_names[12][2] = {
{'C', '-'},
{'C', '#'},
{'D', '-'},
{'D', '#'},
{'E', '-'},
{'F', '-'},
{'F', '#'},
{'G', '-'},
{'G', '#'},
{'A', '-'},
{'A', '#'},
{'B', '-'},
};
int base_color = 0xffffff;
if (note == 97) {
transfer_glyph(writer, glyph::horizontal_bar, x, y, base_color);
x += glyph::hori_advance;
transfer_glyph(writer, glyph::horizontal_bar, x, y, base_color);
x += glyph::hori_advance;
transfer_glyph(writer, glyph::horizontal_bar, x, y, base_color);
x += glyph::hori_advance;
} else if (note != 0) {
int c0_note = note - 1;
const char * note_name = note_names[c0_note % 12];
int octave = c0_note / 12;
transfer_glyph(writer, note_name[0], x, y, base_color);
x += glyph::hori_advance;
transfer_glyph(writer, note_name[1], x, y, base_color);
x += glyph::hori_advance;
transfer_glyph(writer, '0' + octave, x, y, base_color);
x += glyph::hori_advance;
} else {
x = transfer_fill(writer, 3, x, y, base_color);
}
return x;
}
static inline int transfer_instrument(ta_parameter_writer& writer, int instrument, int x, int y)
{
int base_color = 0x9393ff;
if (instrument != 0) {
int len = transfer_hex_integer(writer, instrument, x, y, 2, glyph::interpunct, base_color);
x += glyph::hori_advance * len;
} else {
x = transfer_fill(writer, 2, x, y, base_color);
}
return x;
}
static inline int transfer_volume_column_byte(ta_parameter_writer& writer, int volume_column_byte, int x, int y)
{
int base_color = 0xa7c9f1;
if (volume_column_byte != 0) {
int len = transfer_hex_integer(writer, volume_column_byte, x, y, 2, glyph::interpunct, base_color);
x += glyph::hori_advance * len;
} else {
x = transfer_fill(writer, 2, x, y, base_color);
}
return x;
}
static inline int effect_type_char(int effect_type)
{
if (effect_type >= 0 && effect_type <= 9)
return effect_type + '0';
if (effect_type >= 10 && effect_type <= 36)
return (effect_type - 10) + 'a';
return 128;
}
static inline int transfer_effect_type(ta_parameter_writer& writer, int effect_type, int x, int y)
{
int base_color = 0xffffff;
if (effect_type != 0) {
int c = effect_type_char(effect_type);
transfer_glyph(writer, c, x, y, base_color);
x += glyph::hori_advance;
} else {
x = transfer_fill(writer, 1, x, y, base_color);
}
return x;
}
static inline int transfer_effect_parameter(ta_parameter_writer& writer, int effect_parameter, int x, int y)
{
int base_color = 0x7f7f80;
if (effect_parameter != 0) {
int len = transfer_hex_integer(writer, effect_parameter, x, y, 2, glyph::interpunct,
base_color);
x += glyph::hori_advance * len;
} else {
x = transfer_fill(writer, 2, x, y, base_color);
}
return x;
}
static void transfer_line(ta_parameter_writer& writer, int line_index, int x, int y)
{
using namespace interpreter;
x += 3;
x += 1;
x = transfer_line_index(writer, line_index, x, y);
x += 1;
x += 3;
int line_pattern_index = line_index * state.xm.number_of_channels;
const xm_pattern_format_t * pattern = state.xm.pattern[state.pattern_index];
for (int ch = 0; ch < state.xm.number_of_channels; ch++) {
const xm_pattern_format_t& pf = pattern[line_pattern_index + ch];
x += 1;
x = transfer_note(writer, pf.note, x, y);
x += 1;
x = transfer_instrument(writer, pf.instrument, x, y);
x += 1;
x = transfer_volume_column_byte(writer, pf.volume_column_byte, x, y);
x += 1;
x = transfer_effect_type(writer, pf.effect_type, x, y);
x += 1;
x = transfer_effect_parameter(writer, pf.effect_parameter, x, y);
x += 1;
x += 3;
}
}
namespace tracker {
namespace notes {
void borders(ta_parameter_writer& writer, int x, int y)
{
using namespace interpreter;
int x0 = x;
transfer_vertical_border(writer, x, y, line_column_height);
x += 3 + glyph::hori_advance * 2 + 2;
for (int ch = 0; ch < (state.xm.number_of_channels); ch++) {
transfer_vertical_border(writer, x, y, line_column_height);
x += line_column_width;
}
transfer_vertical_border(writer, x, y, line_column_height);
transfer_horizontal_border(writer, x0, y,
x - x0);
transfer_horizontal_border(writer, x0, y + line_column_height,
x - x0);
}
void transfer_lines(ta_parameter_writer& writer, int x, int y)
{
using namespace interpreter;
y += 3 + 1;
int pattern_line_count = state.xm.pattern_note_count[state.pattern_index] / state.xm.number_of_channels;
for (int i = 0; i < line_rows; i++) {
int line_ix = state.line_index - line_rows_half + i;
if (line_ix >= 0 && line_ix < pattern_line_count)
transfer_line(writer, line_ix, x, y);
y += glyph::vert_advance;
}
}
void transfer_middle_line(ta_parameter_writer& writer, float x, float y)
{
using namespace interpreter;
int middle_width = line_column_width * (state.xm.number_of_channels) + (2 * glyph::hori_advance) + 2 + 3;
int middle_height = glyph::vert_advance - 1;
y += 3;
y += glyph::vert_advance * line_rows_half;
quad_type_0(writer,
{x + 0, y + 0, 1.0/15.0},
{x + 0, y + 1, 1.0/15.0},
{x + middle_width, y + 1, 1.0/15.0},
{x + middle_width, y + 0, 1.0/15.0},
0x555555);
y += 1;
quad_type_0(writer,
{x + 0, y + 0, 1.0/15.0},
{x + 0, y + middle_height, 1.0/15.0},
{x + middle_width, y + middle_height, 1.0/15.0},
{x + middle_width, y + 0, 1.0/15.0},
0x404040);
y += middle_width;
quad_type_0(writer,
{x + 0, y + 0, 1.0/15.0},
{x + 0, y + 1, 1.0/15.0},
{x + middle_width, y + 1, 1.0/15.0},
{x + middle_width, y + 0, 1.0/15.0},
0x202020);
}
}
}

View File

@ -0,0 +1,13 @@
#pragma once
#include "holly/ta_parameter.hpp"
namespace tracker {
namespace notes {
void borders(ta_parameter_writer& writer, int x, int y);
void transfer_lines(ta_parameter_writer& writer, int x, int y);
void transfer_middle_line(ta_parameter_writer& writer, float x, float y);
}
}

View File

@ -0,0 +1,50 @@
#include "../ta_parameter.hpp"
#include "notes.hpp"
#include "channel_status.hpp"
namespace tracker {
namespace scene {
void transfer(ta_parameter_writer& writer)
{
const int x = 3;
const int y = 100;
{ // punch-through
global_polygon_textured(writer,
para_control::list_type::punch_through);
tracker::notes::transfer_lines(writer, x, y);
writer.append<ta_global_parameter::end_of_list>() =
ta_global_parameter::end_of_list(para_control::para_type::end_of_list);
}
{ // translucent
global_polygon_untextured(writer,
para_control::list_type::translucent,
tsp_instruction_word::dst_alpha_instr::one);
tracker::notes::transfer_middle_line(writer, x, y);
writer.append<ta_global_parameter::end_of_list>() =
ta_global_parameter::end_of_list(para_control::para_type::end_of_list);
}
{ // opaque
global_polygon_untextured(writer,
para_control::list_type::opaque,
tsp_instruction_word::dst_alpha_instr::zero);
tracker::notes::borders(writer, x, y);
tracker::channel_status::transfer(writer, 0, 0);
writer.append<ta_global_parameter::end_of_list>() =
ta_global_parameter::end_of_list(para_control::para_type::end_of_list);
}
}
}
}

View File

@ -0,0 +1,7 @@
#pragma once
namespace tracker {
namespace scene {
void transfer(ta_parameter_writer& writer);
}
}

View File

@ -21,7 +21,12 @@ XM_PLAYER_OBJ = \
src/main.o \
src/sound.o \
src/xm.o \
src/malloc.o
src/malloc.o \
src/framebuffer.o \
src/graphics_primitive.o \
src/tracker_scene/scene.o \
src/tracker_scene/channel_status.o \
src/tracker_scene/notes.o
xm_player.elf: LDSCRIPT = $(LIB)/main.lds
xm_player.elf: $(START_OBJ) $(XM_PLAYER_OBJ) $(TEXTURE_OBJ) $(XM_OBJ) $(LIBGCC)