diff --git a/config/Readme.md b/config/Readme.md index fd91bf39..cf73d137 100644 --- a/config/Readme.md +++ b/config/Readme.md @@ -39,6 +39,8 @@ struct DefaultConfiguration { ``` ### `CustomConfiguration` macros ```c++ +// Turns on support of Sonys VAG files for the CDFileProcessor (off by default) +#define __SUPPORT_VAG__ // Turns on colored rectangles during boot (off by default) #define __USE_DEBUG_COLOR__ // Turns on PS3 support (on by default) diff --git a/examples/PoolBox/application/src/Custom/file_parser.cpp b/examples/PoolBox/application/src/Custom/file_parser.cpp index 9f96fb97..f9a8da64 100644 --- a/examples/PoolBox/application/src/Custom/file_parser.cpp +++ b/examples/PoolBox/application/src/Custom/file_parser.cpp @@ -16,7 +16,7 @@ namespace JabyEngine { State create_custom(const uint32_t* data_adr, const CDFileType_t& file_type, const CDFile::Payload& payload) { switch(static_cast(file_type)) { case FileType::Jingle: - return State::from(JingleState{.sfx_id = payload.raw}, reinterpret_cast(data_adr), parse_jingle); + return State::from(JingleState{.sfx_id = payload.raw}, data_adr, parse_jingle); default: return FileProcessor::create(data_adr, Nothing()); diff --git a/examples/PoolBox/application/src/Overlay/ControllerTest/controller_state.cpp b/examples/PoolBox/application/src/Overlay/ControllerTest/controller_state.cpp index cda13cab..5d2919b8 100644 --- a/examples/PoolBox/application/src/Overlay/ControllerTest/controller_state.cpp +++ b/examples/PoolBox/application/src/Overlay/ControllerTest/controller_state.cpp @@ -1,5 +1,6 @@ #include "include/controller_state.hpp" #include +#include namespace ControllerTest { using DigitalButton = Periphery::AnalogeController::Button; @@ -7,6 +8,9 @@ namespace ControllerTest { static void set_active(GPU::SPRT_16::Linked& sprt, bool is_active) { sprt->tex_offset.y = is_active ? 16 : 0; + if(is_active) { + SPU::voice[1].play_if_end(); + } } static void set_active(GPU::POLY_FT4::Linked& poly, bool is_active) { @@ -14,6 +18,10 @@ namespace ControllerTest { poly->tex_offset1.y = is_active ? 16 : 0; poly->tex_offset2.y = is_active ? 32 : 16; poly->tex_offset3.y = is_active ? 32 : 16; + + if(is_active) { + SPU::voice[1].play_if_end(); + } } static const char* get_type_name(Periphery::ControllerType type) { diff --git a/examples/PoolBox/application/src/asset_mgr.cpp b/examples/PoolBox/application/src/asset_mgr.cpp index 0fcc3493..54f9ab99 100644 --- a/examples/PoolBox/application/src/asset_mgr.cpp +++ b/examples/PoolBox/application/src/asset_mgr.cpp @@ -17,6 +17,8 @@ namespace Assets { __jabyengine_start_lba_request __jabyengine_request_lba_for(PACO, "ASSETS/MAIN/PACO.IMG"), __jabyengine_request_lba_for(DFISH, "ASSETS/MAIN/DFISH.IMG"), + __jabyengine_request_lba_for(APPLE_SFX, "SFX/APPLE.VAG"), + __jabyengine_request_lba_for(BLUBB_SFX, "SFX/BLUBB.VAG"), __jabyengine_request_lba_for(MIX_XA, "XAAUDIO/MIX.XA"), __jabyengine_request_lba_for(BIOS_INFO_OVL, "BIO.BIN"), __jabyengine_request_lba_for(GPU_TEST_OVL, "GTO.BIN"), @@ -58,9 +60,13 @@ namespace Assets { } namespace Main { + using SPU::operator""_vol; + static const CDFile Files[] = { CDFileBuilder::simple_tim(LBA::PACO, PacoTIM), CDFileBuilder::simple_tim(LBA::DFISH, DoenerFishInfo.tim), + CDFileBuilder::sony_vag(LBA::APPLE_SFX, VAG::create(0, 1.0_vol)), + CDFileBuilder::sony_vag(LBA::BLUBB_SFX, VAG::create(1, 1.0_vol)), CustomCDFileBuilder::jingle(32), }; diff --git a/examples/PoolBox/application/src/menu.cpp b/examples/PoolBox/application/src/menu.cpp index 2eace68f..1dcbfd09 100644 --- a/examples/PoolBox/application/src/menu.cpp +++ b/examples/PoolBox/application/src/menu.cpp @@ -1,6 +1,7 @@ #include "../include/shared.hpp" #include "include/menu.hpp" #include +#include namespace Menu { using DigitalButton = Periphery::GenericController::Button; @@ -24,6 +25,7 @@ namespace Menu { } if(controller.button.went_down(DigitalButton::Cross)) { + SPU::voice[0].play(); this->selection_callback(this->cur_selection); } diff --git a/examples/PoolBox/assets/Makefile b/examples/PoolBox/assets/Makefile index 2b7aa634..c93cc47b 100644 --- a/examples/PoolBox/assets/Makefile +++ b/examples/PoolBox/assets/Makefile @@ -12,6 +12,8 @@ CLUT_4_COLOR_TRANS_FLAGS = simple-tim clut4 --color-trans ## Music tracks INPUT += $(OUTPUT_DIR)/Evacuation_cdda.xa INPUT += $(OUTPUT_DIR)/fox.xa +INPUT += $(OUTPUT_DIR)/apple.vag +INPUT += $(OUTPUT_DIR)/blubb.vag ## Images INPUT += $(OUTPUT_DIR)/TexturePage.img @@ -36,6 +38,10 @@ $(OUTPUT_DIR)/fox.xa: audio/temp/fox.wav @mkdir -p $(OUTPUT_DIR) jaby_engine_fconv $< -o $@ xa +$(OUTPUT_DIR)/%.vag: audio/temp/%.wav + @mkdir -p $(OUTPUT_DIR) + jaby_engine_fconv $< -o $@ vag + $(OUTPUT_DIR)/%.xa: audio/%.wav @mkdir -p $(OUTPUT_DIR) jaby_engine_fconv $< -o $@ xa diff --git a/examples/PoolBox/iso/Config.xml b/examples/PoolBox/iso/Config.xml index f7dacfa4..10858289 100644 --- a/examples/PoolBox/iso/Config.xml +++ b/examples/PoolBox/iso/Config.xml @@ -47,6 +47,11 @@ ../assets/bin/fox.xa + ../assets/audio/temp/breaking.wav \ No newline at end of file diff --git a/include/PSX/Auxiliary/big_endian.hpp b/include/PSX/Auxiliary/big_endian.hpp new file mode 100644 index 00000000..4e24c32b --- /dev/null +++ b/include/PSX/Auxiliary/big_endian.hpp @@ -0,0 +1,19 @@ +#pragma once +#include "../jabyengine_defines.hpp" + +namespace JabyEngine { + // Taken from boost endian + + static constexpr uint8_t read_be(uint8_t x) { + return x; + } + + static constexpr uint16_t read_be(uint16_t x) { + return (x << 8) | (x >> 8); + } + + static constexpr uint32_t read_be(uint32_t x) { + const uint32_t step16 = x << 16 | x >> 16; + return ((step16 << 8) & 0xff00ff00) | ((step16 >> 8) & 0x00ff00ff); + } +} \ No newline at end of file diff --git a/include/PSX/Auxiliary/math_helper.hpp b/include/PSX/Auxiliary/math_helper.hpp index 1ef021b0..1f022241 100644 --- a/include/PSX/Auxiliary/math_helper.hpp +++ b/include/PSX/Auxiliary/math_helper.hpp @@ -34,4 +34,14 @@ namespace JabyEngine { } return result; } + + template + static constexpr T min_of(T a, T b) { + return (a < b) ? a : b; + } + + template + static constexpr T max_of(T a, T b) { + return (a > b) ? a : b; + } } \ No newline at end of file diff --git a/include/PSX/Auxiliary/word_helper.hpp b/include/PSX/Auxiliary/word_helper.hpp new file mode 100644 index 00000000..6675c8af --- /dev/null +++ b/include/PSX/Auxiliary/word_helper.hpp @@ -0,0 +1,15 @@ +#pragma once +#include + +namespace JabyEngine { + using word_t = uint32_t; + + static constexpr size_t bytes_to_words(size_t bytes) { + return bytes/sizeof(word_t); + } + + static constexpr size_t words_to_bytes(size_t words) { + return words*sizeof(word_t); + } +} + diff --git a/include/PSX/File/Processor/file_processor.hpp b/include/PSX/File/Processor/file_processor.hpp index ecccbe3e..19e7e08b 100644 --- a/include/PSX/File/Processor/file_processor.hpp +++ b/include/PSX/File/Processor/file_processor.hpp @@ -37,8 +37,8 @@ namespace JabyEngine { Reserved reserved; template - static __always_inline State from(const T& state, const uint8_t* data_adr, GenericProcessRoutine process_routine) { - return {Configuration::from(process_routine, data_adr), *reinterpret_cast(&state)}; + static __always_inline State from(const T& state, const uint32_t* data_adr, GenericProcessRoutine process_routine) { + return {Configuration::from(process_routine, reinterpret_cast(data_adr)), *reinterpret_cast(&state)}; static_assert(sizeof(T) <= sizeof(Reserved)); } @@ -52,6 +52,7 @@ namespace JabyEngine { // The nothing state State create(const uint32_t* data_adr, const Nothing& nothing); State create(const uint32_t* data_adr, const SimpleTIM& file); + State create(const uint32_t* data_adr, const VAG& file); State create_custom(const uint32_t* data_adr, const CDFileType_t& file_type, const CDFile::Payload& payload); } diff --git a/include/PSX/File/cd_file_types.hpp b/include/PSX/File/cd_file_types.hpp index 908d3e92..df3feeae 100644 --- a/include/PSX/File/cd_file_types.hpp +++ b/include/PSX/File/cd_file_types.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include "../Overlay/overlay.hpp" #include "file_types.hpp" @@ -7,8 +8,11 @@ namespace JabyEngine { using RawPayload_t = uint32_t; enum struct CDFileType : CDFileType_t { - SimpleTIM = 0, - CopyTo, + CopyTo = 0, + SimpleTIM, + #ifdef __SUPPORT_VAG__ + SonyVAG, + #endif // __SUPPORT_VAG__ Custom, }; @@ -16,8 +20,9 @@ namespace JabyEngine { struct CDFile { union Payload { RawPayload_t raw; - SimpleTIM simple_tim; CopyTo copy_to; + SimpleTIM simple_tim; + VAG vag; Overlay overlay; }; @@ -38,13 +43,19 @@ namespace JabyEngine { #pragma pack(pop) struct CDFileBuilder { + static constexpr CDFile copy_to(uint8_t rel_lba_idx, uint32_t* dst) { + return CDFile{.rel_lba_idx = rel_lba_idx, .type = CDFileType::CopyTo, .payload = {.copy_to = CopyTo{dst}}}; + } + static constexpr CDFile simple_tim(uint8_t rel_lba_idx, SimpleTIM simple_tim) { return CDFile{.rel_lba_idx = rel_lba_idx, .type = CDFileType::SimpleTIM, .payload = {.simple_tim = simple_tim}}; } - static constexpr CDFile copy_to(uint8_t rel_lba_idx, uint32_t* dst) { - return CDFile{.rel_lba_idx = rel_lba_idx, .type = CDFileType::CopyTo, .payload = {.copy_to = CopyTo{dst}}}; +#ifdef __SUPPORT_VAG__ + static constexpr CDFile sony_vag(uint8_t lba_idx, VAG vag) { + return CDFile{.rel_lba_idx = lba_idx, .type = CDFileType::SonyVAG, .payload = {.vag = vag}}; } +#endif //__SUPPORT_VAG__ static constexpr CDFile overlay(uint8_t rel_lba_idx, uint32_t* overlay_dst) { return CDFile{.rel_lba_idx = rel_lba_idx, .type = CDFileType::CopyTo, .payload = {.overlay = Overlay{overlay_dst}}}; diff --git a/include/PSX/File/file_processor_helper.hpp b/include/PSX/File/file_processor_helper.hpp index 26e8fb57..facc5af2 100644 --- a/include/PSX/File/file_processor_helper.hpp +++ b/include/PSX/File/file_processor_helper.hpp @@ -1,6 +1,7 @@ #pragma once #include "Processor/file_processor.hpp" #include "cd_file_types.hpp" +#include namespace JabyEngine { namespace FileProcessor { @@ -18,6 +19,54 @@ namespace JabyEngine { config.process_routine = reinterpret_cast(process_routine); return process_routine(config, state); } + + namespace DMA { + using words_t = size_t; + + struct WordsReady { + uint32_t words_to_use; + bool is_last; + + static constexpr WordsReady calculate(const State::Configuration& config, words_t words_left) { + const auto config_data_words = (config.data_bytes/sizeof(uint32_t)); + const auto words_to_use = (config_data_words > words_left) ? words_left : config_data_words; + + return { + .words_to_use = words_to_use, + .is_last = words_to_use == words_left + }; + } + }; + + template + static words_t send_words(words_t words_to_send, bool send_all) { + auto blocks_to_send = words_to_send/16; + while(blocks_to_send > 0) { + const auto block_send = (blocks_to_send > UI16_MAX) ? UI16_MAX : blocks_to_send; + + // Send data! + T::wait(); + T::Receive::start(blocks_to_send); + blocks_to_send -= block_send; + } + + if(send_all) { + const auto last_words_to_send = (words_to_send & 0b1111); + if(last_words_to_send > 0) { + T::wait(); + T::Receive::start(1, last_words_to_send); + } + + T::wait(); + T::end(); + return words_to_send; + } + + else { + return (words_to_send & ~0b1111); + } + } + } } } } \ No newline at end of file diff --git a/include/PSX/File/file_types.hpp b/include/PSX/File/file_types.hpp index bfdec4ba..542dbf99 100644 --- a/include/PSX/File/file_types.hpp +++ b/include/PSX/File/file_types.hpp @@ -2,12 +2,19 @@ #include "../Auxiliary/bits.hpp" #include "../GPU/gpu_types.hpp" #include "../jabyengine_defines.hpp" +#include "../SPU/spu.hpp" namespace JabyEngine { #pragma pack(push, 1) struct Nothing { }; + struct CopyTo { + uint32_t* dst; + }; + + // TODO: Call TIM? + // TODO: Add create function? struct SimpleTIM { static constexpr auto TextureX = BitRange::from_to(0, 8); static constexpr auto TextureY = BitRange::from_to(9, 16); @@ -61,9 +68,15 @@ namespace JabyEngine { } }; - struct CopyTo { - uint32_t* dst; + struct VAG { + uint8_t voice_number; + SPU::SimpleVolume inital_stereo_vol; + + static constexpr VAG create(uint8_t voice_num, SPU::SimpleVolume volume) { + return VAG{.voice_number = voice_num, .inital_stereo_vol = volume}; + } }; + #pragma pack(pop) typedef CopyTo Overlay; diff --git a/include/PSX/SPU/spu.hpp b/include/PSX/SPU/spu.hpp new file mode 100644 index 00000000..769579e2 --- /dev/null +++ b/include/PSX/SPU/spu.hpp @@ -0,0 +1,53 @@ +#pragma once +#include "../System/IOPorts/spu_io.hpp" + +namespace JabyEngine { + namespace SPU { + using SPU_IO::operator""_vol; + + using SRAMAdr = SPU_IO::SRAMAdr; + using SimpleVolume = SPU_IO::SimpleVolume; + using SweepVolume = SPU_IO::SweepVolume; + + // TODO: Rename to sample...? + struct Voice { + size_t get_id() const { + return reinterpret_cast(this); + } + + SRAMAdr allocate(size_t size); + SRAMAdr allocate(SPU_IO::SampleRate frequency, size_t size); + void deallocate(); + + void set_sample_rate(SPU_IO::SampleRate frequency) { + SPU_IO::Voice[Voice::get_id()].sampleRate.write(frequency); + } + + void set_volume(SimpleVolume left, SimpleVolume right) { + // TODO: Verify that assembly code is super simple + SPU_IO::Voice[Voice::get_id()].volumeLeft.write(SweepVolume::create(left)); + SPU_IO::Voice[Voice::get_id()].volumeRight.write(SweepVolume::create(right)); + } + + void play() { + SPU_IO::Key::On.write(SPU_IO::KeyOn::for_specific(Voice::get_id())); + } + + void play_if_end() { + if(Voice::is_end()) { + Voice::play(); + } + } + + void stop() { + SPU_IO::Key::Off.write(SPU_IO::KeyOff::for_specific(Voice::get_id())); + } + + bool is_end() const { + return SPU_IO::Voice[Voice::get_id()].adsr_volume.read() == SimpleVolume::mute(); + } + }; + + static auto& voice = __new_declare_io_port_array(Voice, SPU_IO::VoiceCount, 0x0); + } +} \ No newline at end of file diff --git a/include/PSX/System/IOPorts/IOValues/spu_io_values.hpp b/include/PSX/System/IOPorts/IOValues/spu_io_values.hpp new file mode 100644 index 00000000..72865aee --- /dev/null +++ b/include/PSX/System/IOPorts/IOValues/spu_io_values.hpp @@ -0,0 +1,214 @@ +#pragma once +#include "../ioport.hpp" +#include + +namespace JabyEngine { + namespace SPU_IO_Values { + namespace MemoryMap { + static constexpr uintptr_t ADPCM = 0x01000; + } + + __declare_io_value(AD, uint16_t) { + static constexpr auto AttackMode = Bit(15); + static constexpr auto AttackShift = BitRange::from_to(10, 14); + static constexpr auto AttackStep = BitRange::from_to(8, 9); + static constexpr auto DecayShift = BitRange::from_to(4, 7); + static constexpr auto SustainLevel = BitRange::from_to(0, 3); + + static constexpr AD none() { + return AD{0}; + } + }; + + __declare_io_value(ControlRegister, uint16_t) { + enum RAMTransferMode { + Stop = 0, + ManualWrite = 1, + DMAWrite = 2, + DMARead = 3 + }; + + static constexpr auto Enable = Bit(15); + static constexpr auto Unmute = Bit(14); + static constexpr auto NoiseFrequcenyShift = BitRange::from_to(10, 13); + static constexpr auto NoiseFrequcenyStep = BitRange::from_to(8, 9); + static constexpr auto ReverbMasterEnable = Bit(7); + static constexpr auto IRQ9Enable = Bit(6); + static constexpr auto TransferMode = BitRange::from_to(4, 5); + static constexpr auto ExternalAudioReverb = Bit(3); + static constexpr auto CDAudioReverb = Bit(2); + static constexpr auto ExternalAudioEnable = Bit(1); + static constexpr auto CDAudioEnable = Bit(0); + }; + + __declare_io_value(DataTransferControl, uint16_t) { + static constexpr DataTransferControl NormalTransferMode() { + return DataTransferControl{0x0004}; + } + }; + + __declare_io_value(Echo, uint32_t) { + static constexpr auto EchoBits = BitRange::from_to(0, 23); + + static constexpr Echo AllOff() { + return Echo{0}; + } + }; + + __declare_io_value(KeyOff, uint32_t) { + static constexpr KeyOff for_specific(uint32_t id) { + return KeyOff{1u << id}; + } + + static constexpr KeyOff all() { + return KeyOff{UI32_MAX}; + } + }; + + __declare_io_value(KeyOn, uint32_t) { + static constexpr KeyOn for_specific(uint32_t id) { + return KeyOn{1u << id}; + } + + static constexpr KeyOn all() { + return KeyOn{UI32_MAX}; + } + }; + + __declare_io_value(KeyStatus, uint32_t) { + }; + + __declare_io_value(Noise, uint16_t) { + static constexpr auto NoiseBits = BitRange::from_to(0, 23); + + static constexpr Noise AllOff() { + return Noise{0}; + } + }; + + __declare_io_value(PitchModulation, uint32_t) { + static constexpr auto EnableBits = BitRange::from_to(1, 23); + + static constexpr PitchModulation AllOff() { + return PitchModulation{0}; + } + }; + + __declare_io_value(SampleRate, uint16_t) { + static constexpr SampleRate stop() { + return SampleRate{0}; + } + + static constexpr SampleRate from_HZ(uint32_t freq) { + constexpr uint32_t Base1024Hz = static_cast((4096.0/44100.0)*1024.0); + return SampleRate{static_cast((freq >> 10)*Base1024Hz)}; + } + + static constexpr SampleRate from_HZ(double freq) { + //4096 == 44100Hz + constexpr double Base = (4096.0 / 44100.0); + return SampleRate{static_cast((freq*Base))}; + } + }; + + __declare_io_value(SimpleVolume, int16_t) { + static constexpr auto MaxVolume = I16_MAX; + + static constexpr SimpleVolume mute() { + return SimpleVolume{0}; + } + + constexpr operator int16_t() const { + return this->raw; + } + }; + + static constexpr SimpleVolume operator""_vol(long double fraction) { + return {static_cast(static_cast(SimpleVolume::MaxVolume)*fraction)}; + } + + __declare_io_value(SR, uint16_t) { + static constexpr auto SustainMode = Bit(31 - 16); + static constexpr auto SustainDirection = Bit(30 - 16); + static constexpr auto SustainShift = BitRange::from_to((24 - 16), (28 - 16)); + static constexpr auto SustainStep = BitRange::from_to((22 - 16), (23 - 16)); + static constexpr auto ReleaseMode = Bit(21 - 16); + static constexpr auto ReleaseShift = BitRange::from_to((16 - 16), (20 - 16)); + + static constexpr SR none() { + return SR{0}; + } + }; + + __declare_io_value(SRAMAdr, uint16_t) { + static constexpr SRAMAdr null() { + return SRAMAdr{0x0}; + } + + static constexpr SRAMAdr adpcm_start() { + return SRAMAdr{MemoryMap::ADPCM}; + } + }; + + __declare_io_value(StatusRegister, uint16_t) { + enum CapureBufferHalf { + First = 0, + Second = 1 + }; + + static constexpr auto Unused = BitRange::from_to(12, 15); + static constexpr auto CaputreBufferHalf = Bit(11); + static constexpr auto TransferBusy = Bit(10); + static constexpr auto IsDMARead = Bit(9); + static constexpr auto isDMAWrite = Bit(8); + static constexpr auto isDMA = Bit(7); + static constexpr auto isIRQ = Bit(6); + // Copies of ControlRegister + static constexpr auto TransferMode = BitRange::from_to(4, 5); + static constexpr auto ExternalAudioReverb = Bit(3); + static constexpr auto CDAudioReverb = Bit(2); + static constexpr auto ExternalAudioEnable = Bit(1); + static constexpr auto CDAudioEnable = Bit(0); + }; + + __declare_io_value(SweepVolume, int16_t) { + struct VolumeMode { + static constexpr auto MaxVolume = (I16_MAX >> 1); + static constexpr auto EnableSweep = Bit(15); + static constexpr auto Enable = !EnableSweep; + static constexpr auto Volume = BitRange::from_to(0, 14); + }; + + struct SweepMode { + enum Mode { + Linear = 0, + Exponential = 1, + }; + + enum Direction { + Increase = 0, + Decrease = 1, + }; + + enum Phase { + Posititve = 0, + Negative = 1, + }; + + static constexpr auto Mode = Bit(14); + static constexpr auto Direction = Bit(13); + static constexpr auto Phase = Bit(12); + static constexpr auto Shift = BitRange::from_to(2, 6); + static constexpr auto Step = BitRange::from_to(0, 1); + }; + + static constexpr SweepVolume create(SimpleVolume volume) { + return from(VolumeMode::Enable, VolumeMode::Volume.with(volume.raw >> 1)); + } + + static constexpr SweepVolume mute() { + return SweepVolume{0}; + } + }; + } +} \ No newline at end of file diff --git a/include/PSX/System/IOPorts/ioport.hpp b/include/PSX/System/IOPorts/ioport.hpp index 5ae35ff9..264af11b 100644 --- a/include/PSX/System/IOPorts/ioport.hpp +++ b/include/PSX/System/IOPorts/ioport.hpp @@ -65,17 +65,9 @@ namespace JabyEngine { }; } - struct ubus32_t { - uint16_t low; - uint16_t high; - - static ubus32_t from(uint32_t value) { - return {.low = static_cast(value & 0xFFFF), .high = static_cast(value >> 16)}; - } - }; - template struct IOPort { + using Value = T; T value; T read() const { @@ -88,24 +80,34 @@ namespace JabyEngine { }; template<> - struct IOPort { - ubus32_t value; + struct IOPort; - ubus32_t read() const { - auto*const cv_this = const_cast*>(this); - - return {.low = cv_this->value.low, .high = cv_this->value.high}; + template + struct IOPort32 { + union ValueHelper { + struct { + uint16_t low; + uint16_t high; + }; + T value; + }; + using Value = T; + T value; + + T read() const { + const auto* cast_this = reinterpret_cast*>(this); + const volatile auto* cv_this = const_cast(cast_this); + + return ValueHelper{.low = cv_this->value.low, .high = cv_this->value.high}.value; } - void write(ubus32_t value) { - auto*const cv_this = const_cast*>(this); + void write(T value) { + const auto new_value = ValueHelper{.value = value}; + auto* cast_this = reinterpret_cast*>(this); + volatile auto* v_this = const_cast(cast_this); - cv_this->value.low = value.low; - cv_this->value.high = value.high; - } - - void write(uint32_t value) { - IOPort::write(ubus32_t::from(value)); + v_this->value.low = new_value.low; + v_this->value.high = new_value.high; } }; @@ -115,4 +117,7 @@ namespace JabyEngine { #define __declare_io_port_w_type(cv, type, name, adr) __declare_value_at(cv, ::JabyEngine::IOPort, name, adr) #define __declare_io_port(cv, name, adr) __declare_io_port_w_type(cv, struct name, name, adr) #define __declare_io_port_array(cv, name, size, adr) __declare_array_at(cv, struct name, name, size, adr) + + #define __new_declare_io_port(type, adr) *reinterpret_cast(adr) + #define __new_declare_io_port_array(type, size, adr) reinterpret_cast(*reinterpret_cast(adr)) } \ No newline at end of file diff --git a/include/PSX/System/IOPorts/spu_io.hpp b/include/PSX/System/IOPorts/spu_io.hpp index afa9c3d7..1b7638a7 100644 --- a/include/PSX/System/IOPorts/spu_io.hpp +++ b/include/PSX/System/IOPorts/spu_io.hpp @@ -1,172 +1,88 @@ #pragma once -#include "ioport.hpp" +#include "IOValues/spu_io_values.hpp" #include namespace JabyEngine { namespace SPU_IO { - enum struct Mode { - Linear = 0, - Exponential = 1, - }; + using namespace SPU_IO_Values; - enum struct Direction { - Increase = 0, - Decrease = 1, - }; + static constexpr size_t VoiceCount = 24; + static constexpr size_t ReverbCount = 1; - enum struct Phase { - Posititve = 0, - Negative = 1, - }; + struct ControlRegisterIO : public IOPort { + using TransferMode = Value::RAMTransferMode; - //0..0x1F = Fast..Slow - typedef uint8_t Shift; - - //0..3 = +7, +6, +5, +4 or -6, -7, -6, -5 - typedef uint8_t Step; - - __declare_io_value(DataTransferControl, uint16_t) { - }; - - __declare_io_value(SimpleVolume, int16_t) { - constexpr operator int16_t() const { - return this->raw; - } - }; - __declare_io_value(Adr, uint16_t) { - }; - - __declare_io_value(SampleRate, uint16_t) { - static constexpr SampleRate from_HZ(double freq) { - //4096 == 44100Hz - constexpr double Base = (4096.0 / 44100.0); - - return {static_cast((freq*Base))}; + void set_transfer_mode(TransferMode mode) { + this->write(this->read().set(ControlRegister::TransferMode.with(mode))); + while(this->read().get(ControlRegister::TransferMode) != mode); } }; - __declare_io_value(SweepVolume, int16_t) { - // For Volume Mode - static constexpr auto SweepEnable = Bit(15); - static constexpr auto VolumeEnable = !SweepEnable; - static constexpr auto Volume = BitRange::from_to(0, 14); - - // For Sweep Mode - static constexpr auto SweepMode = Bit(14); - static constexpr auto SweepDirection = Bit(13); - static constexpr auto SweepPhase = Bit(12); - static constexpr auto SweepShift = BitRange::from_to(2, 6); - static constexpr auto SweepStep = BitRange::from_to(0, 1); - }; - - __declare_io_value(SR, uint16_t) { - static constexpr auto SustainMode = Bit(31 - 16); - static constexpr auto SustainDirection = Bit(30 - 16); - static constexpr auto SustainShift = BitRange::from_to((24 - 16), (28 - 16)); - static constexpr auto SustainStep = BitRange::from_to((22 - 16), (23 - 16)); - static constexpr auto ReleaseMode = Bit(21 - 16); - static constexpr auto ReleaseShift = BitRange::from_to((16 - 16), (20 - 16)); - }; - - __declare_io_value(AD, uint16_t) { - static constexpr auto AttackMode = Bit(15); - static constexpr auto AttackShift = BitRange::from_to(10, 14); - static constexpr auto AttackStep = BitRange::from_to(8, 9); - static constexpr auto DecayShift = BitRange::from_to(4, 7); - static constexpr auto SustainLevel = BitRange::from_to(0, 3); - }; + using ADIO = IOPort; + using DataTransferControlIO = IOPort; + using EchoIO = IOPort32; + using KeyOffIO = IOPort32; + using KeyOnIO = IOPort32; + using KeyStatusIO = IOPort32; + using NoiseIO = IOPort; + using PitchModulationIO = IOPort32; + using SampleRateIO = IOPort; + using SimpleVolumeIO = IOPort; + using StatusRegisterIO = IOPort; + using SRIO = IOPort; + using SRAMAdrIO = IOPort; + using SweepVolumeIO = IOPort; #pragma pack(push, 1) struct Voice { - IOPort volumeLeft; //Offset: 0x0 - IOPort volumeRight; //Offset: 0x2 - IOPort sampleRate; //Offset: 0x4; - IOPort adr; //Offset: 0x6 - IOPort ad; //Offset: 0x8 - IOPort sr; //Offset: 0xA - IOPort currentVolume; //Offset: 0xC - IOPort repeatAdr; //Offset: 0xE - - static constexpr Adr start_adr() { - return {0x200}; - } + SweepVolumeIO volumeLeft; //Offset: 0x0 + SweepVolumeIO volumeRight; //Offset: 0x2 + SampleRateIO sampleRate; //Offset: 0x4; + SRAMAdrIO adr; //Offset: 0x6 + ADIO ad; //Offset: 0x8 + SRIO sr; //Offset: 0xA + SimpleVolumeIO adsr_volume; //Offset: 0xC + SRAMAdrIO repeatAdr; //Offset: 0xE }; #pragma pack(pop) - __declare_io_value(ControlRegister, uint16_t) { - enum RAMTransferMode { - Stop = 0, - ManualWrite = 1, - DMAWrite = 2, - DMARead = 3 - }; - - static constexpr auto Enable = Bit(15); - static constexpr auto Unmute = Bit(14); - static constexpr auto NoiseFrequcenyShift = BitRange::from_to(10, 13); - static constexpr auto NoiseFrequcenyStep = BitRange::from_to(8, 9); - static constexpr auto ReverbMasterEnable = Bit(7); - static constexpr auto IRQ9Enable = Bit(6); - static constexpr auto TransferMode = BitRange::from_to(4, 5); - static constexpr auto ExternalAudioReverb = Bit(3); - static constexpr auto CDAudioReverb = Bit(2); - static constexpr auto ExternalAudioEnable = Bit(1); - static constexpr auto CDAudioEnable = Bit(0); - }; - - __declare_io_value(PMON, uint16_t) { - static constexpr auto EnableBits = BitRange::from_to(1, 23); - }; - - __declare_io_value(NON, uint16_t) { - static constexpr auto NoiseBits = BitRange::from_to(0, 23); - }; - - __declare_io_value(EON, uint16_t) { - static constexpr auto EchoBits = BitRange::from_to(0, 23); - }; - - static constexpr size_t VoiceCount = 24; - - struct Key { - __declare_io_port_w_type(inline, ubus32_t, On, 0x1F801D88); - __declare_io_port_w_type(inline, ubus32_t, Off, 0x1F801D8C); - __declare_io_port_w_type(inline, ubus32_t, Status, 0x1F801D9C); - }; - - struct MainVolume { - __declare_io_port_w_type(inline, SweepVolume, Left, 0x1F801D80); - __declare_io_port_w_type(inline, SweepVolume, Right, 0x1F801D82); - }; + static auto& Voice = __new_declare_io_port_array(struct Voice, VoiceCount, 0x1F801C00); + static auto& PMON = __new_declare_io_port(PitchModulationIO, 0x1F801D90); + static auto& NON = __new_declare_io_port(NoiseIO, 0x1F801D94); + static auto& EON = __new_declare_io_port(EchoIO, 0x1F801D98); + static auto& SRAMTransferAdr = __new_declare_io_port(SRAMAdrIO, 0x1F801DA6); + static auto& ControlRegister = __new_declare_io_port(ControlRegisterIO, 0x1F801DAA); + static auto& DataTransferControl = __new_declare_io_port(DataTransferControlIO, 0x1F801DAC); + static auto& StatusRegister = __new_declare_io_port(StatusRegisterIO, 0x1F801DAE); struct CDVolume { - __declare_io_port_w_type(inline, SimpleVolume, Left, 0x1F801DB0); - __declare_io_port_w_type(inline, SimpleVolume, Right, 0x1F801DB2); + static inline auto& Left = __new_declare_io_port(SimpleVolumeIO, 0x1F801DB0); + static inline auto& Right = __new_declare_io_port(SimpleVolumeIO, 0x1F801DB2); }; struct ExternalAudioInputVolume { - __declare_io_port_w_type(inline, SimpleVolume, Left, 0x1F801DB4); - __declare_io_port_w_type(inline, SimpleVolume, Right, 0x1F801DB6); + static inline auto& Left = __new_declare_io_port(SimpleVolumeIO, 0x1F801DB4); + static inline auto& Right = __new_declare_io_port(SimpleVolumeIO, 0x1F801DB6); + }; + + struct Key { + static inline auto& On = __new_declare_io_port(KeyOnIO, 0x1F801D88); + static inline auto& Off = __new_declare_io_port(KeyOffIO, 0x1F801D8C); + static inline auto& Status = __new_declare_io_port(KeyStatusIO, 0x1F801D9C); + }; + + struct MainVolume { + static inline auto& Left = __new_declare_io_port(SweepVolumeIO, 0x1F801D80); + static inline auto& Right = __new_declare_io_port(SweepVolumeIO, 0x1F801D82); }; struct Reverb { struct Volume { - __declare_io_port_w_type(inline, SimpleVolume, Left, 0x1F801D84); - __declare_io_port_w_type(inline, SimpleVolume, Right, 0x1F801D86); + static inline auto& Left = __new_declare_io_port(SimpleVolumeIO, 0x1F801D84); + static inline auto& Right = __new_declare_io_port(SimpleVolumeIO, 0x1F801D86); }; - __declare_io_port_w_type(inline, Adr, WorkAreaAdr, 0x1F801DA2); + static inline auto& On = EON; + static inline auto& WorkAreaAdr = __new_declare_io_port(SRAMAdrIO, 0x1F801DA2); }; - - static constexpr SimpleVolume operator""_vol(long double fraction) { - return {static_cast(static_cast(I16_MAX)*fraction)}; - } - - __declare_io_port(, ControlRegister, 0x1F801DAA); - __declare_io_port(, DataTransferControl, 0x1F801DAC); - __declare_io_port(, PMON, 0x1F801D90); - __declare_io_port(, NON, 0x1F801D94); - __declare_io_port(, EON, 0x1F801D98); - - __declare_io_port_array(, Voice, VoiceCount, 0x1F801C00); } } \ No newline at end of file diff --git a/include/PSX/jabyengine_config.hpp b/include/PSX/jabyengine_config.hpp index 3a92f80d..868a6052 100644 --- a/include/PSX/jabyengine_config.hpp +++ b/include/PSX/jabyengine_config.hpp @@ -31,6 +31,8 @@ namespace JabyEngine { using Configuration = CustomConfiguration; #else using Configuration = DefaultConfiguration; + // TODO: v Turn off when this is supported + #define __SUPPORT_VAG__ #define __SUPPORT_PS3__ #endif // has jabyengine_custom_config } \ No newline at end of file diff --git a/mkfile/psexe.ld b/mkfile/psexe.ld index d02aff2f..8b672b79 100644 --- a/mkfile/psexe.ld +++ b/mkfile/psexe.ld @@ -55,7 +55,7 @@ __stack_start = ORIGIN(ram) + LENGTH(ram); SECTIONS { .zero (NOLOAD) : { - _ZN10JabyEngine2CD4zeroE = .; + _ZN10JabyEngine3SPU5voiceE = .; } > zero .bios (NOLOAD) : { diff --git a/readme.md b/readme.md index e8ce7758..e2659b0c 100644 --- a/readme.md +++ b/readme.md @@ -9,11 +9,18 @@ - [ ] Emulator - [ ] Real Hardware -# Todo +# TODO - [ ] Ko-fi supporter list - [ ] Support more GTE - [X] Easy serial code swap - [X] Support .subst files to be substituted with environment variables - [X] Support pop-fe - [ ] PS3 PKG generation tool? -- [ ] PS3 runtime detection? \ No newline at end of file +- [ ] PS3 runtime detection? +- [ ] Move DMA code to public include for custom loading of files? + - [ ] Maybe make it an interface with SPU/GPU as a specification...? + - [ ] Could be empty classes that the linker maps to 0 or somewhere + - [ ] Could be a all static struct (I like that better; We are not Nicolas Noble) + - [ ] Redo the IO ports again...? +- [ ] Support better file loading with threads + - [ ] Loading Screen with GPU IO? (Does DMA and IO work together?) \ No newline at end of file diff --git a/src/Library/internal-include/GPU/gpu_internal.hpp b/src/Library/internal-include/GPU/gpu_internal.hpp index dfdbcfcd..f497734f 100644 --- a/src/Library/internal-include/GPU/gpu_internal.hpp +++ b/src/Library/internal-include/GPU/gpu_internal.hpp @@ -65,21 +65,21 @@ namespace JabyEngine { GPU_IO::GP1.write(GPU_IO::Command::ResetCMDBufer()); } - namespace DMA { + struct DMA { #ifdef __SUPPORT_PS3__ // The PS3 doesn't autoincrement the GPU MADR register so we have to do it - extern uintptr_t MADR; + static uintptr_t MADR; #endif // __SUPPORT_PS3__ static void wait() { - ::JabyEngine::DMA_IO::GPU.wait(); + DMA_IO::GPU.wait(); } static void end() { reset_cmd_buffer(); } - namespace Receive { + struct Receive { static void prepare() { GPU_IO::GP1.write(GPU_IO::Command::DMADirection(GPU_IO::DMADirection::CPU2GPU)); reset_cmd_buffer(); @@ -87,7 +87,7 @@ namespace JabyEngine { static void set_src(uintptr_t adr) { #ifdef __SUPPORT_PS3__ - MADR = adr; + DMA::MADR = adr; #else DMA_IO::GPU.set_adr(adr); #endif // __SUPPORT_PS3__ @@ -101,17 +101,17 @@ namespace JabyEngine { } static void start(uint16_t blockCount, uint16_t wordsPerBlock = 0x10) { - typedef DMA_IO::BCR::SyncMode1 SyncMode1; + using SyncMode1 = DMA_IO::BCR::SyncMode1; #ifdef __SUPPORT_PS3__ DMA_IO::GPU.set_adr(MADR); - MADR += (blockCount * wordsPerBlock) << 2; + DMA::MADR += (blockCount * wordsPerBlock) << 2; #endif // __SUPPORT_PS3__ DMA_IO::GPU.block_ctrl.write(DMA_IO::BCR::from(SyncMode1::BlockSize.with(wordsPerBlock), SyncMode1::BlockAmount.with(blockCount))); DMA_IO::GPU.channel_ctrl.write(DMA_IO::CHCHR::StartGPUReceive()); } - } - } + }; + }; } } } \ No newline at end of file diff --git a/src/Library/internal-include/SPU/spu_internal.hpp b/src/Library/internal-include/SPU/spu_internal.hpp new file mode 100644 index 00000000..cb5e7776 --- /dev/null +++ b/src/Library/internal-include/SPU/spu_internal.hpp @@ -0,0 +1,44 @@ +#pragma once +#include +#include + +namespace JabyEngine { + namespace SPU { + namespace internal { + struct DMA { + static void wait() { + DMA_IO::SPU.wait(); + while(SPU_IO::StatusRegister.read().is_set(SPU_IO_Values::StatusRegister::TransferBusy)); + } + + static void end() { + SPU_IO::ControlRegister.set_transfer_mode(SPU_IO::ControlRegister::Stop); + } + + struct Receive { + static void prepare() { + end(); + SPU_IO::DataTransferControl.write(SPU_IO::DataTransferControl::NormalTransferMode()); + SPU_IO::ControlRegister.set_transfer_mode(SPU_IO::ControlRegister::Stop); + } + + static void set_src(uintptr_t adr) { + DMA_IO::SPU.set_adr(adr); + } + + static void set_dst(SPU::SRAMAdr adr) { + SPU_IO::SRAMTransferAdr.write(adr); + SPU_IO::ControlRegister.set_transfer_mode(SPU_IO::ControlRegister::DMAWrite); + } + + static void start(uint16_t blockCount, uint16_t wordsPerBlock = 0x10) { + using SyncMode1 = DMA_IO::BCR::SyncMode1; + + DMA_IO::SPU.block_ctrl.write(DMA_IO::BCR::from(SyncMode1::BlockSize.with(wordsPerBlock), SyncMode1::BlockAmount.with(blockCount))); + DMA_IO::SPU.channel_ctrl.write(DMA_IO::CHCHR::StartSPUReceive()); + } + }; + }; + } + } +} diff --git a/src/Library/internal-include/SPU/spu_mmu.hpp b/src/Library/internal-include/SPU/spu_mmu.hpp new file mode 100644 index 00000000..7ed0560d --- /dev/null +++ b/src/Library/internal-include/SPU/spu_mmu.hpp @@ -0,0 +1,10 @@ +#pragma once +#include + +namespace JabyEngine { + namespace SPU_MMU { + // TODO: Make this work with words? Word align? + const uint8_t* allocate(uint8_t voice, size_t size); + void deallocate(uint8_t voice); + } +} \ No newline at end of file diff --git a/src/Library/src/BootLoader/spu_boot.cpp b/src/Library/src/BootLoader/spu_boot.cpp index 2371f0f9..ea1d6ea0 100644 --- a/src/Library/src/BootLoader/spu_boot.cpp +++ b/src/Library/src/BootLoader/spu_boot.cpp @@ -8,7 +8,7 @@ namespace JabyEngine { using namespace SPU_IO; static void clear_main_volume() { - static constexpr auto StartVol = SweepVolume::from(SweepVolume::VolumeEnable, SweepVolume::Volume.with(static_cast(I16_MAX >> 2))); + static constexpr auto StartVol = SweepVolume::from(SweepVolume::VolumeMode::Enable, SweepVolume::VolumeMode::Volume.with(SweepVolume::VolumeMode::MaxVolume >> 1)); MainVolume::Left.write(StartVol); MainVolume::Right.write(StartVol); @@ -18,37 +18,37 @@ namespace JabyEngine { CDVolume::Left.write(0.75_vol); CDVolume::Right.write(0.75_vol); - ExternalAudioInputVolume::Left.write({0}); - ExternalAudioInputVolume::Right.write({0}); + ExternalAudioInputVolume::Left.write(SimpleVolume::mute()); + ExternalAudioInputVolume::Right.write(SimpleVolume::mute()); } static void clear_voice() { for(auto& voice : SPU_IO::Voice) { - voice.volumeLeft.write({0}); - voice.volumeRight.write({0}); - voice.sampleRate.write({0}); - voice.ad.write({0}); - voice.sr.write({0}); - voice.currentVolume.write({0}); + voice.volumeLeft.write(SweepVolume::mute()); + voice.volumeRight.write(SweepVolume::mute()); + voice.sampleRate.write(SampleRate::stop()); + voice.ad.write(AD::none()); + voice.sr.write(SR::none()); + voice.adsr_volume.write(SimpleVolume::mute()); - voice.adr.write(Voice::start_adr()); - voice.repeatAdr.write(Voice::start_adr()); + voice.adr.write(SRAMAdr::adpcm_start()); + voice.repeatAdr.write(SRAMAdr::adpcm_start()); } } static void clear_pmon() { - SPU_IO::PMON.write({0}); + SPU_IO::PMON.write(PitchModulation::AllOff()); } static void clear_noise_and_echo() { - SPU_IO::NON.write({0}); - SPU_IO::EON.write({0}); + SPU_IO::NON.write(Noise::AllOff()); + SPU_IO::EON.write(Echo::AllOff()); } static void clear_reverb() { - Reverb::Volume::Left.write({0}); - Reverb::Volume::Right.write({0}); - Reverb::WorkAreaAdr.write({0}); + Reverb::Volume::Left.write(SimpleVolume::mute()); + Reverb::Volume::Right.write(SimpleVolume::mute()); + Reverb::WorkAreaAdr.write(SRAMAdr::null()); } static void setup_control_register() { @@ -57,25 +57,19 @@ namespace JabyEngine { SPU_IO::ControlRegister.write(SetupValue); } - static void setup_data_transfer_control() { - static constexpr struct DataTransferControl RequiredValue{(2 << 1)}; - - DataTransferControl.write(RequiredValue); - } - static void wait_voices() { - static constexpr SimpleVolume Treshhold{static_cast(I16_MAX*0.03)}; + static constexpr auto Treshhold = SimpleVolume::mute(); try_again: for(const auto& voice : SPU_IO::Voice) { - if(voice.currentVolume.read() > Treshhold) { + if(voice.adsr_volume.read() > Treshhold) { goto try_again; } } } void stop_voices() { - SPU_IO::Key::Off.write({UI32_MAX}); + SPU_IO::Key::Off.write(KeyOff::all()); } void setup() { @@ -88,7 +82,6 @@ namespace JabyEngine { clear_noise_and_echo(); clear_reverb(); - setup_data_transfer_control(); setup_control_register(); } } diff --git a/src/Library/src/BootLoader/start_boot.cpp b/src/Library/src/BootLoader/start_boot.cpp index 422484a2..177250bd 100644 --- a/src/Library/src/BootLoader/start_boot.cpp +++ b/src/Library/src/BootLoader/start_boot.cpp @@ -2,7 +2,10 @@ #include "../../internal-include/GPU/gpu_internal.hpp" #include +#include "../../internal-include/SPU/spu_mmu.hpp" #include +#include +#include #include extern "C" uint32_t __heap_start; @@ -39,6 +42,26 @@ namespace JabyEngine { } } + static void test_spu_alloc() { + static const auto calculate_spu_adr = [](size_t size) -> const uint8_t* { + return reinterpret_cast(SPU_IO::MemoryMap::ADPCM + size); + }; + static const auto simple_assert = [](uint32_t test_id, const uint8_t* adr, const uint8_t* expected) { + static const char* ok_text = "Test %i: 0x%p == 0x%p; OK\n"; + static const char* failed_text = "Test %i: 0x%p != 0x%p; Failed\n"; + + printf(adr == expected ? ok_text : failed_text, test_id, adr, expected); + }; + + printf("=== SPU test ===\n"); + simple_assert(0, SPU_MMU::allocate(0, 0x600), calculate_spu_adr(0x0)); + simple_assert(1, SPU_MMU::allocate(1, 0x800), calculate_spu_adr(0x600)); + simple_assert(2, SPU_MMU::allocate(0, 0x300), calculate_spu_adr(0x0)); + simple_assert(3, SPU_MMU::allocate(2, 0x300), calculate_spu_adr(0x300)); + + // TODO: More tests + } + namespace boot { namespace Start { // Thanks to Nicolas Noble! @@ -77,6 +100,7 @@ namespace JabyEngine { GTE::setup(); test_bios_font(); test_gte_scale(); + test_spu_alloc(); SPU::setup(); } diff --git a/src/Library/src/File/Processor/cd_file_processor.cpp b/src/Library/src/File/Processor/cd_file_processor.cpp index dcc21e67..63ab8a88 100644 --- a/src/Library/src/File/Processor/cd_file_processor.cpp +++ b/src/Library/src/File/Processor/cd_file_processor.cpp @@ -37,11 +37,17 @@ namespace JabyEngine { }(file, buf_cfg, is_lz4); switch(file.type) { - case CDFileType::SimpleTIM: - return FileProcessor::create(data_adr, file.payload.simple_tim); case CDFileType::CopyTo: return FileProcessor::create(data_adr, Nothing()); - + + case CDFileType::SimpleTIM: + return FileProcessor::create(data_adr, file.payload.simple_tim); + +#ifdef __SUPPORT_VAG__ + case CDFileType::SonyVAG: + return FileProcessor::create(data_adr, file.payload.vag); +#endif //__SUPPORT_VAG__ + default: return FileProcessor::create_custom(data_adr, static_cast(file.type) - static_cast(CDFileType::Custom), file.payload); } diff --git a/src/Library/src/File/Processor/nothing_processor.cpp b/src/Library/src/File/Processor/nothing_processor.cpp index 613078e8..8d628e8f 100644 --- a/src/Library/src/File/Processor/nothing_processor.cpp +++ b/src/Library/src/File/Processor/nothing_processor.cpp @@ -10,7 +10,7 @@ namespace JabyEngine { } State create(const uint32_t* data_adr, const Nothing& nothing) { - return State::from(NothingState(), reinterpret_cast(data_adr), parse_nothing); + return State::from(NothingState(), data_adr, parse_nothing); } } } \ No newline at end of file diff --git a/src/Library/src/File/Processor/tim_processor.cpp b/src/Library/src/File/Processor/tim_processor.cpp index 7de8d882..f19e3e18 100644 --- a/src/Library/src/File/Processor/tim_processor.cpp +++ b/src/Library/src/File/Processor/tim_processor.cpp @@ -1,7 +1,6 @@ #include "../../../internal-include/GPU/gpu_internal.hpp" #include #include -#include #include namespace JabyEngine { @@ -63,44 +62,12 @@ namespace JabyEngine { } static Progress parse_data(State::Configuration& config, SimpleTIMState& state) { - const auto config_data_words = (config.data_bytes/sizeof(uint32_t)); - const auto words_to_use = (config_data_words > state.words_left) ? state.words_left : config_data_words; - bool is_last = (words_to_use == state.words_left); - auto block_count = (words_to_use >> 4); + const auto [words_to_use, is_last] = Helper::DMA::WordsReady::calculate(config, state.words_left); + const auto words_used = Helper::DMA::send_words(words_to_use, is_last); - while(block_count > 0) { - const auto block_send = (block_count > UI16_MAX) ? UI16_MAX : block_count; - - // Send data! - GPU::internal::DMA::wait(); - GPU::internal::DMA::Receive::start(block_send); - block_count -= block_send; - } - - if(is_last) { - // Send words - const auto last_words = (words_to_use & 0b1111); - if(last_words > 0) { - GPU::internal::DMA::wait(); - GPU::internal::DMA::Receive::start(1, last_words); - } - - GPU::internal::DMA::wait(); - GPU::internal::DMA::end(); - - state.words_left = 0; - config.processed(words_to_use*sizeof(uint32_t)); - - return Progress::Done; - } - - else { - const auto words_used = (words_to_use & ~0b1111); - - state.words_left -= words_used; - config.processed(words_used*sizeof(uint32_t)); - return Progress::InProgress; - } + state.words_left -= words_used; + config.processed(words_used*sizeof(uint32_t)); + return is_last ? Progress::Done : Progress::InProgress; } static Progress switch_state_parse_data(State::Configuration& config, SimpleTIMState& state) { @@ -139,7 +106,7 @@ namespace JabyEngine { } State create(const uint32_t* data_adr, const SimpleTIM& file) { - return State::from(SimpleTIMState(file), reinterpret_cast(data_adr), parse_header); + return State::from(SimpleTIMState(file), data_adr, parse_header); } } } diff --git a/src/Library/src/File/Processor/vag_processor.cpp b/src/Library/src/File/Processor/vag_processor.cpp new file mode 100644 index 00000000..3d256035 --- /dev/null +++ b/src/Library/src/File/Processor/vag_processor.cpp @@ -0,0 +1,81 @@ +#include "../../../internal-include/SPU/spu_internal.hpp" +#include +#include +#include +#include +#include + +#ifdef __SUPPORT_VAG__ +namespace JabyEngine { + namespace FileProcessor { + struct VAGHeader { + char id[4]; + uint32_t version; + uint32_t reserved; + uint32_t sample_size; + uint32_t sample_frequency; + uint8_t reserved_2[12]; + char name[16]; + + constexpr uint32_t get_version() const { + return read_be(this->version); + } + + constexpr uint32_t get_sample_size() const { + return read_be(this->sample_size); + } + + constexpr uint32_t get_sample_frequency() const { + return read_be(this->sample_frequency); + } + }; + + struct VAGState { + uint32_t voice_id; + size_t words_left; + SPU::SimpleVolume inital_vol; + + static constexpr VAGState create(uint32_t voice_id, SPU::SimpleVolume inital_vol) { + return VAGState{.voice_id = voice_id, .words_left = 0, .inital_vol = inital_vol}; + } + }; + + static Progress parse_sample(State::Configuration& config, VAGState& state) { + const auto [words_to_use, is_last] = Helper::DMA::WordsReady::calculate(config, state.words_left); + const auto words_used = Helper::DMA::send_words(words_to_use, is_last); + + state.words_left -= words_used; + config.processed(words_used*sizeof(uint32_t)); + return is_last ? Progress::Done : Progress::InProgress; + } + + static Progress parse_header(State::Configuration& config, VAGState& state) { + if(config.data_bytes >= sizeof(VAGHeader)) { + const auto& header = *reinterpret_cast(config.data_adr); + const auto words = bytes_to_words(header.get_sample_size()); + const auto bytes = words_to_bytes(words); + + state.words_left = words; + + auto sram_adr = SPU::voice[state.voice_id].allocate(SPU_IO::SampleRate::from_HZ(header.get_sample_frequency()), bytes); + // TODO: Keep this as optional? + printf("SPU: Allocated %i @0x%p to 0x%p (%i bytes)\n", state.voice_id, sram_adr.raw, (sram_adr.raw + bytes), bytes); + SPU::voice[state.voice_id].set_volume(state.inital_vol, state.inital_vol); + + config.processed(sizeof(VAGHeader)); + SPU::internal::DMA::Receive::prepare(); + SPU::internal::DMA::Receive::set_dst(sram_adr); + SPU::internal::DMA::Receive::set_src(reinterpret_cast(config.data_adr)); + + return Helper::exchange_and_execute_process_function(parse_sample, config, state); + } + + return Progress::InProgress; + } + + State create(const uint32_t* data_adr, const VAG& file) { + return State::from(VAGState::create(file.voice_number, file.inital_stereo_vol), data_adr, parse_header); + } + } +} +#endif // __SUPPORT_VAG__ \ No newline at end of file diff --git a/src/Library/src/GPU/gpu.cpp b/src/Library/src/GPU/gpu.cpp index f4215500..30feeaf0 100644 --- a/src/Library/src/GPU/gpu.cpp +++ b/src/Library/src/GPU/gpu.cpp @@ -8,11 +8,9 @@ namespace JabyEngine { uint8_t Display :: current_id = 1; //< Setup will call exchange and set it to 0 namespace internal { - namespace DMA { - #ifdef __SUPPORT_PS3__ - uintptr_t MADR = 0; - #endif // __SUPPORT_PS3__ - } + #ifdef __SUPPORT_PS3__ + uintptr_t DMA :: MADR = 0; + #endif // __SUPPORT_PS3__ static SysCall::InterruptVerifierResult interrupt_verifier(); static void interrupt_handler(uint32_t); diff --git a/src/Library/src/SPU/spu.cpp b/src/Library/src/SPU/spu.cpp new file mode 100644 index 00000000..46042ad6 --- /dev/null +++ b/src/Library/src/SPU/spu.cpp @@ -0,0 +1,31 @@ +#include "../../internal-include/SPU/spu_internal.hpp" +#include "../../internal-include/SPU/spu_mmu.hpp" +#include +#include +#include + +#include + +namespace JabyEngine { + namespace SPU { + SRAMAdr Voice :: allocate(size_t size) { + Voice::stop(); + const auto voice_id = Voice::get_id(); + const auto adr = SRAMAdr{static_cast(reinterpret_cast(SPU_MMU::allocate(voice_id, size)))}; + + SPU_IO::Voice[voice_id].adr.write(adr); + return adr; + } + + SRAMAdr Voice :: allocate(SPU_IO::SampleRate frequency, size_t size) { + const auto result = Voice::allocate(size); + Voice::set_sample_rate(frequency); + return result; + } + + void Voice :: deallocate() { + Voice::stop(); + SPU_MMU::deallocate(Voice::get_id()); + } + } +} \ No newline at end of file diff --git a/src/Library/src/SPU/spu_mmu.cpp b/src/Library/src/SPU/spu_mmu.cpp new file mode 100644 index 00000000..967d8b83 --- /dev/null +++ b/src/Library/src/SPU/spu_mmu.cpp @@ -0,0 +1,125 @@ +#include "../../internal-include/SPU/spu_mmu.hpp" +#include +#include +#include +#include + +namespace JabyEngine { + namespace SPU_MMU { + namespace SPU_MemoryMap = SPU_IO::MemoryMap; + + struct SPUMemory { + // TODO: v change to uint16_t?? + const uint8_t* adr; + size_t size; + + static SPUMemory create(size_t size) { + return SPUMemory{.adr = reinterpret_cast(SPU_MemoryMap::ADPCM), .size = size}; + } + + constexpr void clear() { + this->adr = nullptr; + this->size = 0; + } + + constexpr const uint8_t* get_end_adr() const { + return (this->adr + this->size); + } + + constexpr bool is_free() const { + return this->adr == nullptr; + } + + constexpr bool intersects(const SPUMemory& other) const { + const auto* min = max_of(this->adr, other.adr); + const auto* max = min_of(this->get_end_adr(), other.get_end_adr()); + return min < max; + } + }; + + struct AllocatedVoice { + SPUMemory memory; + AllocatedVoice* next_entry; + }; + + struct VoiceManager { + struct Iterator { + AllocatedVoice* *prev_voice; + AllocatedVoice* current_voice; + + void next() { + this->prev_voice = &this->current_voice->next_entry; + this->current_voice = this->current_voice->next_entry; + } + + bool has_next() const { + return this->current_voice; + } + + operator bool() const { + return Iterator::has_next(); + } + }; + + AllocatedVoice allocated_voice_buffer[SPU_IO::VoiceCount + SPU_IO::ReverbCount] = {0}; + AllocatedVoice* first_allocated_voice = nullptr; + + Iterator iterator() { + return Iterator{.prev_voice = &this->first_allocated_voice, .current_voice = this->first_allocated_voice}; + } + + AllocatedVoice& get_voice(uint8_t id) { + return this->allocated_voice_buffer[id]; + } + }; + + static VoiceManager voice_mgr; + + using MemoryFoundCallback = const uint8_t* (AllocatedVoice& new_entry, AllocatedVoice* &prev_entry, AllocatedVoice* next_entry); + + static const uint8_t* verify_and_add(AllocatedVoice& new_entry, AllocatedVoice* &prev_entry, AllocatedVoice* next_entry) { + // TODO: Verify that we are not crashing into reverb or that we are higher then SPU + prev_entry = &new_entry; + new_entry.next_entry = next_entry; + + return new_entry.memory.adr; + } + + static const uint8_t* find_first_fit(AllocatedVoice &new_entry, MemoryFoundCallback memory_found) { + auto iterator = voice_mgr.iterator(); + while(iterator) { + if(!iterator.current_voice->memory.intersects(new_entry.memory)) { + break; + } + new_entry.memory.adr = iterator.current_voice->memory.get_end_adr(); + iterator.next(); + } + + return memory_found(new_entry, *iterator.prev_voice, iterator.current_voice); + } + + const uint8_t* allocate(uint8_t voice, size_t size) { + auto& voice_entry = voice_mgr.get_voice(voice); + if(!voice_entry.memory.is_free()) { + deallocate(voice); + } + + voice_entry.memory = SPUMemory::create(size); + return find_first_fit(voice_entry, verify_and_add); + } + + void deallocate(uint8_t voice) { + auto* voice_adr = &voice_mgr.get_voice(voice); + auto iterator = voice_mgr.iterator(); + + voice_adr->memory.clear(); + while(iterator) { + if(iterator.current_voice == voice_adr) { + *iterator.prev_voice = voice_adr->next_entry; + break; + } + iterator.next(); + } + } + } +} \ No newline at end of file diff --git a/src/Tools/jaby_engine_fconv/Cargo.toml b/src/Tools/jaby_engine_fconv/Cargo.toml index add43e6a..fb624db9 100644 --- a/src/Tools/jaby_engine_fconv/Cargo.toml +++ b/src/Tools/jaby_engine_fconv/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jaby_engine_fconv" -version = "0.1.6" +version = "0.2.0" edition = "2021" [profile.release] diff --git a/src/Tools/jaby_engine_fconv/src/audio/mod.rs b/src/Tools/jaby_engine_fconv/src/audio/mod.rs index ec567062..bdfdb429 100644 --- a/src/Tools/jaby_engine_fconv/src/audio/mod.rs +++ b/src/Tools/jaby_engine_fconv/src/audio/mod.rs @@ -1 +1,43 @@ -pub mod xa; \ No newline at end of file +pub mod xa; +pub mod vag; + +use std::{env, path::PathBuf, process::Command}; +use tool_helper::Error; + +fn run_psxavenc(input: PathBuf, output: PathBuf, args: I) -> Result<(), Error> +where + I: IntoIterator, + S: AsRef, { + let psxavenc = get_psxavenc_path()?; + let result = Command::new(psxavenc).args(args).arg(input.to_string_lossy().as_ref()).arg(output.to_string_lossy().as_ref()).output()?; + + let stderr = tool_helper::vec_helper::to_string(result.stderr)?; + let stdout = tool_helper::vec_helper::to_string(result.stdout)?; + + if !result.status.success() { + return Err(Error::from_text(format!("psxavenc returned: {}. {}", result.status, stderr))); + } + + if !stdout.is_empty() { + println!("{}", stdout); + } + Ok(()) +} + +fn get_psxavenc_path() -> Result { + let tool_path = { + let mut my_path = env::current_exe()?; + + my_path.pop(); + my_path.push("extern"); + my_path.push("psxavenc"); + + my_path + }; + + if !tool_path.exists() { + return Err(Error::from_str("Could not locate psxavenc. Make sure it is installed under \"/bin/extern\"")); + } + + Ok(tool_path) +} \ No newline at end of file diff --git a/src/Tools/jaby_engine_fconv/src/audio/vag/mod.rs b/src/Tools/jaby_engine_fconv/src/audio/vag/mod.rs new file mode 100644 index 00000000..f9e9a411 --- /dev/null +++ b/src/Tools/jaby_engine_fconv/src/audio/vag/mod.rs @@ -0,0 +1,22 @@ +use clap::Args; +use std::path::PathBuf; +use tool_helper::Error; + +#[derive(Args)] +pub struct Arguments { + frequency: Option +} + +pub fn convert(args: Arguments, input: PathBuf, output: PathBuf) -> Result<(), Error> { + let mut cmd_args = vec!["-t", "vag"]; + let frequency_str; + + if let Some(frequency) = args.frequency { + frequency_str = frequency.to_string().to_owned(); + + cmd_args.push("-f"); + cmd_args.push(frequency_str.as_ref()); + } + + super::run_psxavenc(input, output, cmd_args) +} \ No newline at end of file diff --git a/src/Tools/jaby_engine_fconv/src/audio/xa/mod.rs b/src/Tools/jaby_engine_fconv/src/audio/xa/mod.rs index c53257d4..82a66ed4 100644 --- a/src/Tools/jaby_engine_fconv/src/audio/xa/mod.rs +++ b/src/Tools/jaby_engine_fconv/src/audio/xa/mod.rs @@ -1,5 +1,5 @@ use clap::{Args, ValueEnum}; -use std::{env, path::PathBuf, process::Command, str}; +use std::{path::PathBuf, str}; use tool_helper::Error; #[derive(Args)] @@ -43,39 +43,13 @@ impl Sample { pub fn convert(args: Arguments, input: PathBuf, output: PathBuf) -> Result<(), Error> { let quality = args.quality; let sample = args.sample; - let tool_path = { - let mut my_path = env::current_exe()?; - - my_path.pop(); - my_path.push("extern"); - my_path.push("psxavenc"); - - my_path - }; - - if !tool_path.exists() { - return Err(Error::from_str("Could not locate psxavenc. Make sure it is installed under \"/bin/extern\"")); - } - - let result = Command::new(tool_path).args([ + + super::run_psxavenc(input, output, [ "-t", "xacd", "-f", quality.get_frequency().to_string().as_ref(), "-b", "4", "-c", sample.get_channel().to_string().as_ref(), "-F", "0", "-C", "0", - input.to_string_lossy().as_ref(), output.to_string_lossy().as_ref() - ]) .output()?; - - let stderr = tool_helper::vec_helper::to_string(result.stderr)?; - let stdout = tool_helper::vec_helper::to_string(result.stdout)?; - - if !result.status.success() { - return Err(Error::from_text(format!("psxavenc returned: {}. {}", result.status, stderr))); - } - - if !stdout.is_empty() { - println!("{}", stdout); - } - Ok(()) + ]) } \ No newline at end of file diff --git a/src/Tools/jaby_engine_fconv/src/main.rs b/src/Tools/jaby_engine_fconv/src/main.rs index 219dae79..e96f4413 100644 --- a/src/Tools/jaby_engine_fconv/src/main.rs +++ b/src/Tools/jaby_engine_fconv/src/main.rs @@ -21,9 +21,25 @@ struct CommandLine { #[derive(Subcommand)] enum SubCommands { + // === Internal Commands === Nothing, SimpleTIM(reduced_tim::Arguments), - XA(xa::Arguments) + + // === External Commands === + VAG(vag::Arguments), + XA(xa::Arguments), +} + +impl SubCommands { + pub fn is_external_command(&self) -> bool { + match self { + SubCommands::Nothing => false, + SubCommands::SimpleTIM(_) => false, + + SubCommands::VAG(_) => true, + SubCommands::XA(_) => true + } + } } fn run_internal_conversion(cmd: CommandLine) -> Result<(), Error> { @@ -44,7 +60,7 @@ fn run_internal_conversion(cmd: CommandLine) -> Result<(), Error> { match cmd.sub_command { SubCommands::Nothing => nothing::copy(&mut input, dst_buffer), SubCommands::SimpleTIM(args) => reduced_tim::convert(args, input, dst_buffer), - _ => Err(Error::not_implemented("External functions can not be called for internal conversion")) + _ => Err(Error::from_str("External functions can not be called for internal conversion")) } }; @@ -74,13 +90,14 @@ fn run_external_conversion(cmd: CommandLine) -> Result<(), Error> { let output_file = cmd.output_file.ok_or(Error::from_str("Output has to be a file"))?; match cmd.sub_command { - SubCommands::XA(args) => xa::convert(args, input_file, output_file), - _ => Err(Error::not_implemented("Internal functions can not be called for external conversion")) + SubCommands::VAG(args) => vag::convert(args, input_file, output_file), + SubCommands::XA(args) => xa::convert(args, input_file, output_file), + _ => Err(Error::from_str("Internal functions can not be called for external conversion")) } } fn run_main(cmd: CommandLine) -> Result<(), Error> { - if matches!(cmd.sub_command, SubCommands::XA(_)) { + if cmd.sub_command.is_external_command() { run_external_conversion(cmd) }