diff --git a/example/aica/aica_xm.cpp b/example/aica/aica_xm.cpp index 08ddceb..803ebbb 100644 --- a/example/aica/aica_xm.cpp +++ b/example/aica/aica_xm.cpp @@ -1,3 +1,17 @@ +#include "holly/background.hpp" +#include "holly/core.hpp" +#include "holly/core_bits.hpp" +#include "holly/holly.hpp" +#include "holly/isp_tsp.hpp" +#include "holly/region_array.hpp" +#include "holly/ta_bits.hpp" +#include "holly/ta_fifo_polygon_converter.hpp" +#include "holly/ta_global_parameter.hpp" +#include "holly/ta_parameter.hpp" +#include "holly/ta_vertex_parameter.hpp" +#include "holly/texture_memory_alloc5.hpp" +#include "holly/video_output.hpp" + #include "memorymap.hpp" #include "systembus.hpp" #include "systembus_bits.hpp" @@ -8,6 +22,8 @@ #include "sh7091/serial.hpp" #include "printf/printf.h" +#include "math/float_types.hpp" + #include "assert.h" //#include "example/arm/xm.bin.h" @@ -16,6 +32,8 @@ #include "xm/middle_c.xm.h" #include "xm/test.xm.h" +#include "interrupt.hpp" + constexpr int max_patterns = 64; constexpr int max_instruments = 128; struct xm_state { @@ -483,12 +501,371 @@ void next_pattern(interpreter_state& state, int pattern_break) state.pattern_index = 1; } -uint8_t __attribute__((aligned(32))) zero[0x28c0] = {}; - -void main() +void vbr100() { - serial::init(0); + serial::string("vbr100\n"); + interrupt_exception(); +} +void vbr400() +{ + serial::string("vbr400\n"); + interrupt_exception(); +} + +constexpr int div(int n, int d) +{ + return (n + 32 - 1) / 32; +} + +struct framebuffer { + int px_width; + int px_height; + + framebuffer(int width, int height) + : px_width(width), px_height(height) + {} + + int tile_width() { + return div(px_width, 32); + } + int tile_height() { + return div(px_height, 32); + } +}; +struct framebuffer framebuffer(640, 480); +const int bytes_per_pixel = 2; + +constexpr uint32_t ta_alloc = 0 + | ta_alloc_ctrl::pt_opb::no_list + | ta_alloc_ctrl::tm_opb::no_list + | ta_alloc_ctrl::t_opb::no_list + | ta_alloc_ctrl::om_opb::no_list + | ta_alloc_ctrl::o_opb::_32x4byte; + +constexpr int ta_cont_count = 1; +constexpr struct opb_size opb_size[ta_cont_count] = { + { + .opaque = 32 * 4, + .opaque_modifier = 0, + .translucent = 0, + .translucent_modifier = 0, + .punch_through = 0 + } +}; + +static volatile int ta_in_use = 0; +static volatile int core_in_use = 0; +static volatile int next_frame = 0; +static volatile int framebuffer_ix = 0; +static volatile int next_frame_ix = 0; + +static inline void pump_events(uint32_t istnrm) +{ + if (istnrm & istnrm::v_blank_in) { + system.ISTNRM = istnrm::v_blank_in; + + next_frame = 1; + holly.FB_R_SOF1 = texture_memory_alloc.framebuffer[next_frame_ix].start; + } + + if (istnrm & istnrm::end_of_render_tsp) { + system.ISTNRM = istnrm::end_of_render_tsp + | istnrm::end_of_render_isp + | istnrm::end_of_render_video; + + next_frame_ix = framebuffer_ix; + framebuffer_ix += 1; + if (framebuffer_ix >= 3) framebuffer_ix = 0; + + core_in_use = 0; + } + + if (istnrm & istnrm::end_of_transferring_opaque_list) { + system.ISTNRM = istnrm::end_of_transferring_opaque_list; + + core_in_use = 1; + 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[framebuffer_ix].start, + framebuffer_width); + + ta_in_use = 0; + } +} + +struct interpreter_state state; + +static inline void tmu0_events() +{ + xm_pattern_header_t * pattern_header = xm.pattern_header[state.pattern_index]; + int pattern_data_size = s16(&pattern_header->packed_pattern_data_size); + + bool keyoff_tick = (state.tick + 1) % (state.ticks_per_line * 2) == 0; + bool note_tick = state.tick % (state.ticks_per_line * 2) == 0; + bool effect_tick = (state.tick & 1) == 0; + bool pattern_break_tick = (state.tick % (state.ticks_per_line * 2)) == (state.ticks_per_line * 2 - 1); + if (keyoff_tick) { + // execute keyoffs + parse_pattern_line(state, pattern_header, state.next_note_offset, rekey_note); + wait(); aica_sound.channel[0].KYONEX(1); + } + + if (state.pattern_break >= 0 && pattern_break_tick) { + printf("pattern_break\n"); + next_pattern(state, -1); + } + + if (note_tick) { + state.note_offset = state.next_note_offset; + state.next_note_offset = parse_pattern_line(state, pattern_header, state.note_offset, play_debug_note); + //state.next_note_offset = parse_pattern_line(state, pattern_header, state.note_offset, play_note); + state.line_index += 1; + wait(); aica_sound.channel[0].KYONEX(1); + } + if (effect_tick && !note_tick) { + // execute effects + parse_pattern_line(state, pattern_header, state.note_offset, play_note_effect); + wait(); aica_sound.channel[0].KYONEX(1); + } + + if (state.next_note_offset >= pattern_data_size && pattern_break_tick) { + printf("pattern_data_size\n"); + next_pattern(state, -1); + } + + state.tick += 1; +} + +void vbr600() +{ + uint32_t sr; + asm volatile ("stc sr,%0" : "=r" (sr)); + sr |= sh::sr::imask(15); + asm volatile ("ldc %0,sr" : : "r" (sr)); + + if (sh7091.CCN.EXPEVT == 0 && sh7091.CCN.INTEVT == 0x320) { // Holly + uint32_t istnrm = system.ISTNRM; + uint32_t isterr = system.ISTERR; + + if (isterr) { + serial::string("isterr: "); + serial::integer(system.ISTERR); + } + + pump_events(istnrm); + } else if (sh7091.CCN.EXPEVT == 0 && sh7091.CCN.INTEVT == 0x400) { // TMU0 + sh7091.TMU.TCR0 + = tmu::tcr0::UNIE + | tmu::tcr0::tpsc::p_phi_256; // clear underflow + + tmu0_events(); + } else { + serial::string("vbr600\n"); + interrupt_exception(); + } + + sr &= ~sh::sr::imask(15); + asm volatile ("ldc %0,sr" : : "r" (sr)); +} + +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_dither + | fb_w_ctrl::fb_packmode::_565_rgb_16bit; + + holly.FB_W_LINESTRIDE = (framebuffer_width * bytes_per_pixel) / 8; +} + +void graphics_init() +{ + holly.SOFTRESET = softreset::pipeline_soft_reset + | softreset::ta_soft_reset; + holly.SOFTRESET = 0; + + scaler_init(); + core_init(); + core_param_init(); + spg_set_mode_640x480(); + framebuffer_init(); + + background_parameter2(texture_memory_alloc.framebuffer[0].start, + 0xff800080); + + region_array_multipass(framebuffer.tile_width(), + framebuffer.tile_height(), + opb_size, + ta_cont_count, + texture_memory_alloc.region_array.start, + texture_memory_alloc.object_list.start); +} + +void transfer_scene(ta_parameter_writer& writer) +{ +} + +void graphics_event(ta_parameter_writer& writer) +{ + writer.offset = 0; + + transfer_scene(writer); + + while (ta_in_use); + while (core_in_use); + ta_in_use = 1; + ta_polygon_converter_init2(texture_memory_alloc.isp_tsp_parameters.start, + texture_memory_alloc.isp_tsp_parameters.end, + texture_memory_alloc.object_list.start, + texture_memory_alloc.object_list.end, + opb_size[0].total(), + ta_alloc, + framebuffer.tile_width(), + framebuffer.tile_height()); + ta_polygon_converter_writeback(writer.buf, writer.offset); + ta_polygon_converter_transfer(writer.buf, writer.offset); + + while (next_frame == 0); + next_frame = 0; +} + +void sound_init() +{ int buf = (int)&_binary_xm_milkypack01_xm_start; //int buf = (int)&_binary_xm_middle_c_xm_start; //int buf = (int)&_binary_xm_test_xm_start; @@ -582,8 +959,6 @@ void main() printf("default_bpm %d\n", xm.header->default_bpm); printf("default_tempo %d\n", xm.header->default_tempo); - struct interpreter_state state; - state.tick_rate = 195.32 * 2500 / xm.header->default_bpm; state.ticks_per_line = xm.header->default_tempo; state.tick = 0; @@ -600,53 +975,29 @@ void main() sh7091.TMU.TSTR = 0; // stop all timers sh7091.TMU.TCOR0 = state.tick_rate / 2; sh7091.TMU.TOCR = tmu::tocr::tcoe::tclk_is_external_clock_or_input_capture; - sh7091.TMU.TCR0 = tmu::tcr0::tpsc::p_phi_256; // 256 / 50MHz = 5.12 μs ; underflows in ~1 hour + sh7091.TMU.TCR0 + = tmu::tcr0::UNIE + | tmu::tcr0::tpsc::p_phi_256; // 256 / 50MHz = 5.12 μs ; underflows in ~1 hour sh7091.TMU.TCNT0 = 0; sh7091.TMU.TSTR = tmu::tstr::str0::counter_start; - while (1) { - xm_pattern_header_t * pattern_header = xm.pattern_header[state.pattern_index]; - int pattern_data_size = s16(&pattern_header->packed_pattern_data_size); - - while ((sh7091.TMU.TCR0 & tmu::tcr0::UNF) == 0) { - } - sh7091.TMU.TCR0 = tmu::tcr0::tpsc::p_phi_256; // clear underflow - - bool keyoff_tick = (state.tick + 1) % (state.ticks_per_line * 2) == 0; - bool note_tick = state.tick % (state.ticks_per_line * 2) == 0; - bool effect_tick = (state.tick & 1) == 0; - bool pattern_break_tick = (state.tick % (state.ticks_per_line * 2)) == (state.ticks_per_line * 2 - 1); - if (keyoff_tick) { - // execute keyoffs - parse_pattern_line(state, pattern_header, state.next_note_offset, rekey_note); - wait(); aica_sound.channel[0].KYONEX(1); - } - - if (state.pattern_break >= 0 && pattern_break_tick) { - printf("pattern_break\n"); - next_pattern(state, -1); - } - - if (note_tick) { - state.note_offset = state.next_note_offset; - state.next_note_offset = parse_pattern_line(state, pattern_header, state.note_offset, play_debug_note); - //state.next_note_offset = parse_pattern_line(state, pattern_header, state.note_offset, play_note); - state.line_index += 1; - wait(); aica_sound.channel[0].KYONEX(1); - } - if (effect_tick && !note_tick) { - // execute effects - parse_pattern_line(state, pattern_header, state.note_offset, play_note_effect); - wait(); aica_sound.channel[0].KYONEX(1); - } - - if (state.next_note_offset >= pattern_data_size && pattern_break_tick) { - printf("pattern_data_size\n"); - next_pattern(state, -1); - } - - state.tick += 1; - } - - while (1); + sh7091.INTC.IPRA = intc::ipra::TMU0(1); +} + +uint8_t __attribute__((aligned(32))) zero[0x28c0] = {}; + +void main() +{ + serial::init(0); + + sound_init(); + graphics_init(); + interrupt_init(); + + static uint8_t __attribute__((aligned(32))) ta_parameter_buf[1024 * 1024 * 1]; + ta_parameter_writer writer = ta_parameter_writer(ta_parameter_buf, (sizeof (ta_parameter_buf))); + + while (1) { + graphics_event(writer); + } }