diff --git a/CLOCKTOW.PCM b/CLOCKTOW.PCM new file mode 100644 index 0000000..e9c1e71 Binary files /dev/null and b/CLOCKTOW.PCM differ diff --git a/ECCLESIA.PCM b/ECCLESIA.PCM new file mode 100644 index 0000000..e37637d Binary files /dev/null and b/ECCLESIA.PCM differ diff --git a/ELEC.PCM b/ELEC.PCM new file mode 100644 index 0000000..fcf89ef Binary files /dev/null and b/ELEC.PCM differ diff --git a/PILLAR.PCM b/PILLAR.PCM new file mode 100644 index 0000000..6e7e196 Binary files /dev/null and b/PILLAR.PCM differ diff --git a/PRELUDE.PCM b/PRELUDE.PCM new file mode 100644 index 0000000..1f6139e Binary files /dev/null and b/PRELUDE.PCM differ diff --git a/REIGN.PCM b/REIGN.PCM new file mode 100644 index 0000000..cafb942 Binary files /dev/null and b/REIGN.PCM differ diff --git a/RIDDLE.PCM b/RIDDLE.PCM new file mode 100644 index 0000000..06e2243 Binary files /dev/null and b/RIDDLE.PCM differ diff --git a/color_format.hpp b/color_format.hpp index 49f31bc..0f9730f 100644 --- a/color_format.hpp +++ b/color_format.hpp @@ -4,6 +4,11 @@ namespace color_format { +uint32_t argb8888(uint8_t a, uint8_t r, uint8_t g, uint8_t b) +{ + return (a << 24) | (r << 16) | (g << 8) | (b << 0); +} + uint16_t argb4444(uint8_t a, uint8_t r, uint8_t g, uint8_t b) { int a4 = (a >> 4) & 15; diff --git a/common.mk b/common.mk index b5fd50c..486af18 100644 --- a/common.mk +++ b/common.mk @@ -2,7 +2,7 @@ MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) DIR := $(dir $(MAKEFILE_PATH)) LIB ?= . -OPT ?= -Os +OPT ?= -O3 GENERATED ?= AARCH = --isa=sh4 --little @@ -79,7 +79,14 @@ sine.pcm: common.mk /1ST_READ.BIN=./$< \ /=./COPYRIGH.TXT \ /=./ABSTRACT.TXT \ - /=./BIBLIOGR.TXT + /=./BIBLIOGR.TXT \ + /=./REIGN.PCM \ + /=./PILLAR.PCM \ + /=./RIDDLE.PCM \ + /=./PRELUDE.PCM \ + /=./CLOCKTOW.PCM \ + /=./ELEC.PCM \ + /=./ECCLESIA.PCM %.cdi: %.iso ./cdi4dc $< $@ >/dev/null diff --git a/example/aica_gdrom_dft.cpp b/example/aica_gdrom_dft.cpp new file mode 100644 index 0000000..6d85145 --- /dev/null +++ b/example/aica_gdrom_dft.cpp @@ -0,0 +1,669 @@ +#include "memorymap.hpp" + +#include "holly/isp_tsp.hpp" +#include "holly/ta_parameter.hpp" +#include "holly/ta_global_parameter.hpp" +#include "holly/ta_vertex_parameter.hpp" +#include "holly/ta_bits.hpp" +#include "holly/ta_fifo_polygon_converter.hpp" +#include "holly/holly.hpp" +#include "holly/core_bits.hpp" +#include "holly/core.hpp" +#include "holly/region_array.hpp" +#include "holly/background.hpp" +#include "holly/video_output.hpp" +#include "holly/texture_memory_alloc2.hpp" + +#include "sh7091/store_queue.hpp" +#include "sh7091/serial.hpp" +#include "sh7091/sh7091.hpp" +#include "sh7091/sh7091_bits.hpp" + +#include "systembus.hpp" +#include "systembus_bits.hpp" + +#include "aica/aica.hpp" + +#include "gdrom/gdrom.hpp" +#include "gdrom/gdrom_bits.hpp" +#include "gdrom/command_packet_format.hpp" +#include "gdrom/toc.hpp" + +#include "iso9660/primary_volume_descriptor.hpp" +#include "iso9660/directory_record.hpp" + +#include "math/fft.hpp" + +#include "color_format.hpp" +#include "twiddle.hpp" + +extern void * _binary_start __asm("_binary_example_arm_sh4_interrupt_bin_start"); +extern void * _binary_size __asm("_binary_example_arm_sh4_interrupt_bin_size"); + +constexpr uint32_t mcipd__sh4_interrupt = (1 << 5); +constexpr uint32_t miceb__sh4_interrupt = (1 << 5); +constexpr uint32_t scipd__arm_interrupt = (1 << 5); + +constexpr int32_t sectors_per_chunk = 16; +constexpr int32_t chunk_size = 2048 * sectors_per_chunk; +constexpr int32_t samples_per_chunk = chunk_size / 2; +constexpr int32_t samples_per_line = 1024; +constexpr int32_t lines_per_chunk = samples_per_chunk / (samples_per_line * 2); + +struct vertex { + float x; + float y; + float z; + float u; + float v; +}; + +// screen space coordinates +const struct vertex v[4] = { + { 64.f, 0.f, 0.1f, 0.0f, 0.0f }, + { 576.f, 0.f, 0.1f, 1.0f, 0.0f }, + { 576.f, 512.f, 0.1f, 1.0f, 1.0f / 2 }, + { 64.f, 512.f, 0.1f, 0.0f, 1.0f / 2 }, +}; + +static int __x = 0; + +void transfer_scene() +{ + const uint32_t parameter_control_word = para_control::para_type::sprite + | para_control::list_type::opaque + | obj_control::col_type::packed_color + | obj_control::texture + | obj_control::_16bit_uv; + + const uint32_t isp_tsp_instruction_word = isp_tsp_instruction_word::depth_compare_mode::greater + | isp_tsp_instruction_word::culling_mode::no_culling; + + const uint32_t tsp_instruction_word = tsp_instruction_word::src_alpha_instr::one + | tsp_instruction_word::dst_alpha_instr::zero + | tsp_instruction_word::dst_select::primary_accumulation_buffer + | tsp_instruction_word::fog_control::no_fog + | tsp_instruction_word::texture_shading_instruction::modulate + | tsp_instruction_word::texture_u_size::from_int(samples_per_line) + | tsp_instruction_word::texture_v_size::from_int(samples_per_line) + | tsp_instruction_word::filter_mode::point_sampled; + + const uint32_t texture_address = texture_memory_alloc::texture.start + 0; + const uint32_t texture_control_word = texture_control_word::pixel_format::_8bpp_palette + | texture_control_word::scan_order::twiddled + | texture_control_word::texture_address(texture_address / 8); + + constexpr uint32_t base_color = 0xffffffff; + *reinterpret_cast(store_queue) = + ta_global_parameter::sprite(parameter_control_word, + isp_tsp_instruction_word, + tsp_instruction_word, + texture_control_word, + base_color, + 0, // offset_color + 0, // data_size_for_sort_dma + 0); // next_address_for_sort_dma + sq_transfer_32byte(ta_fifo_polygon_converter); + + /* + float u[3]; + for (int i = 0; i < 3; i++) { + u[i] = v[i].u + (float)((((__x / 2) * 2) + 3) - (samples_per_line - 2)) / (float)(samples_per_line - 2); + while (u[i] >= 1) u[i] -= 1; + } + */ + + *reinterpret_cast(store_queue) = + ta_vertex_parameter::sprite_type_1(para_control::para_type::vertex_parameter, + v[0].x, + v[0].y, + v[0].z, + v[1].x, + v[1].y, + v[1].z, + v[2].x, + v[2].y, + v[2].z, + v[3].x, + v[3].y, + uv_16bit(v[0].u, v[0].v), + uv_16bit(v[1].u, v[1].v), + uv_16bit(v[2].u, v[2].v) + ); + sq_transfer_64byte(ta_fifo_polygon_converter); + + *reinterpret_cast(store_queue) = + ta_global_parameter::end_of_list(para_control::para_type::end_of_list); + sq_transfer_32byte(ta_fifo_polygon_converter); +} + +void aica_wait_write() +{ + while (ffst::aica_internal_write_buffer(system.FFST)); +} + +void aica_wait_read() +{ + uint32_t ffst = system.FFST; + while ( ffst::holly_cpu_if_block_internal_write_buffer(ffst) + | ffst::holly_g2_if_block_internal_write_buffer(ffst) + | ffst::aica_internal_write_buffer(ffst)) { + ffst = system.FFST; + }; +} + +void aica_fill_chunk(volatile uint32_t * dest_chunk, const uint32_t * src_chunk, const uint32_t size) +{ + for (uint32_t i = 0; i < size / 4; i++) { + if (i % 8 == 0) aica_wait_write(); + dest_chunk[i] = src_chunk[i]; + } +} + +static volatile uint32_t (* chunk)[2][chunk_size / 4]; + +void aica_init(uint32_t& chunk_index, const uint32_t * src_chunk) +{ + const uint32_t * binary = reinterpret_cast(&_binary_start); + const uint32_t binary_size = reinterpret_cast(&_binary_size); + + aica_wait_write(); aica_sound.common.vreg_armrst = aica::vreg_armrst::ARMRST(1); + aica_wait_write(); aica_sound.common.dmea0_mrwinh = aica::dmea0_mrwinh::MRWINH(0); + for (uint32_t i = 0; i < binary_size / 4; i++) { + // copy + while (aica_wave_memory[i] != binary[i]) { + aica_wait_write(); + aica_wave_memory[i] = binary[i]; + } + } + + chunk = reinterpret_cast(&aica_wave_memory[0x00100000 / 4]); + + serial::integer(reinterpret_cast(&(*chunk)[0][0])); + serial::integer(reinterpret_cast(&(*chunk)[1][0])); + + aica_fill_chunk(&(*chunk)[chunk_index][0], + src_chunk, + chunk_size); + chunk_index = (chunk_index + 1) % 2; + + aica_wait_write(); aica_sound.common.vreg_armrst = aica::vreg_armrst::ARMRST(0); + + { // send arm interrupt + aica_sound.common.scipd = scipd__arm_interrupt; + } +} + +static int32_t step_time = -1; +static int32_t step_end = -1; +static int32_t step_start = -1; + +extern "C" +void * memset(void * dest, int c, size_t n) +{ + unsigned char * s = (unsigned char *)dest; + for (; n; n--, s++) *s = c; + return dest; +} + +static fft::complex comp[samples_per_line * 2]; + +void render_column(int col, int x, const uint32_t * buf) +{ + uint32_t offset = texture_memory_alloc::texture.start + 0; + auto texture = reinterpret_cast(&texture_memory64[offset / 4]); + + const int16_t * src = &((int16_t *)(buf))[col * samples_per_line * 2]; + fft::int16_to_complex(src, samples_per_line * 2, comp); + fft::fft(comp, samples_per_line * 2); + for (int32_t y = 0; y < samples_per_line; y++) { + const fft::complex& cx = comp[y]; + float abs = sqrt(cx.real * cx.real + cx.imag * cx.imag); + int v = abs / (65536 / 8); + if (v > 255) v = 255; + if (v < 0) v = 0; + uint32_t ix = twiddle::from_xy(x, y, samples_per_line, samples_per_line); + uint32_t value = texture[ix / 4]; + int shift = (ix % 4) * 8; + value &= ~(0xff << shift); + value |= v << shift; + texture[ix / 4] = value; + } +} + +void render(); +void texture_init(); + +void aica_step(uint32_t& chunk_index, const uint32_t gdrom_buf[2][chunk_size / 4]) +{ + { // wait for interrupt from arm + int col = 0; + while ((system.ISTEXT & (1 << 1)) == 0) { + if (step_time >= 0) { + int32_t dt = step_start - (int32_t)sh7091.TMU.TCNT0; + if (step_time * col / lines_per_chunk < dt) { + render_column(col, __x, gdrom_buf[!chunk_index]); + render(); + col += 1; + __x += 1; + __x %= samples_per_line; + } + } + }; + aica_wait_write(); aica_sound.common.mcire = mcipd__sh4_interrupt; + } + step_end = sh7091.TMU.TCNT0; + step_time = step_start - step_end; + serial::string("aica step time: "); + serial::integer(step_time); + step_start = sh7091.TMU.TCNT0; + + { // fill the requested chunk + aica_fill_chunk(&(*chunk)[chunk_index][0], + gdrom_buf[chunk_index], + chunk_size); + + chunk_index = (chunk_index + 1) % 2; + } + + { // send arm interrupt + aica_sound.common.scipd = scipd__arm_interrupt; + } +} + +// gdrom + +void gdrom_pio_data(const uint8_t * data) +{ + while ((gdrom::status::bsy(gdrom_if.status) | gdrom::status::drq(gdrom_if.status)) != 0); + + gdrom_if.features = gdrom::features::dma::disable; + gdrom_if.drive_select = gdrom::drive_select::drive_select + | gdrom::drive_select::lun(0); + + gdrom_if.command = gdrom::command::code::packet_command; + while (gdrom::status::drq(gdrom_if.status) == 0); + + const uint16_t * buf = reinterpret_cast(&data[0]); + for (int i = 0; i < 6; i++) { + gdrom_if.data = buf[i]; + } + + while (gdrom::status::bsy(gdrom_if.status) != 0); +} + +void gdrom_read_data(uint16_t * buf, const uint32_t length) +{ + //serial::string("read_data drq interrupt_reason: "); + //serial::integer(gdrom::status::drq(gdrom_if.status), ' '); + //serial::integer(gdrom_if.interrupt_reason); + for (uint32_t i = 0; i < (length / 2); i++) { + buf[i] = gdrom_if.data; + } +} + +uint32_t gdrom_toc__get_data_track_fad() +{ + auto packet = gdrom_command_packet_format::get_toc(0, // single-density + 0x0198 // maximum toc length + ); + serial::string("get_toc\n"); + gdrom_pio_data(packet._data()); + + serial::string("byte_count: "); + serial::integer(gdrom_if.byte_count()); + uint16_t buf[gdrom_if.byte_count() / 2]; + gdrom_read_data(buf, gdrom_if.byte_count()); + + serial::string("status: "); + serial::integer(gdrom_if.status); + + auto toc = reinterpret_cast(buf); + for (int i = 0; i < 99; i++) { + if (toc->track[i].fad() == 0xffffff) + break; + serial::string("track "); + serial::integer(i); + serial::integer(toc->track[i].fad()); + } + + // assume track 1 is the correct track + return toc->track[1].fad(); +} + +uint32_t gdrom_cd_read2(uint16_t * buf, + const uint32_t starting_address, + const uint32_t transfer_length, + const uint32_t next_address) +{ + const uint8_t data_select = 0b0010; // data + const uint8_t expected_data_type = 0b100; // XA mode 2 form 1 + const uint8_t parameter_type = 0b0; // FAD specified + const uint8_t data = (data_select << 4) | (expected_data_type << 1) | (parameter_type << 0); + + auto packet = gdrom_command_packet_format::cd_read2(data, + starting_address, + transfer_length, + next_address); + //serial::string("cd_read\n"); + //serial::string("starting_address: "); + //serial::integer(starting_address); + //serial::string("transfer_length: "); + //serial::integer(transfer_length); + //serial::string("next_address: "); + //serial::integer(next_address); + gdrom_pio_data(packet._data()); + + while ((gdrom::status::bsy(gdrom_if.status)) != 0); // wait for drive to become not-busy + + uint32_t length = 0; + while ((gdrom::status::drq(gdrom_if.status)) != 0) { + const uint32_t byte_count = gdrom_if.byte_count(); + length += byte_count; + gdrom_read_data(buf, byte_count); + + serial::string("status: "); + serial::integer(gdrom_if.status); + + while ((gdrom::status::bsy(gdrom_if.status)) != 0); // wait for drive to become not-busy + } + + serial::string("length: "); + serial::integer(length); + return length; +} + +void gdrom_unlock() +{ + // gdrom unlock undocumented register + g1_if.GDUNLOCK = 0x1fffff; + + // Without this read from system_boot_rom, the read value of + // gdrom_if.status is always 0xff + for(uint32_t i = 0; i < 0x200000 / 4; i++) { + (void)system_boot_rom[i]; + } +} + +bool str_equal(const uint8_t * a, + const uint32_t a_len, + const char * b, + const uint32_t b_len) +{ + if (a_len != b_len) + return false; + + uint32_t len = a_len; + + while (len != 0) { + if (*a++ != *b++) + return false; + + len--; + } + + return true; +} + +struct extent +{ + const uint32_t location; + const uint32_t data_length; +}; + +struct extent gdrom_find_file() +{ + const uint32_t fad = gdrom_toc__get_data_track_fad(); + serial::character('\n'); + + const uint32_t primary_volume_descriptor = fad + 16; + uint16_t buf[2048 / 2]; + gdrom_cd_read2(buf, + primary_volume_descriptor, // starting address + 1, // one sector; 2048 bytes + primary_volume_descriptor + 1 // next address + ); + serial::character('\n'); + + auto pvd = reinterpret_cast(&buf[0]); + auto root_dr = reinterpret_cast(&pvd->directory_record_for_root_directory[0]); + + serial::string("primary volume descriptor:\n"); + serial::string(" standard_identifier: "); + serial::string(pvd->standard_identifier, 5); + serial::character('\n'); + serial::string(" root directory record:\n"); + serial::string(" location of extent: "); + serial::integer(root_dr->location_of_extent.get()); + serial::string(" data length: "); + serial::integer(root_dr->data_length.get()); + + serial::character('\n'); + + const uint32_t root_directory_extent = root_dr->location_of_extent.get(); + gdrom_cd_read2(buf, + root_directory_extent + 150, // 150? + 1, // one sector; 2048 bytes + root_directory_extent + 151 // 150? + ); + serial::character('\n'); + + auto buf8 = reinterpret_cast(buf); + uint32_t offset = 0; + while (true) { + serial::string("directory entry offset: "); + serial::integer(offset); + + auto dr = reinterpret_cast(&buf8[offset]); + if (dr->length_of_directory_record == 0) + break; + + serial::string(" length_of_directory_record: "); + serial::integer(dr->length_of_directory_record); + serial::string(" length_of_file_identifier: "); + serial::integer(dr->length_of_file_identifier); + serial::string(" file_identifier: "); + serial::string(dr->file_identifier, dr->length_of_file_identifier); + serial::character('\n'); + + const char filename[] = "ELEC.PCM;1"; + bool equal = str_equal(dr->file_identifier, dr->length_of_file_identifier, + filename, (sizeof (filename)) - 1); + + if (dr->file_flags == 0) { + serial::string(" location_of_extent: "); + serial::integer(dr->location_of_extent.get()); + serial::string(" data_length: "); + serial::integer(dr->data_length.get()); + + if (equal) { + serial::string("FOUND\n"); + return { + dr->location_of_extent.get(), + dr->data_length.get() + }; + } + } + + offset += dr->length_of_directory_record; + } + + return { 0 , 0 }; +} + +void gdrom_read_chunk(uint32_t * buf, const uint32_t extent, const uint32_t num_extents) +{ + const uint32_t gdrom_start = sh7091.TMU.TCNT0; + + gdrom_cd_read2(reinterpret_cast(buf), + extent + 150, // 150? + num_extents, // one sector; 2048 bytes + extent + 150 + num_extents // 150? + ); + + const uint32_t gdrom_end = sh7091.TMU.TCNT0; + const uint32_t gdrom_time = gdrom_start - gdrom_end; + serial::string("gdrom time: "); + serial::integer(gdrom_time); +} + +void next_segment(const struct extent& extent, uint32_t& segment_index) +{ + segment_index += sectors_per_chunk; + if ((segment_index * 2048) > extent.data_length) + segment_index = 0; +} + +void palette_init() +{ + holly.PAL_RAM_CTRL = pal_ram_ctrl::pixel_format::argb8888; + + for (int i = 0; i < 256; i++) { + holly.PALETTE_RAM[i] = color_format::argb8888(255, i, i, i); + } +} + +void texture_init() +{ + uint32_t offset = texture_memory_alloc::texture.start + 0; + auto texture = reinterpret_cast(&texture_memory64[offset / 4]); + for (int i = 0; i < samples_per_line * samples_per_line / 4; i++) { + texture[i] = 0x0; + } +} + +constexpr uint32_t ta_alloc = + ta_alloc_ctrl::pt_opb::_8x4byte + | 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::no_list + ; + +constexpr int render_passes = 1; +constexpr struct opb_size opb_size[render_passes] = { + { + .opaque = 8 * 4, + .opaque_modifier = 0, + .translucent = 0, + .translucent_modifier = 0, + .punch_through = 0 + } +}; + + +constexpr int framebuffer_width = 640; +constexpr int framebuffer_height = 480; +constexpr int tile_width = framebuffer_width / 32; +constexpr int tile_height = framebuffer_height / 32; + +void render() +{ + static int ta = -1; + static int core = -2; + if (core >= 0) { + // core = 0 ; core = 1 + // ta = 1 ; ta = 0 + core_wait_end_of_render_video(); + //while (!spg_status::vsync(holly.SPG_STATUS)); + holly.FB_R_SOF1 = texture_memory_alloc::framebuffer[core].start; + } + + // core = -2 ; core = 1 ; core = 0 + // ta = -1 ; ta = 0 ; ta = 1 + core += 1; + ta += 1; + if (core > 1) core = 0; + if (ta > 1) ta = 0; + + if (core >= 0) { + // core = 1 ; core = 0 + // ta = 0 ; ta = 1 + ta_wait_opaque_list(); + core_start_render2(texture_memory_alloc::region_array[core].start, + texture_memory_alloc::isp_tsp_parameters[core].start, + texture_memory_alloc::background[core].start, + texture_memory_alloc::framebuffer[core].start, + framebuffer_width); + } + + + // core = -1 ; core = 1 ; core = 0 + // ta = 0 ; ta = 0 ; ta = 1 + ta_polygon_converter_init2(texture_memory_alloc::isp_tsp_parameters[ta].start, + texture_memory_alloc::isp_tsp_parameters[ta].end, + texture_memory_alloc::object_list[ta].start, + texture_memory_alloc::object_list[ta].end, + opb_size[0].total(), + ta_alloc, + tile_width, + tile_height); + transfer_scene(); +} + +void main() +{ + serial::init(4); + + holly.SOFTRESET = softreset::pipeline_soft_reset + | softreset::ta_soft_reset; + holly.SOFTRESET = 0; + + core_init(); + + video_output::set_mode_vga(); + + region_array_multipass(tile_width, + tile_height, + opb_size, + render_passes, + texture_memory_alloc::region_array[0].start, + texture_memory_alloc::object_list[0].start); + region_array_multipass(tile_width, + tile_height, + opb_size, + render_passes, + texture_memory_alloc::region_array[1].start, + texture_memory_alloc::object_list[1].start); + + background_parameter2(texture_memory_alloc::background[0].start, + 0x00220033); + background_parameter2(texture_memory_alloc::background[1].start, + 0x00220033); + + palette_init(); + texture_init(); + + sh7091.TMU.TSTR = 0; // stop all timers + 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.TCOR0 = 0xffff'ffff; + sh7091.TMU.TCNT0 = 0xffff'ffff; + sh7091.TMU.TSTR = tmu::tstr::str0::counter_start; + + uint32_t chunk_index = 0; + uint32_t segment_index = 0; + + gdrom_unlock(); + const auto extent = gdrom_find_file(); + uint32_t gdrom_buf[2][chunk_size / 4]; + gdrom_read_chunk(gdrom_buf[chunk_index], extent.location + segment_index, sectors_per_chunk); + next_segment(extent, segment_index); + + aica_init(chunk_index, gdrom_buf[chunk_index]); + + aica_sound.common.MCIEB(miceb__sh4_interrupt); + + //render(); + //render(); + //render(); + + while (1) { + gdrom_read_chunk(gdrom_buf[chunk_index], extent.location + segment_index, sectors_per_chunk); + next_segment(extent, segment_index); + + aica_step(chunk_index, gdrom_buf); + } + + while (1); +} diff --git a/example/arm/sh4_interrupt.cpp b/example/arm/sh4_interrupt.cpp index 9e0276a..14518f2 100644 --- a/example/arm/sh4_interrupt.cpp +++ b/example/arm/sh4_interrupt.cpp @@ -39,13 +39,13 @@ void main() aica_sound.channel[0].LPCTL(1); aica_sound.channel[0].PCMS(0); aica_sound.channel[0].LSA(0); - aica_sound.channel[0].LEA((chunk_size / 2) * 2); + aica_sound.channel[0].LEA((chunk_size / 2) * 2 - 1); aica_sound.channel[0].D2R(0x0); aica_sound.channel[0].D1R(0x0); aica_sound.channel[0].RR(0x1f); aica_sound.channel[0].AR(0x1f); - aica_sound.channel[0].OCT(-1); + aica_sound.channel[0].OCT(0); aica_sound.channel[0].FNS(0x0); aica_sound.channel[0].DISDL(0xf); aica_sound.channel[0].DIPAN(0x0); @@ -72,49 +72,26 @@ void main() dram[0] = reinterpret_cast(&chunk[0][0]); dram[1] = reinterpret_cast(&chunk[1][0]); - request_chunk(); - aica_sound.channel[0].SA(reinterpret_cast(&chunk[0][0])); - aica_sound.channel[0].KYONEX(1); - - //constexpr uint32_t scipd__arm_interrupt = (1 << 5); - //constexpr uint32_t timer_a_interrupt = (1 << 6); - - uint8_t next_chunk = 1; + int started = 0; while (1) { // detect buffer underrun - /* - if (!(aica_sound.common.SCIPD() & scipd__arm_interrupt)) { - //aica_sound.channel[0].KYONB(0); - aica_sound.channel[0].KYONEX(1); - while (!(aica_sound.common.SCIPD() & scipd__arm_interrupt)); - aica_sound.channel[0].KYONB(1); - aica_sound.channel[0].SA(reinterpret_cast(&chunk[next_chunk][0])); - aica_sound.channel[0].KYONEX(1); - aica_sound.channel[0].SA(reinterpret_cast(&chunk[0][0])); - } - aica_sound.common.scire = scipd__arm_interrupt; - */ - - next_chunk = !next_chunk; - request_chunk(); uint32_t sample = 0; - constexpr uint32_t samples_per_sample = 2; + constexpr uint32_t samples_per_sample = 1; constexpr uint32_t target = chunk_size / 2 * samples_per_sample; constexpr uint32_t scire__sample_interval = (1 << 10); constexpr uint32_t scipd__sample_interval = (1 << 10); while (sample < target) { - //aica_sound.common.tactl_tima = aica::tactl_tima::TACTL(tactl) - // | aica::tactl_tima::TIMA(tima); - - //while (!(aica_sound.common.SCIPD() & timer_a_interrupt)); - //aica_sound.common.scire = timer_a_interrupt; while (!(aica_sound.common.SCIPD() & scipd__sample_interval)); aica_sound.common.scire = scire__sample_interval; - sample++; } + + request_chunk(); + if (!started) { + aica_sound.channel[0].KYONEX(1); + } } } diff --git a/example/example.mk b/example/example.mk index 3799c84..e9f2a68 100644 --- a/example/example.mk +++ b/example/example.mk @@ -17,6 +17,7 @@ SPRITE_OBJ = \ holly/region_array.o \ holly/background.o \ holly/ta_fifo_polygon_converter.o \ + sh7091/serial.o \ $(LIBGCC) example/sprite.elf: LDSCRIPT = $(LIB)/main.lds @@ -420,6 +421,20 @@ AICA_GDROM_OBJ = \ example/aica_gdrom.elf: LDSCRIPT = $(LIB)/main.lds example/aica_gdrom.elf: $(START_OBJ) $(AICA_GDROM_OBJ) +AICA_GDROM_DFT_OBJ = \ + example/aica_gdrom_dft.o \ + holly/core.o \ + holly/region_array.o \ + holly/background.o \ + holly/ta_fifo_polygon_converter.o \ + holly/video_output.o \ + sh7091/serial.o \ + example/arm/sh4_interrupt.bin.o \ + $(LIBGCC) + +example/aica_gdrom_dft.elf: LDSCRIPT = $(LIB)/main.lds +example/aica_gdrom_dft.elf: $(START_OBJ) $(AICA_GDROM_DFT_OBJ) + MAC_SATURATION_OBJ = \ example/mac_saturation.o \ example/macl_saturation.o \ diff --git a/math/fft.hpp b/math/fft.hpp new file mode 100644 index 0000000..f78ebd1 --- /dev/null +++ b/math/fft.hpp @@ -0,0 +1,78 @@ +#pragma once + +#include +#include "math.hpp" + +namespace fft { + +struct complex { + float real; + float imag; + + inline constexpr complex(); + inline constexpr complex(float real, float imag); +}; + +inline constexpr complex::complex() + : real(0), imag(0) +{} + +inline constexpr complex::complex(float real, float imag) + : real(real), imag(imag) +{} + +inline constexpr complex operator+(complex const& c1, complex const& c2) +{ + return complex(c1.real + c2.real, + c1.imag + c2.imag); +} + +inline constexpr complex operator-(complex const& c1, complex const& c2) +{ + return complex(c1.real - c2.real, + c1.imag - c2.imag); +} + +inline constexpr complex operator*(complex const& c1, complex const& c2) +{ + // (a+bi)(c+di) = (ac−bd) + (ad+bc)i + return complex(c1.real * c2.real - c1.imag * c2.imag, + c1.real * c2.imag + c1.imag * c2.real); +} + +constexpr float pi = 3.141592653589793; + +static void fft(complex * x, int length) +{ + if (length == 1) { + return; + } + + complex even[length / 2]; + complex odd[length / 2]; + + for (int i = 0; i < length / 2; i++) { + even[i] = x[i * 2 + 0]; + odd[i] = x[i * 2 + 1]; + } + + fft(even, length / 2); + fft(odd, length / 2); + + for (int k = 0; k < length / 2; k++) { + float pi_k = -2 * pi * k / length; + complex t = complex(cos(pi_k), sin(pi_k)) * odd[k]; + x[k] = even[k] + t; + x[length / 2 + k] = even[k] - t; + } +} + +static void int16_to_complex(const int16_t * buf, int length, + complex * out) +{ + for (int i = 0; i < length; i++) { + out[i] = complex(buf[i], 0); + } +} + +}