├── .github └── FUNDING.yml ├── libs ├── CMakeLists.txt └── ymir-core │ ├── include │ └── ymir │ │ ├── sys │ │ ├── system_features.hpp │ │ ├── system_internal_callbacks.hpp │ │ └── memory_defs.hpp │ │ ├── hw │ │ ├── sh1 │ │ │ ├── sh1_defs.hpp │ │ │ ├── sh1_power.hpp │ │ │ ├── sh1_regs.hpp │ │ │ └── sh1_internal_callbacks.hpp │ │ ├── cart │ │ │ ├── cart.hpp │ │ │ ├── rom_cart_defs.hpp │ │ │ └── cart_impl_none.hpp │ │ ├── scu │ │ │ ├── scu_internal_callbacks.hpp │ │ │ └── scu_callbacks.hpp │ │ ├── cdblock │ │ │ ├── cdblock_buffer.hpp │ │ │ ├── ygr_internal_callbacks.hpp │ │ │ ├── cd_drive_internal_callbacks.hpp │ │ │ └── cdblock_internal_callbacks.hpp │ │ ├── sh2 │ │ │ ├── sh2_internal_callbacks.hpp │ │ │ ├── sh2_regs.hpp │ │ │ ├── sh2_ubc.hpp │ │ │ └── sh2_sci.hpp │ │ ├── scsp │ │ │ ├── scsp_internal_callbacks.hpp │ │ │ ├── scsp_callbacks.hpp │ │ │ └── scsp_midi_defs.hpp │ │ ├── smpc │ │ │ └── peripheral │ │ │ │ ├── peripheral_callbacks.hpp │ │ │ │ ├── peripheral_impl_null.hpp │ │ │ │ ├── peripheral_state_common.hpp │ │ │ │ └── peripheral_impl_control_pad.hpp │ │ ├── hw_defs.hpp │ │ └── vdp │ │ │ ├── vdp_internal_callbacks.hpp │ │ │ └── vdp_callbacks.hpp │ │ ├── ymir.hpp │ │ ├── state │ │ ├── state_scsp_timer.hpp │ │ ├── state_m68k.hpp │ │ ├── state_scheduler.hpp │ │ ├── state_system.hpp │ │ ├── state_ygr.hpp │ │ ├── state_smpc.hpp │ │ └── state_scsp_dsp.hpp │ │ ├── util │ │ ├── thread_name.hpp │ │ ├── unreachable.hpp │ │ ├── process.hpp │ │ ├── type_traits_ex.hpp │ │ └── backup_datetime.hpp │ │ ├── media │ │ ├── binary_reader │ │ │ ├── binary_reader_impl.hpp │ │ │ ├── binary_reader.hpp │ │ │ └── binary_reader_zero.hpp │ │ ├── cdrom_crc.hpp │ │ ├── loader │ │ │ ├── loader_result.hpp │ │ │ ├── loader.hpp │ │ │ ├── loader_chd.hpp │ │ │ ├── loader_mdf_mds.hpp │ │ │ └── loader_iso.hpp │ │ └── frame_address.hpp │ │ ├── core │ │ └── types.hpp │ │ ├── db │ │ └── cdb_rom_db.hpp │ │ └── debug │ │ ├── cd_drive_tracer_base.hpp │ │ └── scsp_tracer_base.hpp │ └── src │ └── ymir │ ├── core │ ├── configuration.cpp │ └── hash.cpp │ ├── hw │ └── cart │ │ ├── cart_slot.cpp │ │ └── cart_impl_bup.cpp │ ├── ymir.cpp │ ├── db │ ├── rom_cart_db.cpp │ └── cdb_rom_db.cpp │ └── media │ └── cdrom_crc.cpp ├── tests ├── CMakeLists.txt └── ymir-core-tests │ └── src │ └── hw │ └── sh2 │ └── sh2_disasm_tests.cpp ├── .gitattributes ├── docs ├── images │ ├── debugger.png │ ├── sonic-r.png │ ├── cd-player.png │ ├── nights-into-dreams.png │ ├── radiant-silvergun.png │ ├── virtua-fighter-2.png │ └── panzer-dragoon-saga.png └── dev-notes │ ├── finicky-games │ ├── sh2-cache.txt │ ├── cdblock-put-sector.txt │ ├── smpc-sh2-direct-mode.txt │ ├── scsp-unaligned-16-bit-samples.txt │ ├── vdp1-command-timings.txt │ ├── vdp1-illegal-sprite-color-data.txt │ ├── vdp2-vertical-cell-scroll.txt │ ├── vdp2-midframe-effects.txt │ ├── vdp-sprite-data-mismatch.txt │ ├── cdblock-nontrivial-filters.txt │ └── vdp2-bad-window-params.txt │ └── system-info │ └── cdblock-ygr.txt ├── apps ├── ymir-sdl3 │ ├── res │ │ ├── ymir.icns │ │ ├── ymir.ico │ │ ├── ymir.png │ │ ├── embed │ │ │ ├── images │ │ │ │ └── ymir.png │ │ │ └── fonts │ │ │ │ ├── SplineSans-Bold.ttf │ │ │ │ ├── ZenDots-Regular.ttf │ │ │ │ ├── SplineSans-Medium.ttf │ │ │ │ ├── SplineSansMono-Bold.ttf │ │ │ │ ├── SplineSansMono-Medium.ttf │ │ │ │ └── MaterialSymbolsOutlined_Filled-Regular.ttf │ │ ├── io.github.strikerx3.ymir.desktop │ │ └── ymir.rc │ ├── src │ │ ├── app │ │ │ ├── toml_implementation.cpp │ │ │ ├── stb_implementations.cpp │ │ │ ├── ui │ │ │ │ ├── defs │ │ │ │ │ └── settings_defs.hpp │ │ │ │ ├── widgets │ │ │ │ │ ├── cartridge_widgets.hpp │ │ │ │ │ ├── datetime_widgets.hpp │ │ │ │ │ ├── common_widgets.hpp │ │ │ │ │ ├── system_widgets.hpp │ │ │ │ │ ├── peripheral_widgets.hpp │ │ │ │ │ ├── common_widgets.cpp │ │ │ │ │ ├── audio_widgets.hpp │ │ │ │ │ ├── unbound_actions_widget.hpp │ │ │ │ │ ├── settings_widgets.hpp │ │ │ │ │ ├── cartridge_widgets.cpp │ │ │ │ │ └── savestate_widgets.hpp │ │ │ │ ├── windows │ │ │ │ │ ├── debug │ │ │ │ │ │ ├── vdp_window_base.cpp │ │ │ │ │ │ ├── scsp_window_base.cpp │ │ │ │ │ │ ├── cdblock_window_base.cpp │ │ │ │ │ │ ├── sh2_window_base.cpp │ │ │ │ │ │ ├── scsp_slots_window.hpp │ │ │ │ │ │ ├── sh2_window_base.hpp │ │ │ │ │ │ ├── vdp_window_base.hpp │ │ │ │ │ │ ├── scsp_window_base.hpp │ │ │ │ │ │ ├── sh2_power_window.hpp │ │ │ │ │ │ ├── vdp1_registers_window.hpp │ │ │ │ │ │ ├── scsp_slots_window.cpp │ │ │ │ │ │ ├── cdblock_window_base.hpp │ │ │ │ │ │ ├── vdp2_vram_delay_window.hpp │ │ │ │ │ │ ├── sh2_interrupts_window.hpp │ │ │ │ │ │ ├── vdp2_cram_window.hpp │ │ │ │ │ │ ├── vdp2_layer_params_window.hpp │ │ │ │ │ │ ├── scsp_output_window.hpp │ │ │ │ │ │ ├── vdp1_registers_window.cpp │ │ │ │ │ │ ├── debug_output_window.hpp │ │ │ │ │ │ ├── vdp2_layer_visibility_window.hpp │ │ │ │ │ │ ├── vdp2_vram_delay_window.cpp │ │ │ │ │ │ ├── cdblock_filters_window.hpp │ │ │ │ │ │ ├── scu_dma_trace_window.hpp │ │ │ │ │ │ ├── sh2_breakpoints_window.hpp │ │ │ │ │ │ ├── sh2_watchpoints_window.hpp │ │ │ │ │ │ ├── vdp2_debug_overlay_window.hpp │ │ │ │ │ │ ├── vdp2_layer_params_window.cpp │ │ │ │ │ │ ├── scu_interrupt_trace_window.hpp │ │ │ │ │ │ ├── cdblock_ygr_cmd_trace_window.hpp │ │ │ │ │ │ ├── scsp_kyonex_trace_window.hpp │ │ │ │ │ │ ├── sh2_exception_vectors_window.hpp │ │ │ │ │ │ ├── cdblock_cmd_trace_window.hpp │ │ │ │ │ │ ├── sh2_interrupt_trace_window.hpp │ │ │ │ │ │ ├── cdblock_drive_state_trace_window.hpp │ │ │ │ │ │ ├── scu_dma_window.hpp │ │ │ │ │ │ ├── sh2_timers_window.hpp │ │ │ │ │ │ ├── vdp2_layer_visibility_window.cpp │ │ │ │ │ │ ├── sh2_power_window.cpp │ │ │ │ │ │ ├── sh2_interrupts_window.cpp │ │ │ │ │ │ ├── sh2_cache_window.hpp │ │ │ │ │ │ ├── sh2_dmac_trace_window.hpp │ │ │ │ │ │ ├── cdblock_filters_window.cpp │ │ │ │ │ │ ├── scu_registers_window.hpp │ │ │ │ │ │ ├── sh2_dmac_window.hpp │ │ │ │ │ │ ├── scsp_window_set.hpp │ │ │ │ │ │ ├── scu_dma_trace_window.cpp │ │ │ │ │ │ ├── debug_output_window.cpp │ │ │ │ │ │ ├── cdblock_ygr_cmd_trace_window.cpp │ │ │ │ │ │ ├── cdblock_cmd_trace_window.cpp │ │ │ │ │ │ ├── cdblock_drive_state_trace_window.cpp │ │ │ │ │ │ ├── sh2_timers_window.cpp │ │ │ │ │ │ ├── vdp2_cram_window.cpp │ │ │ │ │ │ ├── memory_viewer_window.hpp │ │ │ │ │ │ ├── sh2_divu_window.hpp │ │ │ │ │ │ ├── scsp_output_window.cpp │ │ │ │ │ │ ├── scu_registers_window.cpp │ │ │ │ │ │ ├── scu_interrupt_trace_window.cpp │ │ │ │ │ │ ├── vdp2_debug_overlay_window.cpp │ │ │ │ │ │ ├── scsp_kyonex_trace_window.cpp │ │ │ │ │ │ ├── sh2_debugger_window.hpp │ │ │ │ │ │ ├── sh2_interrupt_trace_window.cpp │ │ │ │ │ │ ├── sh2_exception_vectors_window.cpp │ │ │ │ │ │ ├── sh2_breakpoints_window.cpp │ │ │ │ │ │ ├── sh2_watchpoints_window.cpp │ │ │ │ │ │ ├── cdblock_window_set.hpp │ │ │ │ │ │ ├── scu_window_set.hpp │ │ │ │ │ │ ├── scu_dsp_window.hpp │ │ │ │ │ │ ├── sh2_cache_window.cpp │ │ │ │ │ │ ├── sh2_dmac_window.cpp │ │ │ │ │ │ ├── scu_dma_window.cpp │ │ │ │ │ │ └── sh2_divu_window.cpp │ │ │ │ │ ├── update_window.hpp │ │ │ │ │ ├── about_window.hpp │ │ │ │ │ ├── update_onboarding_window.hpp │ │ │ │ │ ├── backup_ram_manager_window.hpp │ │ │ │ │ ├── system_state_window.hpp │ │ │ │ │ └── peripheral_config_window.hpp │ │ │ │ ├── views │ │ │ │ │ ├── settings │ │ │ │ │ │ ├── gui_settings_view.hpp │ │ │ │ │ │ ├── audio_settings_view.hpp │ │ │ │ │ │ ├── input_settings_view.hpp │ │ │ │ │ │ ├── video_settings_view.hpp │ │ │ │ │ │ ├── tweaks_settings_view.hpp │ │ │ │ │ │ ├── settings_view_base.cpp │ │ │ │ │ │ ├── hotkeys_settings_view.hpp │ │ │ │ │ │ ├── analog_pad_config_view.hpp │ │ │ │ │ │ ├── control_pad_config_view.hpp │ │ │ │ │ │ ├── settings_view_base.hpp │ │ │ │ │ │ ├── cdblock_settings_view.hpp │ │ │ │ │ │ ├── mission_stick_config_view.hpp │ │ │ │ │ │ ├── ipl_settings_view.hpp │ │ │ │ │ │ ├── arcade_racer_config_view.hpp │ │ │ │ │ │ ├── system_settings_view.hpp │ │ │ │ │ │ ├── general_settings_view.hpp │ │ │ │ │ │ └── cartridge_settings_view.hpp │ │ │ │ │ └── debug │ │ │ │ │ │ ├── scu_registers_view.hpp │ │ │ │ │ │ ├── scu_timers_view.hpp │ │ │ │ │ │ ├── scu_dsp_data_ram_view.hpp │ │ │ │ │ │ ├── scu_dma_state_view.hpp │ │ │ │ │ │ ├── scu_dsp_registers_view.hpp │ │ │ │ │ │ ├── sh2_power_view.hpp │ │ │ │ │ │ ├── vdp2_cram_view.hpp │ │ │ │ │ │ ├── cdblock_filters_view.hpp │ │ │ │ │ │ ├── scsp_kyonex_trace_view.hpp │ │ │ │ │ │ ├── scu_dma_registers_view.hpp │ │ │ │ │ │ ├── scu_dsp_dma_registers_view.hpp │ │ │ │ │ │ ├── sh2_wdt_view.hpp │ │ │ │ │ │ ├── vdp1_registers_view.hpp │ │ │ │ │ │ ├── vdp2_vram_delay_view.hpp │ │ │ │ │ │ ├── sh2_cache_entries_view.hpp │ │ │ │ │ │ ├── sh2_debug_toolbar_view.hpp │ │ │ │ │ │ ├── sh2_frt_view.hpp │ │ │ │ │ │ ├── vdp2_layer_params_view.hpp │ │ │ │ │ │ ├── sh2_cache_register_view.hpp │ │ │ │ │ │ ├── vdp2_debug_overlay_view.hpp │ │ │ │ │ │ ├── scu_dma_trace_view.hpp │ │ │ │ │ │ ├── vdp2_layer_visibility_view.hpp │ │ │ │ │ │ ├── scu_dsp_dma_trace_view.hpp │ │ │ │ │ │ ├── sh2_divu_registers_view.hpp │ │ │ │ │ │ ├── sh2_dmac_registers_view.hpp │ │ │ │ │ │ ├── sh2_registers_view.hpp │ │ │ │ │ │ ├── cdblock_ygr_cmd_trace_view.hpp │ │ │ │ │ │ ├── scu_interrupt_trace_view.hpp │ │ │ │ │ │ ├── cdblock_cmd_trace_view.hpp │ │ │ │ │ │ ├── scsp_slots_view.hpp │ │ │ │ │ │ ├── sh2_breakpoints_view.hpp │ │ │ │ │ │ ├── cdblock_drive_state_trace_view.hpp │ │ │ │ │ │ ├── sh2_interrupt_trace_view.hpp │ │ │ │ │ │ ├── scu_interrupts_view.hpp │ │ │ │ │ │ ├── sh2_divu_statistics_view.hpp │ │ │ │ │ │ ├── sh2_interrupts_view.hpp │ │ │ │ │ │ ├── sh2_divu_trace_view.hpp │ │ │ │ │ │ ├── sh2_dmac_channel_view.hpp │ │ │ │ │ │ ├── scsp_output_view.hpp │ │ │ │ │ │ ├── sh2_dmac_trace_view.hpp │ │ │ │ │ │ ├── sh2_exception_vectors_view.hpp │ │ │ │ │ │ ├── sh2_watchpoints_view.hpp │ │ │ │ │ │ ├── scsp_output_view.cpp │ │ │ │ │ │ ├── debug_output_view.hpp │ │ │ │ │ │ ├── scu_registers_view.cpp │ │ │ │ │ │ └── vdp2_layer_visibility_view.cpp │ │ │ │ ├── window_base.hpp │ │ │ │ └── window_base.cpp │ │ │ ├── cmdline_opts.hpp │ │ │ ├── message.hpp │ │ │ ├── debug │ │ │ │ ├── scsp_tracer.cpp │ │ │ │ ├── cd_drive_tracer.cpp │ │ │ │ ├── cdblock_tracer.cpp │ │ │ │ ├── cd_drive_tracer.hpp │ │ │ │ ├── cdblock_tracer.hpp │ │ │ │ ├── ygr_tracer.hpp │ │ │ │ └── scsp_tracer.hpp │ │ │ ├── input │ │ │ │ └── input_bind.hpp │ │ │ └── events │ │ │ │ └── emu_debug_event_factory.hpp │ │ └── util │ │ │ ├── regions.hpp │ │ │ ├── os_exception_handler.hpp │ │ │ ├── math.hpp │ │ │ ├── std_lib.hpp │ │ │ ├── file_loader.hpp │ │ │ ├── mig │ │ │ ├── macos_mig.c │ │ │ └── macos_mig.h │ │ │ ├── os_features.hpp │ │ │ ├── std_lib.cpp │ │ │ ├── rom_loader.hpp │ │ │ ├── os_features.cpp │ │ │ ├── file_loader.cpp │ │ │ └── regions.cpp │ └── packaging │ │ └── CMakeLists.txt ├── CMakeLists.txt ├── ymir-sandbox │ └── README.md └── ymdasm │ ├── src │ ├── disassembler_sh2.hpp │ ├── disassembler_m68k.hpp │ ├── disassembler_scudsp.hpp │ ├── disassembler_scspdsp.hpp │ ├── utils.hpp │ └── ansi.hpp │ └── packaging │ └── CMakeLists.txt ├── vcpkg-triplets ├── arm64-windows.cmake ├── x64-win-llvm │ ├── Platform │ │ ├── Clang-CL-C.cmake │ │ └── Clang-CL-CXX.cmake │ └── Notes.md ├── x64-windows.cmake ├── x64-windows-release.cmake ├── x64-win-llvm-static-md.cmake ├── LICENSE ├── x64-win-llvm-lto-static-md.cmake ├── x64-win-llvm-san-static-md.cmake └── x64-win-llvm-lto-san-static-md.cmake ├── cmake ├── DefineWrapFile.cmake ├── TargetConditionalSources.cmake ├── VSHelpers.cmake └── CopyRuntimeDLLs.cmake ├── vcpkg-configuration.json ├── vendor └── lz4 │ └── CMakeLists.txt ├── .gitignore ├── .gitmodules ├── vcpkg.json └── packaging └── CMakeLists.txt /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: StrikerX3 2 | -------------------------------------------------------------------------------- /libs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(ymir-core) 2 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(ymir-core-tests) 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | vendor/* linguist-vendored 2 | *.h linguist-language=cpp 3 | -------------------------------------------------------------------------------- /docs/images/debugger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrikerX3/Ymir/HEAD/docs/images/debugger.png -------------------------------------------------------------------------------- /docs/images/sonic-r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrikerX3/Ymir/HEAD/docs/images/sonic-r.png -------------------------------------------------------------------------------- /docs/images/cd-player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrikerX3/Ymir/HEAD/docs/images/cd-player.png -------------------------------------------------------------------------------- /apps/ymir-sdl3/res/ymir.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrikerX3/Ymir/HEAD/apps/ymir-sdl3/res/ymir.icns -------------------------------------------------------------------------------- /apps/ymir-sdl3/res/ymir.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrikerX3/Ymir/HEAD/apps/ymir-sdl3/res/ymir.ico -------------------------------------------------------------------------------- /apps/ymir-sdl3/res/ymir.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrikerX3/Ymir/HEAD/apps/ymir-sdl3/res/ymir.png -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/toml_implementation.cpp: -------------------------------------------------------------------------------- 1 | #define TOML_IMPLEMENTATION 2 | #include 3 | -------------------------------------------------------------------------------- /docs/images/nights-into-dreams.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrikerX3/Ymir/HEAD/docs/images/nights-into-dreams.png -------------------------------------------------------------------------------- /docs/images/radiant-silvergun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrikerX3/Ymir/HEAD/docs/images/radiant-silvergun.png -------------------------------------------------------------------------------- /docs/images/virtua-fighter-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrikerX3/Ymir/HEAD/docs/images/virtua-fighter-2.png -------------------------------------------------------------------------------- /docs/images/panzer-dragoon-saga.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrikerX3/Ymir/HEAD/docs/images/panzer-dragoon-saga.png -------------------------------------------------------------------------------- /apps/ymir-sdl3/res/embed/images/ymir.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrikerX3/Ymir/HEAD/apps/ymir-sdl3/res/embed/images/ymir.png -------------------------------------------------------------------------------- /vcpkg-triplets/arm64-windows.cmake: -------------------------------------------------------------------------------- 1 | set(VCPKG_TARGET_ARCHITECTURE arm64) 2 | set(VCPKG_CRT_LINKAGE dynamic) 3 | set(VCPKG_LIBRARY_LINKAGE static) 4 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/res/embed/fonts/SplineSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrikerX3/Ymir/HEAD/apps/ymir-sdl3/res/embed/fonts/SplineSans-Bold.ttf -------------------------------------------------------------------------------- /apps/ymir-sdl3/res/embed/fonts/ZenDots-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrikerX3/Ymir/HEAD/apps/ymir-sdl3/res/embed/fonts/ZenDots-Regular.ttf -------------------------------------------------------------------------------- /cmake/DefineWrapFile.cmake: -------------------------------------------------------------------------------- 1 | file(READ "${input_file}" file_contents) 2 | file(WRITE "${output_file}" "#if (${condition})\n${file_contents}\n#endif\n") 3 | -------------------------------------------------------------------------------- /vcpkg-triplets/x64-win-llvm/Platform/Clang-CL-C.cmake: -------------------------------------------------------------------------------- 1 | include("${CMAKE_CURRENT_LIST_DIR}/Clang-CL-override.cmake") 2 | __windows_compiler_msvc_clang(C) -------------------------------------------------------------------------------- /vcpkg-triplets/x64-windows.cmake: -------------------------------------------------------------------------------- 1 | set(VCPKG_TARGET_ARCHITECTURE x64) 2 | set(VCPKG_CRT_LINKAGE dynamic) 3 | set(VCPKG_LIBRARY_LINKAGE static) 4 | 5 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/res/embed/fonts/SplineSans-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrikerX3/Ymir/HEAD/apps/ymir-sdl3/res/embed/fonts/SplineSans-Medium.ttf -------------------------------------------------------------------------------- /apps/ymir-sdl3/res/embed/fonts/SplineSansMono-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrikerX3/Ymir/HEAD/apps/ymir-sdl3/res/embed/fonts/SplineSansMono-Bold.ttf -------------------------------------------------------------------------------- /vcpkg-triplets/x64-win-llvm/Platform/Clang-CL-CXX.cmake: -------------------------------------------------------------------------------- 1 | include("${CMAKE_CURRENT_LIST_DIR}/Clang-CL-override.cmake") 2 | __windows_compiler_msvc_clang(CXX) -------------------------------------------------------------------------------- /apps/ymir-sdl3/res/embed/fonts/SplineSansMono-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrikerX3/Ymir/HEAD/apps/ymir-sdl3/res/embed/fonts/SplineSansMono-Medium.ttf -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/stb_implementations.cpp: -------------------------------------------------------------------------------- 1 | #define STB_IMAGE_IMPLEMENTATION 2 | #define STB_IMAGE_WRITE_IMPLEMENTATION 3 | #include 4 | #include 5 | -------------------------------------------------------------------------------- /vcpkg-triplets/x64-windows-release.cmake: -------------------------------------------------------------------------------- 1 | set(VCPKG_TARGET_ARCHITECTURE x64) 2 | set(VCPKG_CRT_LINKAGE dynamic) 3 | set(VCPKG_LIBRARY_LINKAGE static) 4 | set(VCPKG_BUILD_TYPE release) 5 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/res/embed/fonts/MaterialSymbolsOutlined_Filled-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrikerX3/Ymir/HEAD/apps/ymir-sdl3/res/embed/fonts/MaterialSymbolsOutlined_Filled-Regular.ttf -------------------------------------------------------------------------------- /apps/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (Ymir_ENABLE_SANDBOX) 2 | add_subdirectory(ymir-sandbox) 3 | endif () 4 | add_subdirectory(ymir-sdl3) 5 | if (Ymir_ENABLE_YMDASM) 6 | add_subdirectory(ymdasm) 7 | endif () 8 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/defs/settings_defs.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace app::ui { 4 | 5 | enum class SettingsTab { None, General, GUI, Hotkeys, System, IPL, Input, Video, Audio, Cartridge, CDBlock, Tweaks }; 6 | 7 | } // namespace app::ui 8 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/widgets/cartridge_widgets.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui::widgets { 6 | 7 | void CartridgeInfo(SharedContext &ctx); 8 | 9 | } // namespace app::ui::widgets 10 | -------------------------------------------------------------------------------- /apps/ymir-sandbox/README.md: -------------------------------------------------------------------------------- 1 | # Sandbox project 2 | 3 | This project is meant to be used as a sandbox for testing emulator features in isolation. 4 | For this reason, it is not included in the CMake packaging as it's not meant to make its way to end users. 5 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/sys/system_features.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace ymir::sys { 4 | 5 | struct SystemFeatures { 6 | bool enableDebugTracing = false; 7 | bool emulateSH2Cache = false; 8 | }; 9 | 10 | } // namespace ymir::sys 11 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/res/io.github.strikerx3.ymir.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Exec=ymir-sdl3 3 | Icon=ymir 4 | Terminal=false 5 | Type=Application 6 | Categories=Game;Emulator; 7 | Name=Ymir 8 | GenericName=Sega Saturn Emulator 9 | Comment=A Sega Saturn Emulator 10 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/util/regions.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace util { 8 | 9 | std::string RegionToString(ymir::core::config::sys::Region region); 10 | 11 | } // namespace util 12 | -------------------------------------------------------------------------------- /apps/ymdasm/src/disassembler_sh2.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "disassembler.hpp" 4 | 5 | #include 6 | 7 | bool DisassembleSH2(Disassembler &disasm, std::string_view origin, const std::vector &args, 8 | const std::string &inputFile); 9 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/widgets/datetime_widgets.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui::widgets { 6 | 7 | bool DateTimeSelector(const char *id, util::datetime::DateTime &dateTime); 8 | 9 | } // namespace app::ui::widgets 10 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/util/os_exception_handler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace util { 4 | 5 | void ShowFatalErrorDialog(const char *msg); 6 | 7 | void RegisterExceptionHandler(bool allExceptions); 8 | // void UnregisterExceptionHandler(); 9 | 10 | } // namespace util 11 | -------------------------------------------------------------------------------- /vcpkg-triplets/x64-win-llvm/Notes.md: -------------------------------------------------------------------------------- 1 | Just notes for myself to not forgot i already tried that 2 | # Additional Sanitizer stuff: 3 | -fsanitize-address-use-odr-indicator -fsanitize-address-globals-dead-stripping (duplicated asan symbols in lib/exe) 4 | -mllvm -asan-use-private-alias=1 -------------------------------------------------------------------------------- /apps/ymdasm/src/disassembler_m68k.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "disassembler.hpp" 4 | 5 | #include 6 | 7 | bool DisassembleM68K(Disassembler &disasm, std::string_view origin, const std::vector &args, 8 | const std::string &inputFile); 9 | -------------------------------------------------------------------------------- /apps/ymdasm/src/disassembler_scudsp.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "disassembler.hpp" 4 | 5 | #include 6 | 7 | bool DisassembleSCUDSP(Disassembler &disasm, std::string_view origin, const std::vector &args, 8 | const std::string &inputFile); 9 | -------------------------------------------------------------------------------- /docs/dev-notes/finicky-games/sh2-cache.txt: -------------------------------------------------------------------------------- 1 | ====================================== 2 | Games that rely on SH-2 cache features 3 | ====================================== 4 | 5 | NiGHTS into Dreams... 6 | Uses the cache data array (Cxxxxxxx) on the slave SH-2 as scratch memory for transfers. 7 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/sh1/sh1_defs.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace ymir::sh1 { 6 | 7 | inline constexpr size_t kROMSize = 64_KiB; 8 | inline constexpr uint64 kROMHashSeed = 0x65F0F39321BD1CE2ull; 9 | 10 | } // namespace ymir::sh1 11 | -------------------------------------------------------------------------------- /apps/ymdasm/src/disassembler_scspdsp.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "disassembler.hpp" 4 | 5 | #include 6 | 7 | bool DisassembleSCSPDSP(Disassembler &disasm, std::string_view origin, const std::vector &args, 8 | const std::string &inputFile, bool raw); 9 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/widgets/common_widgets.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace app::ui::widgets { 4 | 5 | // Creates a "(?)" element with a simple text explanation. 6 | void ExplanationTooltip(const char *explanation, float scale, bool sameLine = true); 7 | 8 | } // namespace app::ui::widgets 9 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/widgets/system_widgets.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui::widgets { 6 | 7 | bool VideoStandardSelector(SharedContext &ctx); 8 | bool RegionSelector(SharedContext &ctx); 9 | 10 | } // namespace app::ui::widgets 11 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/cart/cart.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | @file 5 | @brief Cartridge implementations. 6 | 7 | Includes all available cartridge implementation headers. 8 | */ 9 | 10 | #include "cart_impl_bup.hpp" 11 | #include "cart_impl_dram.hpp" 12 | #include "cart_impl_rom.hpp" 13 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/widgets/peripheral_widgets.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui::widgets { 6 | 7 | bool Port1PeripheralSelector(SharedContext &ctx); 8 | bool Port2PeripheralSelector(SharedContext &ctx); 9 | 10 | } // namespace app::ui::widgets 11 | -------------------------------------------------------------------------------- /docs/dev-notes/finicky-games/cdblock-put-sector.txt: -------------------------------------------------------------------------------- 1 | =============================================== 2 | Games that use CD Block Put Sector Data command 3 | =============================================== 4 | 5 | Virtual On - Cyber Warriors 6 | Uses the command to store replay buffers. Writes 2340 bytes per sector. 7 | -------------------------------------------------------------------------------- /apps/ymdasm/packaging/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | install(TARGETS ymdasm EXPORT Ymir_Targets 2 | RUNTIME COMPONENT Ymir_Runtime 3 | LIBRARY COMPONENT Ymir_Runtime 4 | NAMELINK_COMPONENT Ymir_Development 5 | ARCHIVE COMPONENT Ymir_Development 6 | INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") 7 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/util/math.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace util { 8 | 9 | FORCE_INLINE double RoundToMultiple(double value, double multiple) { 10 | return std::round(value / multiple) * multiple; 11 | } 12 | 13 | } // namespace util 14 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/ymir.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | @file 5 | @brief The entrypoint of the Ymir core library. Includes all functionality needed to operate the emulator. 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/state/state_scsp_timer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace ymir::state { 6 | 7 | struct SCSPTimer { 8 | uint8 incrementInterval; 9 | uint8 reload; 10 | 11 | bool doReload; 12 | uint8 counter; 13 | }; 14 | 15 | } // namespace ymir::state 16 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/vdp_window_base.cpp: -------------------------------------------------------------------------------- 1 | #include "vdp_window_base.hpp" 2 | 3 | namespace app::ui { 4 | 5 | VDPWindowBase::VDPWindowBase(SharedContext &context) 6 | : WindowBase(context) 7 | , m_vdp(context.saturn.GetVDP()) 8 | /*, m_tracer(context.tracers.VDP)*/ {} 9 | 10 | } // namespace app::ui 11 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/scsp_window_base.cpp: -------------------------------------------------------------------------------- 1 | #include "scsp_window_base.hpp" 2 | 3 | namespace app::ui { 4 | 5 | SCSPWindowBase::SCSPWindowBase(SharedContext &context) 6 | : WindowBase(context) 7 | , m_scsp(context.saturn.GetSCSP()) 8 | , m_tracer(context.tracers.SCSP) {} 9 | 10 | } // namespace app::ui 11 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/util/std_lib.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace util { 9 | 10 | tm to_local_time(std::chrono::system_clock::time_point tp); 11 | std::optional parse8601(std::string str); 12 | 13 | } // namespace util 14 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/settings/gui_settings_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "settings_view_base.hpp" 4 | 5 | namespace app::ui { 6 | 7 | class GUISettingsView : public SettingsViewBase { 8 | public: 9 | GUISettingsView(SharedContext &context); 10 | 11 | void Display(); 12 | }; 13 | 14 | } // namespace app::ui 15 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/cart/rom_cart_defs.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace ymir::cart { 8 | 9 | inline constexpr size_t kROMCartSize = 2_MiB; 10 | inline constexpr uint64 kROMCartHashSeed = 0xED19D10410708CB7ull; 11 | 12 | } // namespace ymir::cart 13 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/settings/audio_settings_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "settings_view_base.hpp" 4 | 5 | namespace app::ui { 6 | 7 | class AudioSettingsView : public SettingsViewBase { 8 | public: 9 | AudioSettingsView(SharedContext &context); 10 | 11 | void Display(); 12 | }; 13 | 14 | } // namespace app::ui 15 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/settings/input_settings_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "settings_view_base.hpp" 4 | 5 | namespace app::ui { 6 | 7 | class InputSettingsView : public SettingsViewBase { 8 | public: 9 | InputSettingsView(SharedContext &context); 10 | 11 | void Display(); 12 | }; 13 | 14 | } // namespace app::ui 15 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/settings/video_settings_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "settings_view_base.hpp" 4 | 5 | namespace app::ui { 6 | 7 | class VideoSettingsView : public SettingsViewBase { 8 | public: 9 | VideoSettingsView(SharedContext &context); 10 | 11 | void Display(); 12 | }; 13 | 14 | } // namespace app::ui 15 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/cdblock_window_base.cpp: -------------------------------------------------------------------------------- 1 | #include "cdblock_window_base.hpp" 2 | 3 | namespace app::ui { 4 | 5 | CDBlockWindowBase::CDBlockWindowBase(SharedContext &context) 6 | : WindowBase(context) 7 | , m_cdblock(context.saturn.GetCDBlock()) 8 | , m_tracer(context.tracers.CDBlock) {} 9 | 10 | } // namespace app::ui 11 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/util/file_loader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | namespace util { 9 | 10 | std::vector LoadFile(std::filesystem::path romPath); 11 | std::vector LoadFile(std::filesystem::path romPath, std::error_code &error); 12 | 13 | } // namespace util 14 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/scu_registers_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class SCURegistersView { 8 | public: 9 | SCURegistersView(SharedContext &context); 10 | 11 | void Display(); 12 | 13 | private: 14 | ymir::scu::SCU &m_scu; 15 | }; 16 | 17 | } // namespace app::ui 18 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/scu/scu_internal_callbacks.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace ymir::scu { 8 | 9 | // Invoked when the SCU raises an interrupt. 10 | using CBExternalInterrupt = util::RequiredCallback; 11 | 12 | } // namespace ymir::scu 13 | -------------------------------------------------------------------------------- /libs/ymir-core/src/ymir/core/configuration.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace ymir::core { 4 | 5 | void Configuration::NotifyObservers() { 6 | system.preferredRegionOrder.Notify(); 7 | 8 | video.threadedVDP.Notify(); 9 | 10 | audio.interpolation.Notify(); 11 | audio.threadedSCSP.Notify(); 12 | } 13 | 14 | } // namespace ymir::core 15 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/cmdline_opts.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app { 6 | 7 | struct CommandLineOptions { 8 | std::filesystem::path gameDiscPath; 9 | std::filesystem::path profilePath; 10 | bool forceUserProfile; 11 | bool fullScreen; 12 | bool startPaused; 13 | bool enableDebugTracing; 14 | }; 15 | 16 | } // namespace app 17 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/sh2_window_base.cpp: -------------------------------------------------------------------------------- 1 | #include "sh2_window_base.hpp" 2 | 3 | namespace app::ui { 4 | 5 | SH2WindowBase::SH2WindowBase(SharedContext &context, bool master) 6 | : WindowBase(context) 7 | , m_sh2(context.saturn.GetSH2(master)) 8 | , m_tracer(master ? context.tracers.masterSH2 : context.tracers.slaveSH2) {} 9 | 10 | } // namespace app::ui 11 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/update_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class UpdateWindow : public WindowBase { 8 | public: 9 | UpdateWindow(SharedContext &context); 10 | 11 | protected: 12 | void PrepareWindow() override; 13 | void DrawContents() override; 14 | }; 15 | 16 | } // namespace app::ui 17 | -------------------------------------------------------------------------------- /libs/ymir-core/src/ymir/hw/cart/cart_slot.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | namespace ymir::cart { 6 | 7 | CartridgeSlot::CartridgeSlot() { 8 | RemoveCartridge(); 9 | } 10 | 11 | void CartridgeSlot::RemoveCartridge() { 12 | m_cart = std::make_unique(); 13 | } 14 | 15 | } // namespace ymir::cart 16 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/scu_timers_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class SCUTimersView { 8 | public: 9 | SCUTimersView(SharedContext &context); 10 | 11 | void Display(); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::scu::SCU &m_scu; 16 | }; 17 | 18 | } // namespace app::ui 19 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/util/mig/macos_mig.c: -------------------------------------------------------------------------------- 1 | // Provides the mig server implementation for the current architecture 2 | // These files are generated from cmake using the 'mig' command 3 | 4 | #if defined(__x86_64__) 5 | #include "x64/mach_exc_server.c" 6 | #elif defined(__aarch64__) || defined(__arm64__) 7 | #include "a64/mach_exc_server.c" 8 | #else 9 | #error "Unsupported MIG architecture 10 | #endif -------------------------------------------------------------------------------- /libs/ymir-core/src/ymir/ymir.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | static_assert(std::endian::native == std::endian::little, "big-endian platforms are not supported at this moment"); 7 | static_assert(CHAR_BIT == 8, "char is expected to have 8 bits"); 8 | 9 | namespace ymir { 10 | 11 | // LIB_EXPORT void func() {} 12 | 13 | } // namespace ymir 14 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/scu_dsp_data_ram_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class SCUDSPDataRAMView { 8 | public: 9 | SCUDSPDataRAMView(SharedContext &context); 10 | 11 | void Display(); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::scu::SCU &m_scu; 16 | }; 17 | 18 | } // namespace app::ui 19 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/util/thread_name.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | @file 5 | @brief Defines the `util::SetCurrentThreadName` function to rename the current thread. 6 | */ 7 | 8 | namespace util { 9 | 10 | /// @brief Changes the name of the current thread. 11 | /// @param[in] threadName the new thread name 12 | void SetCurrentThreadName(const char *threadName); 13 | 14 | } // namespace util 15 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/scu_dma_state_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class SCUDMAStateView { 8 | public: 9 | SCUDMAStateView(SharedContext &context); 10 | 11 | void Display(uint8 channel); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::scu::SCU &m_scu; 16 | }; 17 | 18 | } // namespace app::ui 19 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/scu_dsp_registers_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class SCUDSPRegistersView { 8 | public: 9 | SCUDSPRegistersView(SharedContext &context); 10 | 11 | void Display(); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::scu::SCU &m_scu; 16 | }; 17 | 18 | } // namespace app::ui 19 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/sh2_power_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class SH2PowerView { 8 | public: 9 | SH2PowerView(SharedContext &context, ymir::sh2::SH2 &sh2); 10 | 11 | void Display(); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::sh2::SH2 &m_sh2; 16 | }; 17 | 18 | } // namespace app::ui 19 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/vdp2_cram_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class VDP2CRAMView { 8 | public: 9 | VDP2CRAMView(SharedContext &context, ymir::vdp::VDP &vdp); 10 | 11 | void Display(); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::vdp::VDP &m_vdp; 16 | }; 17 | 18 | } // namespace app::ui 19 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/cdblock_filters_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class CDBlockFiltersView { 8 | public: 9 | CDBlockFiltersView(SharedContext &context); 10 | 11 | void Display(); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::cdblock::CDBlock &m_cdblock; 16 | }; 17 | 18 | } // namespace app::ui 19 | -------------------------------------------------------------------------------- /docs/dev-notes/finicky-games/smpc-sh2-direct-mode.txt: -------------------------------------------------------------------------------- 1 | =============================================== 2 | Games that read peripherals in SH-2 direct mode 3 | =============================================== 4 | 5 | Golden Axe - The Duel 6 | Also expects the fixed bits in the first data of the Control Pad and 3D Control Pad readings to be exactly 1 0 0. 7 | Failing to do so causes the game to boot back to the BIOS screen. 8 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/cdblock/cdblock_buffer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace ymir::cdblock { 10 | 11 | struct Buffer { 12 | std::array data; 13 | uint16 size; 14 | uint32 frameAddress; 15 | media::Subheader subheader; 16 | }; 17 | 18 | } // namespace ymir::cdblock 19 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/media/binary_reader/binary_reader_impl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // This header file includes all implementations of IBinaryReader for ease of use. 4 | 5 | #include "binary_reader_composite.hpp" 6 | #include "binary_reader_file.hpp" 7 | #include "binary_reader_mem.hpp" 8 | #include "binary_reader_mmap.hpp" 9 | #include "binary_reader_subview.hpp" 10 | #include "binary_reader_zero.hpp" 11 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/scsp_kyonex_trace_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class SCSPKeyOnExecuteTraceView { 8 | public: 9 | SCSPKeyOnExecuteTraceView(SharedContext &context); 10 | 11 | void Display(); 12 | 13 | private: 14 | SharedContext &m_context; 15 | SCSPTracer &m_tracer; 16 | }; 17 | 18 | } // namespace app::ui 19 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/scu_dma_registers_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class SCUDMARegistersView { 8 | public: 9 | SCUDMARegistersView(SharedContext &context); 10 | 11 | void Display(uint8 channel); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::scu::SCU &m_scu; 16 | }; 17 | 18 | } // namespace app::ui 19 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/scu_dsp_dma_registers_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class SCUDSPDMARegistersView { 8 | public: 9 | SCUDSPDMARegistersView(SharedContext &context); 10 | 11 | void Display(); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::scu::SCU &m_scu; 16 | }; 17 | 18 | } // namespace app::ui 19 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/sh2_wdt_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class SH2WatchdogTimerView { 8 | public: 9 | SH2WatchdogTimerView(SharedContext &context, ymir::sh2::SH2 &sh2); 10 | 11 | void Display(); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::sh2::SH2 &m_sh2; 16 | }; 17 | 18 | } // namespace app::ui 19 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/scu/scu_callbacks.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | @file 5 | @brief SCU callbacks. 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | 12 | namespace ymir::scu { 13 | 14 | /// @brief Invoked whenever a byte is written to the debug port. 15 | using CBDebugPortWrite = util::OptionalCallback; 16 | 17 | } // namespace ymir::scu 18 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/vdp1_registers_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class VDP1RegistersView { 8 | public: 9 | VDP1RegistersView(SharedContext &context, ymir::vdp::VDP &vdp); 10 | 11 | void Display(); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::vdp::VDP &m_vdp; 16 | }; 17 | 18 | } // namespace app::ui 19 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/vdp2_vram_delay_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class VDP2VRAMDelayView { 8 | public: 9 | VDP2VRAMDelayView(SharedContext &context, ymir::vdp::VDP &vdp); 10 | 11 | void Display(); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::vdp::VDP &m_vdp; 16 | }; 17 | 18 | } // namespace app::ui 19 | -------------------------------------------------------------------------------- /docs/dev-notes/finicky-games/scsp-unaligned-16-bit-samples.txt: -------------------------------------------------------------------------------- 1 | ===================================================== 2 | Games that play 16-bit samples at unaligned addresses 3 | ===================================================== 4 | 5 | These games play 16-bit samples with an unaligned address (odd addresses) 6 | 7 | System menu (US/EU version) - tray open/no disc chime 8 | Radiant Silvergun - one or two instruments on most songs 9 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/message.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace app { 7 | 8 | inline constexpr auto kMessageDisplayDuration = std::chrono::seconds{3}; 9 | inline constexpr auto kMessageFadeOutDuration = std::chrono::seconds{1}; 10 | 11 | struct Message { 12 | std::string message; 13 | std::chrono::steady_clock::time_point timestamp; 14 | }; 15 | 16 | } // namespace app 17 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/sh2_cache_entries_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class SH2CacheEntriesView { 8 | public: 9 | SH2CacheEntriesView(SharedContext &context, ymir::sh2::SH2 &sh2); 10 | 11 | void Display(); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::sh2::SH2 &m_sh2; 16 | }; 17 | 18 | } // namespace app::ui 19 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/sh2_debug_toolbar_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class SH2DebugToolbarView { 8 | public: 9 | SH2DebugToolbarView(SharedContext &context, ymir::sh2::SH2 &sh2); 10 | 11 | void Display(); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::sh2::SH2 &m_sh2; 16 | }; 17 | 18 | } // namespace app::ui 19 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/sh2_frt_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class SH2FreeRunningTimerView { 8 | public: 9 | SH2FreeRunningTimerView(SharedContext &context, ymir::sh2::SH2 &sh2); 10 | 11 | void Display(); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::sh2::SH2 &m_sh2; 16 | }; 17 | 18 | } // namespace app::ui 19 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/vdp2_layer_params_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class VDP2LayerParamsView { 8 | public: 9 | VDP2LayerParamsView(SharedContext &context, ymir::vdp::VDP &vdp); 10 | 11 | void Display(); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::vdp::VDP &m_vdp; 16 | }; 17 | 18 | } // namespace app::ui 19 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/state/state_m68k.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace ymir::state { 8 | 9 | struct M68KState { 10 | alignas(16) std::array DA; 11 | uint32 SP_swap; 12 | uint32 PC; 13 | uint16 SR; 14 | 15 | std::array prefetchQueue; 16 | uint8 extIntrLevel; 17 | }; 18 | 19 | } // namespace ymir::state 20 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/sh2_cache_register_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class SH2CacheRegisterView { 8 | public: 9 | SH2CacheRegisterView(SharedContext &context, ymir::sh2::SH2 &sh2); 10 | 11 | void Display(); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::sh2::SH2 &m_sh2; 16 | }; 17 | 18 | } // namespace app::ui 19 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/vdp2_debug_overlay_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class VDP2DebugOverlayView { 8 | public: 9 | VDP2DebugOverlayView(SharedContext &context, ymir::vdp::VDP &vdp); 10 | 11 | void Display(); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::vdp::VDP &m_vdp; 16 | }; 17 | 18 | } // namespace app::ui 19 | -------------------------------------------------------------------------------- /docs/dev-notes/finicky-games/vdp1-command-timings.txt: -------------------------------------------------------------------------------- 1 | ========================================================= 2 | Games that are sensitive to VDP1 command processing times 3 | ========================================================= 4 | 5 | Virtua Racing 6 | Intro video freezes if VDP1 commands are processed too quickly 7 | 8 | Dragon Ball Z - Shinbutouden 9 | Freezes after SEGA logo if VDP1 commands are processed too quickly 10 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/sh2/sh2_internal_callbacks.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | @file 5 | @brief Internal callback definitions used by the SH2. 6 | */ 7 | 8 | #include 9 | 10 | namespace ymir::sh2 { 11 | 12 | /// @brief Invoked when the SH2 acknowledges an external interrupt signal. 13 | using CBAcknowledgeExternalInterrupt = util::RequiredCallback; 14 | 15 | } // namespace ymir::sh2 16 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/scu_dma_trace_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class SCUDMATraceView { 10 | public: 11 | SCUDMATraceView(SharedContext &context); 12 | 13 | void Display(); 14 | 15 | private: 16 | SharedContext &m_context; 17 | SCUTracer &m_tracer; 18 | }; 19 | 20 | } // namespace app::ui 21 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/vdp2_layer_visibility_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class VDP2LayerVisibilityView { 8 | public: 9 | VDP2LayerVisibilityView(SharedContext &context, ymir::vdp::VDP &vdp); 10 | 11 | void Display(); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::vdp::VDP &m_vdp; 16 | }; 17 | 18 | } // namespace app::ui 19 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/settings/tweaks_settings_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "settings_view_base.hpp" 4 | 5 | namespace app::ui { 6 | 7 | class TweaksSettingsView : public SettingsViewBase { 8 | public: 9 | TweaksSettingsView(SharedContext &context); 10 | 11 | void Display(); 12 | 13 | private: 14 | void DisplayEnhancements(); 15 | void DisplayAccuracyOptions(); 16 | }; 17 | 18 | } // namespace app::ui 19 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/scu_dsp_dma_trace_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class SCUDSPDMATraceView { 10 | public: 11 | SCUDSPDMATraceView(SharedContext &context); 12 | 13 | void Display(); 14 | 15 | private: 16 | SharedContext &m_context; 17 | SCUTracer &m_tracer; 18 | }; 19 | 20 | } // namespace app::ui 21 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/sh2_divu_registers_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class SH2DivisionUnitRegistersView { 8 | public: 9 | SH2DivisionUnitRegistersView(SharedContext &context, ymir::sh2::SH2 &sh2); 10 | 11 | void Display(); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::sh2::SH2 &m_sh2; 16 | }; 17 | 18 | } // namespace app::ui 19 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/sh2_dmac_registers_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class SH2DMAControllerRegistersView { 8 | public: 9 | SH2DMAControllerRegistersView(SharedContext &context, ymir::sh2::SH2 &sh2); 10 | 11 | void Display(); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::sh2::SH2 &m_sh2; 16 | }; 17 | 18 | } // namespace app::ui 19 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/core/types.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | @file 5 | @brief Core type definitions. 6 | 7 | Defines aliases for all fixed-width integer types. 8 | */ 9 | 10 | #include 11 | 12 | using uint8 = uint8_t; 13 | using uint16 = uint16_t; 14 | using uint32 = uint32_t; 15 | using uint64 = uint64_t; 16 | 17 | using sint8 = int8_t; 18 | using sint16 = int16_t; 19 | using sint32 = int32_t; 20 | using sint64 = int64_t; 21 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/sh2_registers_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class SH2RegistersView { 8 | public: 9 | SH2RegistersView(SharedContext &context, ymir::sh2::SH2 &sh2); 10 | 11 | void Display(); 12 | 13 | float GetViewWidth(); 14 | 15 | private: 16 | SharedContext &m_context; 17 | ymir::sh2::SH2 &m_sh2; 18 | }; 19 | 20 | } // namespace app::ui 21 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/cdblock/ygr_internal_callbacks.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | @file 5 | @brief Internal callback definitions used by the YGR. 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | 12 | namespace ymir::cdblock { 13 | 14 | /// @brief Invoked when a sector transfer is finished 15 | using CBSectorTransferDone = util::RequiredCallback; 16 | 17 | } // namespace ymir::cdblock 18 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/cdblock_ygr_cmd_trace_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class YGRCommandTraceView { 10 | public: 11 | YGRCommandTraceView(SharedContext &context); 12 | 13 | void Display(); 14 | 15 | private: 16 | SharedContext &m_context; 17 | YGRTracer &m_tracer; 18 | }; 19 | 20 | } // namespace app::ui 21 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/scu_interrupt_trace_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class SCUInterruptTraceView { 10 | public: 11 | SCUInterruptTraceView(SharedContext &context); 12 | 13 | void Display(); 14 | 15 | private: 16 | SharedContext &m_context; 17 | SCUTracer &m_tracer; 18 | }; 19 | 20 | } // namespace app::ui 21 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/scsp/scsp_internal_callbacks.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | @file 5 | @brief Internal callback definitions used by the SCSP. 6 | */ 7 | 8 | #include 9 | 10 | namespace ymir::scsp { 11 | 12 | /// @brief Invoked when the SCSP needs to raise the SCU sound request interrupt signal. 13 | using CBTriggerSoundRequestInterrupt = util::RequiredCallback; 14 | 15 | } // namespace ymir::scsp 16 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/smpc/peripheral/peripheral_callbacks.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | @file 5 | @brief Peripheral callbacks. 6 | */ 7 | 8 | #include "peripheral_report.hpp" 9 | 10 | #include 11 | 12 | namespace ymir::peripheral { 13 | 14 | // Invoked when a peripheral requests a report. 15 | using CBPeripheralReport = util::OptionalCallback; 16 | 17 | } // namespace ymir::peripheral 18 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/debug/scsp_tracer.cpp: -------------------------------------------------------------------------------- 1 | #include "scsp_tracer.hpp" 2 | 3 | namespace app { 4 | 5 | void SCSPTracer::SlotSample(uint32 index, sint16 output) { 6 | slotOutputs[index].Write(output); 7 | if (index == 31) { 8 | ++m_sampleCounter; 9 | } 10 | } 11 | 12 | void SCSPTracer::KeyOnExecute(uint32 slotsMask) { 13 | kyonexTrace.Write({.sampleCounter = m_sampleCounter, .slotsMask = slotsMask}); 14 | } 15 | 16 | } // namespace app 17 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/cdblock_cmd_trace_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class CDBlockCommandTraceView { 10 | public: 11 | CDBlockCommandTraceView(SharedContext &context); 12 | 13 | void Display(); 14 | 15 | private: 16 | SharedContext &m_context; 17 | CDBlockTracer &m_tracer; 18 | }; 19 | 20 | } // namespace app::ui 21 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/scsp_slots_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class SCSPSlotsView { 8 | public: 9 | SCSPSlotsView(SharedContext &context); 10 | 11 | void Display(); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::scsp::SCSP &m_scsp; 16 | SCSPTracer &m_tracer; 17 | 18 | bool m_colorSlotsBySA = true; 19 | }; 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/sh2_breakpoints_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class SH2BreakpointsView { 8 | public: 9 | SH2BreakpointsView(SharedContext &context, ymir::sh2::SH2 &sh2); 10 | 11 | void Display(); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::sh2::SH2 &m_sh2; 16 | 17 | uint32 m_address = 0x00000000; 18 | }; 19 | 20 | } // namespace app::ui 21 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/sys/system_internal_callbacks.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | @file 5 | @brief Internal callback definitions used by the system. 6 | */ 7 | 8 | #include "clocks.hpp" 9 | 10 | #include 11 | 12 | namespace ymir::sys { 13 | 14 | /// @brief Invoked when the system clock speed changes. 15 | using CBClockSpeedChange = util::RequiredCallback; 16 | 17 | } // namespace ymir::sys 18 | -------------------------------------------------------------------------------- /libs/ymir-core/src/ymir/db/rom_cart_db.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | namespace ymir::db { 6 | 7 | const ROMCartInfo *GetROMCartInfo(XXH128Hash hash) { 8 | if (hash == kKOF95ROMInfo.hash) { 9 | return &kKOF95ROMInfo; 10 | } else if (hash == kUltramanROMInfo.hash) { 11 | return &kUltramanROMInfo; 12 | } else { 13 | return nullptr; 14 | } 15 | } 16 | 17 | } // namespace ymir::db 18 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/cdblock_drive_state_trace_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class CDDriveStateTraceView { 10 | public: 11 | CDDriveStateTraceView(SharedContext &context); 12 | 13 | void Display(); 14 | 15 | private: 16 | SharedContext &m_context; 17 | CDDriveTracer &m_tracer; 18 | }; 19 | 20 | } // namespace app::ui 21 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/scsp_slots_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "scsp_window_base.hpp" 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class SCSPSlotsWindow : public SCSPWindowBase { 10 | public: 11 | SCSPSlotsWindow(SharedContext &context); 12 | 13 | protected: 14 | void DrawContents() override; 15 | 16 | private: 17 | SCSPSlotsView m_slotsView; 18 | }; 19 | 20 | } // namespace app::ui -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/sh2_window_base.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class SH2WindowBase : public WindowBase { 10 | public: 11 | SH2WindowBase(SharedContext &context, bool master); 12 | virtual ~SH2WindowBase() = default; 13 | 14 | protected: 15 | ymir::sh2::SH2 &m_sh2; 16 | SH2Tracer &m_tracer; 17 | }; 18 | 19 | } // namespace app::ui -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/vdp_window_base.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // #include 6 | 7 | namespace app::ui { 8 | 9 | class VDPWindowBase : public WindowBase { 10 | public: 11 | VDPWindowBase(SharedContext &context); 12 | virtual ~VDPWindowBase() = default; 13 | 14 | protected: 15 | ymir::vdp::VDP &m_vdp; 16 | // VDPTracer &m_tracer; 17 | }; 18 | 19 | } // namespace app::ui 20 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/sh2_interrupt_trace_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class SH2InterruptTraceView { 10 | public: 11 | SH2InterruptTraceView(SharedContext &context, SH2Tracer &tracer); 12 | 13 | void Display(); 14 | 15 | private: 16 | SharedContext &m_context; 17 | SH2Tracer &m_tracer; 18 | }; 19 | 20 | } // namespace app::ui 21 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/scsp_window_base.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class SCSPWindowBase : public WindowBase { 10 | public: 11 | SCSPWindowBase(SharedContext &context); 12 | virtual ~SCSPWindowBase() = default; 13 | 14 | protected: 15 | ymir::scsp::SCSP &m_scsp; 16 | SCSPTracer &m_tracer; 17 | }; 18 | 19 | } // namespace app::ui 20 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/sh2_power_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sh2_window_base.hpp" 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class SH2PowerWindow : public SH2WindowBase { 10 | public: 11 | SH2PowerWindow(SharedContext &context, bool master); 12 | 13 | protected: 14 | void DrawContents() override; 15 | 16 | private: 17 | SH2PowerView m_powerView; 18 | }; 19 | 20 | } // namespace app::ui -------------------------------------------------------------------------------- /vcpkg-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "default-registry": { 3 | "kind": "git", 4 | "baseline": "a62ce77d56ee07513b4b67de1ec2daeaebfae51a", 5 | "repository": "https://github.com/microsoft/vcpkg" 6 | }, 7 | "registries": [ 8 | { 9 | "kind": "artifact", 10 | "location": "https://github.com/microsoft/vcpkg-ce-catalog/archive/refs/heads/main.zip", 11 | "name": "microsoft" 12 | } 13 | ], 14 | "overlay-triplets": [ "./vcpkg-triplets" ] 15 | } 16 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/util/mig/macos_mig.h: -------------------------------------------------------------------------------- 1 | // Provides the mig server implementation for the current architecture 2 | // These files are generated from cmake using the 'mig' command 3 | 4 | #if defined(__x86_64__) 5 | #define mig_external extern "C" 6 | #include "x64/mach_exc_server.h" 7 | #elif defined(__aarch64__) || defined(__arm64__) 8 | #define mig_external extern "C" 9 | #include "a64/mach_exc_server.h" 10 | #else 11 | #error "Unsupported mig architecture 12 | #endif -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/scu_interrupts_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class SCUInterruptsView { 8 | public: 9 | SCUInterruptsView(SharedContext &context); 10 | 11 | void Display(); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::scu::SCU &m_scu; 16 | 17 | void DisplayInternalInterrupts(); 18 | void DisplayExternalInterrupts(); 19 | }; 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/sh2_divu_statistics_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class SH2DivisionUnitStatisticsView { 10 | public: 11 | SH2DivisionUnitStatisticsView(SharedContext &context, SH2Tracer &tracer); 12 | 13 | void Display(); 14 | 15 | private: 16 | SharedContext &m_context; 17 | SH2Tracer &m_tracer; 18 | }; 19 | 20 | } // namespace app::ui 21 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/vdp1_registers_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vdp_window_base.hpp" 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class VDP1RegistersWindow : public VDPWindowBase { 10 | public: 11 | VDP1RegistersWindow(SharedContext &context); 12 | 13 | protected: 14 | void DrawContents() override; 15 | 16 | private: 17 | VDP1RegistersView m_regsView; 18 | }; 19 | 20 | } // namespace app::ui 21 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/settings/settings_view_base.cpp: -------------------------------------------------------------------------------- 1 | #include "settings_view_base.hpp" 2 | 3 | namespace app::ui { 4 | 5 | SettingsViewBase::SettingsViewBase(SharedContext &context) 6 | : m_context(context) {} 7 | 8 | void SettingsViewBase::MakeDirty() { 9 | m_context.settings.MakeDirty(); 10 | } 11 | 12 | bool SettingsViewBase::MakeDirty(bool value) { 13 | if (value) { 14 | MakeDirty(); 15 | } 16 | return value; 17 | } 18 | 19 | } // namespace app::ui 20 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/scsp_slots_window.cpp: -------------------------------------------------------------------------------- 1 | #include "scsp_slots_window.hpp" 2 | 3 | namespace app::ui { 4 | 5 | SCSPSlotsWindow::SCSPSlotsWindow(SharedContext &context) 6 | : SCSPWindowBase(context) 7 | , m_slotsView(context) { 8 | 9 | m_windowConfig.name = "SCSP slots"; 10 | m_windowConfig.flags = ImGuiWindowFlags_AlwaysAutoResize; 11 | } 12 | 13 | void SCSPSlotsWindow::DrawContents() { 14 | m_slotsView.Display(); 15 | } 16 | 17 | } // namespace app::ui 18 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/media/cdrom_crc.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | @file 5 | @brief CD-ROM error detection code calculation routines. 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | 12 | namespace ymir::media { 13 | 14 | /// @brief Calculates the CRC for the given sector. 15 | /// @param[in] sector the sector to checksum 16 | /// @return the CD-ROM ECC for the sector 17 | uint32 CalcCRC(std::span sector); 18 | 19 | } // namespace ymir::media 20 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/sh2_interrupts_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class SH2InterruptsView { 8 | public: 9 | SH2InterruptsView(SharedContext &context, ymir::sh2::SH2 &sh2); 10 | 11 | void Display(); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::sh2::SH2 &m_sh2; 16 | 17 | uint8 m_extIntrVector = 0x0; 18 | uint8 m_extIntrLevel = 0x0; 19 | }; 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/cdblock_window_base.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class CDBlockWindowBase : public WindowBase { 10 | public: 11 | CDBlockWindowBase(SharedContext &context); 12 | virtual ~CDBlockWindowBase() = default; 13 | 14 | protected: 15 | ymir::cdblock::CDBlock &m_cdblock; 16 | CDBlockTracer &m_tracer; 17 | }; 18 | 19 | } // namespace app::ui 20 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/vdp2_vram_delay_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vdp_window_base.hpp" 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class VDP2VRAMDelayWindow : public VDPWindowBase { 10 | public: 11 | VDP2VRAMDelayWindow(SharedContext &context); 12 | 13 | protected: 14 | void DrawContents() override; 15 | 16 | private: 17 | VDP2VRAMDelayView m_nbgDelayView; 18 | }; 19 | 20 | } // namespace app::ui 21 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/about_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class AboutWindow : public WindowBase { 8 | public: 9 | AboutWindow(SharedContext &context); 10 | 11 | protected: 12 | void PrepareWindow() override; 13 | void DrawContents() override; 14 | 15 | private: 16 | void DrawAboutTab(); 17 | void DrawDependenciesTab(); 18 | void DrawAcknowledgementsTab(); 19 | }; 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/sh2_interrupts_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sh2_window_base.hpp" 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class SH2InterruptsWindow : public SH2WindowBase { 10 | public: 11 | SH2InterruptsWindow(SharedContext &context, bool master); 12 | 13 | protected: 14 | void DrawContents() override; 15 | 16 | private: 17 | SH2InterruptsView m_intrView; 18 | }; 19 | 20 | } // namespace app::ui 21 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/vdp2_cram_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vdp_window_base.hpp" 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class VDP2CRAMWindow : public VDPWindowBase { 10 | public: 11 | VDP2CRAMWindow(SharedContext &context); 12 | 13 | protected: 14 | void PrepareWindow() override; 15 | void DrawContents() override; 16 | 17 | private: 18 | VDP2CRAMView m_cramView; 19 | }; 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/input/input_bind.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "input_action.hpp" 4 | #include "input_events.hpp" 5 | 6 | namespace app::input { 7 | 8 | // Number of simultaneous input elements allowed per bind 9 | inline constexpr size_t kNumBindsPerInput = 5; 10 | 11 | struct InputBind { 12 | InputBind(Action action) 13 | : action(action) {} 14 | 15 | const Action action; 16 | std::array elements; 17 | }; 18 | 19 | } // namespace app::input 20 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/vdp2_layer_params_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vdp_window_base.hpp" 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class VDP2LayerParamsWindow : public VDPWindowBase { 10 | public: 11 | VDP2LayerParamsWindow(SharedContext &context); 12 | 13 | protected: 14 | void DrawContents() override; 15 | 16 | private: 17 | VDP2LayerParamsView m_layerParamsView; 18 | }; 19 | 20 | } // namespace app::ui 21 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/util/os_features.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace util::os { 8 | 9 | // Changes window decorations depending on the operating system: 10 | // - Windows 11: disables rounded corners 11 | void ConfigureWindowDecorations(SDL_Window *window); 12 | 13 | // Changes the hidden attribute of a file. 14 | // Only applies to Windows. 15 | void SetFileHidden(std::filesystem::path path, bool hidden); 16 | 17 | } // namespace util::os 18 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/sh2_divu_trace_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class SH2DivisionUnitTraceView { 10 | public: 11 | SH2DivisionUnitTraceView(SharedContext &context, SH2Tracer &tracer); 12 | 13 | void Display(); 14 | 15 | private: 16 | SharedContext &m_context; 17 | SH2Tracer &m_tracer; 18 | 19 | bool m_showHex = false; 20 | }; 21 | 22 | } // namespace app::ui 23 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/scsp_output_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "scsp_window_base.hpp" 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class SCSPOutputWindow : public SCSPWindowBase { 10 | public: 11 | SCSPOutputWindow(SharedContext &context); 12 | 13 | protected: 14 | void PrepareWindow() override; 15 | void DrawContents() override; 16 | 17 | private: 18 | SCSPOutputView m_outputView; 19 | }; 20 | 21 | } // namespace app::ui -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/hw_defs.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | @file 5 | @brief Common hardware definitions. 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | 12 | namespace ymir { 13 | 14 | /// @brief Specifies valid types for memory accesses: `uint8`, `uint16` and `uint32`. 15 | /// @tparam T the type to check 16 | template 17 | concept mem_primitive = std::same_as || std::same_as || std::same_as; 18 | 19 | } // namespace ymir 20 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/update_onboarding_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class UpdateOnboardingWindow : public WindowBase { 8 | public: 9 | UpdateOnboardingWindow(SharedContext &context); 10 | 11 | protected: 12 | void PrepareWindow() override; 13 | void DrawContents() override; 14 | 15 | private: 16 | bool m_checkForUpdates = false; 17 | bool m_includeNightlyBuilds = false; 18 | }; 19 | 20 | } // namespace app::ui 21 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/vdp1_registers_window.cpp: -------------------------------------------------------------------------------- 1 | #include "vdp1_registers_window.hpp" 2 | 3 | namespace app::ui { 4 | 5 | VDP1RegistersWindow::VDP1RegistersWindow(SharedContext &context) 6 | : VDPWindowBase(context) 7 | , m_regsView(context, m_vdp) { 8 | 9 | m_windowConfig.name = "VDP1 registers"; 10 | m_windowConfig.flags = ImGuiWindowFlags_AlwaysAutoResize; 11 | } 12 | 13 | void VDP1RegistersWindow::DrawContents() { 14 | m_regsView.Display(); 15 | } 16 | 17 | } // namespace app::ui 18 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/debug_output_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class DebugOutputWindow : public WindowBase { 10 | public: 11 | DebugOutputWindow(SharedContext &context); 12 | 13 | protected: 14 | void PrepareWindow() override; 15 | void DrawContents() override; 16 | 17 | private: 18 | DebugOutputView m_debugOutputView; 19 | }; 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/vdp2_layer_visibility_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vdp_window_base.hpp" 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class VDP2LayerVisibilityWindow : public VDPWindowBase { 10 | public: 11 | VDP2LayerVisibilityWindow(SharedContext &context); 12 | 13 | protected: 14 | void DrawContents() override; 15 | 16 | private: 17 | VDP2LayerVisibilityView m_layerVisibilityView; 18 | }; 19 | 20 | } // namespace app::ui -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/vdp2_vram_delay_window.cpp: -------------------------------------------------------------------------------- 1 | #include "vdp2_vram_delay_window.hpp" 2 | 3 | namespace app::ui { 4 | 5 | VDP2VRAMDelayWindow::VDP2VRAMDelayWindow(SharedContext &context) 6 | : VDPWindowBase(context) 7 | , m_nbgDelayView(context, m_vdp) { 8 | 9 | m_windowConfig.name = "VDP2 VRAM access delay"; 10 | m_windowConfig.flags = ImGuiWindowFlags_AlwaysAutoResize; 11 | } 12 | 13 | void VDP2VRAMDelayWindow::DrawContents() { 14 | m_nbgDelayView.Display(); 15 | } 16 | 17 | } // namespace app::ui 18 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/scsp/scsp_callbacks.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace ymir::scsp { 8 | 9 | // Sample output callback, invoked every sample 10 | using CBOutputSample = util::OptionalCallback; 11 | 12 | // MIDI message output callback, invoked when a complete midi message is ready to send 13 | using CBSendMidiOutputMessage = util::OptionalCallback msg)>; 14 | 15 | } // namespace ymir::scsp 16 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/sh2_dmac_channel_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class SH2DMAControllerChannelView { 10 | public: 11 | SH2DMAControllerChannelView(SharedContext &context, ymir::sh2::DMAChannel &channel, int index); 12 | 13 | void Display(); 14 | 15 | private: 16 | SharedContext &m_context; 17 | ymir::sh2::DMAChannel &m_channel; 18 | const int m_index; 19 | }; 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/cdblock_filters_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cdblock_window_base.hpp" 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class CDBlockFiltersWindow : public CDBlockWindowBase { 10 | public: 11 | CDBlockFiltersWindow(SharedContext &context); 12 | 13 | protected: 14 | void PrepareWindow() override; 15 | void DrawContents() override; 16 | 17 | private: 18 | CDBlockFiltersView m_filtersView; 19 | }; 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/state/state_scheduler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace ymir::state { 8 | 9 | struct SchedulerState { 10 | struct EventState { 11 | uint64 target; 12 | uint64 countNumerator; 13 | uint64 countDenominator; 14 | core::UserEventID id; 15 | }; 16 | 17 | uint64 currCount; 18 | alignas(16) std::array events; 19 | }; 20 | 21 | } // namespace ymir::state 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/scu_dma_trace_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace app::ui { 10 | 11 | class SCUDMATraceWindow : public WindowBase { 12 | public: 13 | SCUDMATraceWindow(SharedContext &context); 14 | 15 | protected: 16 | void PrepareWindow() override; 17 | void DrawContents() override; 18 | 19 | private: 20 | SCUDMATraceView m_dmaTraceView; 21 | }; 22 | 23 | } // namespace app::ui 24 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/sh2_breakpoints_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sh2_window_base.hpp" 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class SH2BreakpointsWindow : public SH2WindowBase { 10 | public: 11 | SH2BreakpointsWindow(SharedContext &context, bool master); 12 | 13 | protected: 14 | void PrepareWindow() override; 15 | void DrawContents() override; 16 | 17 | private: 18 | SH2BreakpointsView m_breakpointsView; 19 | }; 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/sh2_watchpoints_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sh2_window_base.hpp" 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class SH2WatchpointsWindow : public SH2WindowBase { 10 | public: 11 | SH2WatchpointsWindow(SharedContext &context, bool master); 12 | 13 | protected: 14 | void PrepareWindow() override; 15 | void DrawContents() override; 16 | 17 | private: 18 | SH2WatchpointsView m_watchpointsView; 19 | }; 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/vdp2_debug_overlay_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vdp_window_base.hpp" 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class VDP2DebugOverlayWindow : public VDPWindowBase { 10 | public: 11 | VDP2DebugOverlayWindow(SharedContext &context); 12 | 13 | protected: 14 | void PrepareWindow() override; 15 | void DrawContents() override; 16 | 17 | private: 18 | VDP2DebugOverlayView m_debugOverlayView; 19 | }; 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/vdp2_layer_params_window.cpp: -------------------------------------------------------------------------------- 1 | #include "vdp2_layer_params_window.hpp" 2 | 3 | namespace app::ui { 4 | 5 | VDP2LayerParamsWindow::VDP2LayerParamsWindow(SharedContext &context) 6 | : VDPWindowBase(context) 7 | , m_layerParamsView(context, m_vdp) { 8 | 9 | m_windowConfig.name = "VDP2 layer parameters"; 10 | m_windowConfig.flags = ImGuiWindowFlags_AlwaysAutoResize; 11 | } 12 | 13 | void VDP2LayerParamsWindow::DrawContents() { 14 | m_layerParamsView.Display(); 15 | } 16 | 17 | } // namespace app::ui 18 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/scu_interrupt_trace_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class SCUInterruptTraceWindow : public WindowBase { 10 | public: 11 | SCUInterruptTraceWindow(SharedContext &context); 12 | 13 | protected: 14 | void PrepareWindow() override; 15 | void DrawContents() override; 16 | 17 | private: 18 | SCUInterruptTraceView m_intrTraceView; 19 | }; 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/cdblock_ygr_cmd_trace_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cdblock_window_base.hpp" 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class YGRCommandTraceWindow : public CDBlockWindowBase { 10 | public: 11 | YGRCommandTraceWindow(SharedContext &context); 12 | 13 | protected: 14 | void PrepareWindow() override; 15 | void DrawContents() override; 16 | 17 | private: 18 | YGRCommandTraceView m_cmdTraceView; 19 | }; 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/scsp_kyonex_trace_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "scsp_window_base.hpp" 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class SCSPKeyOnExecuteTraceWindow : public SCSPWindowBase { 10 | public: 11 | SCSPKeyOnExecuteTraceWindow(SharedContext &context); 12 | 13 | protected: 14 | void PrepareWindow() override; 15 | void DrawContents() override; 16 | 17 | private: 18 | SCSPKeyOnExecuteTraceView m_kyonexTraceView; 19 | }; 20 | 21 | } // namespace app::ui -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/sh2_exception_vectors_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sh2_window_base.hpp" 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class SH2ExceptionVectorsWindow : public SH2WindowBase { 10 | public: 11 | SH2ExceptionVectorsWindow(SharedContext &context, bool master); 12 | 13 | protected: 14 | void PrepareWindow(); 15 | void DrawContents(); 16 | 17 | private: 18 | SH2ExceptionVectorsView m_excptVecView; 19 | }; 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/cdblock_cmd_trace_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cdblock_window_base.hpp" 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class CDBlockCommandTraceWindow : public CDBlockWindowBase { 10 | public: 11 | CDBlockCommandTraceWindow(SharedContext &context); 12 | 13 | protected: 14 | void PrepareWindow() override; 15 | void DrawContents() override; 16 | 17 | private: 18 | CDBlockCommandTraceView m_cmdTraceView; 19 | }; 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/sh2_interrupt_trace_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sh2_window_base.hpp" 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class SH2InterruptTraceWindow : public SH2WindowBase { 10 | public: 11 | SH2InterruptTraceWindow(SharedContext &context, bool master); 12 | 13 | protected: 14 | void PrepareWindow() override; 15 | void DrawContents() override; 16 | 17 | private: 18 | SH2InterruptTraceView m_intrTraceView; 19 | }; 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/cdblock_drive_state_trace_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cdblock_window_base.hpp" 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class CDDriveStateTraceWindow : public CDBlockWindowBase { 10 | public: 11 | CDDriveStateTraceWindow(SharedContext &context); 12 | 13 | protected: 14 | void PrepareWindow() override; 15 | void DrawContents() override; 16 | 17 | private: 18 | CDDriveStateTraceView m_stateTraceView; 19 | }; 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/scu_dma_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | namespace app::ui { 9 | 10 | class SCUDMAWindow : public WindowBase { 11 | public: 12 | SCUDMAWindow(SharedContext &context); 13 | 14 | protected: 15 | void DrawContents() override; 16 | 17 | private: 18 | SCUDMARegistersView m_dmaRegsView; 19 | SCUDMAStateView m_dmaStateView; 20 | }; 21 | 22 | } // namespace app::ui 23 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/sh2_timers_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sh2_window_base.hpp" 4 | 5 | #include 6 | #include 7 | 8 | namespace app::ui { 9 | 10 | class SH2TimersWindow : public SH2WindowBase { 11 | public: 12 | SH2TimersWindow(SharedContext &context, bool master); 13 | 14 | protected: 15 | void DrawContents() override; 16 | 17 | private: 18 | SH2FreeRunningTimerView m_frtView; 19 | SH2WatchdogTimerView m_wdtView; 20 | }; 21 | 22 | } // namespace app::ui -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/scsp_output_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace app::ui { 9 | 10 | class SCSPOutputView { 11 | public: 12 | SCSPOutputView(SharedContext &context); 13 | 14 | void Display(ImVec2 size = {0, 0}); 15 | 16 | private: 17 | SharedContext &m_context; 18 | 19 | std::array m_audioBuffer; 20 | std::array m_waveform; 21 | }; 22 | 23 | } // namespace app::ui 24 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/vdp2_layer_visibility_window.cpp: -------------------------------------------------------------------------------- 1 | #include "vdp2_layer_visibility_window.hpp" 2 | 3 | namespace app::ui { 4 | 5 | VDP2LayerVisibilityWindow::VDP2LayerVisibilityWindow(SharedContext &context) 6 | : VDPWindowBase(context) 7 | , m_layerVisibilityView(context, m_vdp) { 8 | 9 | m_windowConfig.name = "VDP2 layer visibility"; 10 | m_windowConfig.flags = ImGuiWindowFlags_AlwaysAutoResize; 11 | } 12 | 13 | void VDP2LayerVisibilityWindow::DrawContents() { 14 | m_layerVisibilityView.Display(); 15 | } 16 | 17 | } // namespace app::ui 18 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/sh2_power_window.cpp: -------------------------------------------------------------------------------- 1 | #include "sh2_power_window.hpp" 2 | 3 | using namespace ymir; 4 | 5 | namespace app::ui { 6 | 7 | SH2PowerWindow::SH2PowerWindow(SharedContext &context, bool master) 8 | : SH2WindowBase(context, master) 9 | , m_powerView(context, m_sh2) { 10 | 11 | m_windowConfig.name = fmt::format("{}SH2 power module", master ? 'M' : 'S'); 12 | m_windowConfig.flags = ImGuiWindowFlags_AlwaysAutoResize; 13 | } 14 | 15 | void SH2PowerWindow::DrawContents() { 16 | m_powerView.Display(); 17 | } 18 | 19 | } // namespace app::ui 20 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/sh2_dmac_trace_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class SH2DMAControllerChannelTraceView { 10 | public: 11 | SH2DMAControllerChannelTraceView(SharedContext &context, int index, SH2Tracer &tracer); 12 | 13 | void Display(); 14 | 15 | private: 16 | SharedContext &m_context; 17 | const int m_index; 18 | SH2Tracer &m_tracer; 19 | 20 | void DisplayStatistics(); 21 | void DisplayTrace(); 22 | }; 23 | 24 | } // namespace app::ui 25 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/settings/hotkeys_settings_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "settings_view_base.hpp" 4 | 5 | #include 6 | #include 7 | 8 | namespace app::ui { 9 | 10 | class HotkeysSettingsView : public SettingsViewBase { 11 | public: 12 | HotkeysSettingsView(SharedContext &context); 13 | 14 | void Display(); 15 | 16 | private: 17 | widgets::InputCaptureWidget m_inputCaptureWidget; 18 | widgets::UnboundActionsWidget m_unboundActionsWidget; 19 | }; 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/widgets/common_widgets.cpp: -------------------------------------------------------------------------------- 1 | #include "common_widgets.hpp" 2 | 3 | #include 4 | 5 | namespace app::ui::widgets { 6 | 7 | void ExplanationTooltip(const char *explanation, float scale, bool sameLine) { 8 | if (sameLine) { 9 | ImGui::SameLine(); 10 | } 11 | ImGui::TextDisabled("(?)"); 12 | if (ImGui::BeginItemTooltip()) { 13 | ImGui::PushTextWrapPos(450.0f * scale); 14 | ImGui::TextUnformatted(explanation); 15 | ImGui::PopTextWrapPos(); 16 | ImGui::EndTooltip(); 17 | } 18 | } 19 | 20 | } // namespace app::ui::widgets -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/sh2_interrupts_window.cpp: -------------------------------------------------------------------------------- 1 | #include "sh2_interrupts_window.hpp" 2 | 3 | using namespace ymir; 4 | 5 | namespace app::ui { 6 | 7 | SH2InterruptsWindow::SH2InterruptsWindow(SharedContext &context, bool master) 8 | : SH2WindowBase(context, master) 9 | , m_intrView(context, m_sh2) { 10 | 11 | m_windowConfig.name = fmt::format("{}SH2 interrupts", master ? 'M' : 'S'); 12 | m_windowConfig.flags = ImGuiWindowFlags_AlwaysAutoResize; 13 | } 14 | 15 | void SH2InterruptsWindow::DrawContents() { 16 | m_intrView.Display(); 17 | } 18 | 19 | } // namespace app::ui 20 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/sys/memory_defs.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace ymir::sys { 8 | 9 | inline constexpr size_t kIPLSize = 512_KiB; 10 | inline constexpr uint64 kIPLHashSeed = 0x94B487AF51733FBEull; 11 | 12 | inline constexpr size_t kWRAMLowSize = 1_MiB; 13 | inline constexpr size_t kWRAMHighSize = 1_MiB; 14 | 15 | inline constexpr bup::BackupMemorySize kInternalBackupRAMSize = bup::BackupMemorySize::_256Kbit; 16 | inline constexpr size_t kInternalBackupRAMSizeAmount = 32_KiB; 17 | 18 | } // namespace ymir::sys 19 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/sh2_cache_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sh2_window_base.hpp" 4 | 5 | #include 6 | #include 7 | 8 | namespace app::ui { 9 | 10 | class SH2CacheWindow : public SH2WindowBase { 11 | public: 12 | SH2CacheWindow(SharedContext &context, bool master); 13 | 14 | protected: 15 | void PrepareWindow(); 16 | void DrawContents(); 17 | 18 | private: 19 | SH2CacheRegisterView m_cacheRegView; 20 | SH2CacheEntriesView m_cacheEntriesView; 21 | }; 22 | 23 | } // namespace app::ui 24 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/sh2_dmac_trace_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sh2_window_base.hpp" 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class SH2DMAControllerTraceWindow : public SH2WindowBase { 10 | public: 11 | SH2DMAControllerTraceWindow(SharedContext &context, bool master); 12 | 13 | protected: 14 | void PrepareWindow() override; 15 | void DrawContents() override; 16 | 17 | private: 18 | SH2DMAControllerChannelTraceView m_dmac0TraceView; 19 | SH2DMAControllerChannelTraceView m_dmac1TraceView; 20 | }; 21 | 22 | } // namespace app::ui 23 | -------------------------------------------------------------------------------- /docs/dev-notes/finicky-games/vdp1-illegal-sprite-color-data.txt: -------------------------------------------------------------------------------- 1 | =============================================== 2 | Games that draw sprites with illegal color data 3 | =============================================== 4 | 5 | Sonic X-treme 6 | Draws sprites in RGB 5:5:5 mode containing "illegal" color data (specifically 0x0060, read from VDP1 VRAM at 0x10000). 7 | The manual states that 0x0000 is the only valid transparent color code and the entire range from 0x0001 to 0x7FFE is 8 | "invalid", as it is reserved for palette data. In practice, all color data values from 0x0000 to 0x7FFE are considered 9 | transparent in RGB 5:5:5 mode. 10 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/sh2_exception_vectors_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class SH2ExceptionVectorsView { 8 | public: 9 | SH2ExceptionVectorsView(SharedContext &context, ymir::sh2::SH2 &sh2); 10 | 11 | void Display(); 12 | 13 | float GetWidth() const; 14 | 15 | private: 16 | SharedContext &m_context; 17 | ymir::sh2::SH2 &m_sh2; 18 | 19 | bool m_useVBR = true; 20 | uint32 m_customAddress = 0x00000000; 21 | 22 | // Split into 2**m_columnShift columns 23 | uint32 m_columnShift = 2; 24 | }; 25 | 26 | } // namespace app::ui 27 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/backup_ram_manager_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class BackupMemoryManagerWindow : public WindowBase { 10 | public: 11 | BackupMemoryManagerWindow(SharedContext &context); 12 | 13 | protected: 14 | void PrepareWindow() final; 15 | void DrawContents() final; 16 | 17 | private: 18 | BackupMemoryView m_sysBupView; 19 | BackupMemoryView m_cartBupView; 20 | 21 | // TODO: additional windows for backup memory views from files 22 | }; 23 | 24 | } // namespace app::ui 25 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/state/state_system.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | namespace ymir::state { 10 | 11 | struct SystemState { 12 | core::config::sys::VideoStandard videoStandard; 13 | sys::ClockSpeed clockSpeed; 14 | bool slaveSH2Enabled; 15 | 16 | alignas(16) XXH128Hash iplRomHash; 17 | 18 | alignas(16) std::array WRAMLow; 19 | alignas(16) std::array WRAMHigh; 20 | }; 21 | 22 | } // namespace ymir::state 23 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/sh2_watchpoints_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class SH2WatchpointsView { 8 | public: 9 | SH2WatchpointsView(SharedContext &context, ymir::sh2::SH2 &sh2); 10 | 11 | void Display(); 12 | 13 | private: 14 | SharedContext &m_context; 15 | ymir::sh2::SH2 &m_sh2; 16 | 17 | uint32 m_address = 0x00000000; 18 | bool m_read8 = true; 19 | bool m_read16 = true; 20 | bool m_read32 = true; 21 | bool m_write8 = true; 22 | bool m_write16 = true; 23 | bool m_write32 = true; 24 | }; 25 | 26 | } // namespace app::ui 27 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/settings/analog_pad_config_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "settings_view_base.hpp" 4 | 5 | #include 6 | #include 7 | 8 | namespace app::ui { 9 | 10 | class AnalogPadConfigView : public SettingsViewBase { 11 | public: 12 | AnalogPadConfigView(SharedContext &context); 13 | 14 | void Display(Settings::Input::Port::AnalogPad &controllerSettings, uint32 portIndex); 15 | 16 | private: 17 | widgets::InputCaptureWidget m_inputCaptureWidget; 18 | widgets::UnboundActionsWidget m_unboundActionsWidget; 19 | }; 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/media/loader/loader_result.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace ymir::media { 7 | 8 | enum class MessageType { 9 | InvalidFormat, // Parser has not detected valid magic signature 10 | Error, // Problem found parsing media; must be reported to the user 11 | NotValid, // None of the parsers matched the file format 12 | Debug, // Debug messages, detailed parser logs 13 | }; 14 | 15 | // Callback function for loader messages. 16 | using CbLoaderMessage = std::function; 17 | 18 | } // namespace ymir::media 19 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/debug/cd_drive_tracer.cpp: -------------------------------------------------------------------------------- 1 | #include "cd_drive_tracer.hpp" 2 | 3 | namespace app { 4 | 5 | void CDDriveTracer::ClearStateUpdates() { 6 | stateUpdates.Clear(); 7 | m_stateUpdateCounter = 0; 8 | } 9 | 10 | void CDDriveTracer::RxCommandTxStatus(std::span command, std::span status) { 11 | if (!traceStateUpdates) { 12 | return; 13 | } 14 | 15 | auto &entry = stateUpdates.Write({.index = m_stateUpdateCounter++}); 16 | std::copy(command.begin(), command.end(), entry.command.begin()); 17 | std::copy(status.begin(), status.end(), entry.status.begin()); 18 | } 19 | 20 | } // namespace app 21 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/settings/control_pad_config_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "settings_view_base.hpp" 4 | 5 | #include 6 | #include 7 | 8 | namespace app::ui { 9 | 10 | class ControlPadConfigView : public SettingsViewBase { 11 | public: 12 | ControlPadConfigView(SharedContext &context); 13 | 14 | void Display(Settings::Input::Port::ControlPad &controllerSettings, uint32 portIndex); 15 | 16 | private: 17 | widgets::InputCaptureWidget m_inputCaptureWidget; 18 | widgets::UnboundActionsWidget m_unboundActionsWidget; 19 | }; 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/cdblock_filters_window.cpp: -------------------------------------------------------------------------------- 1 | #include "cdblock_filters_window.hpp" 2 | 3 | using namespace ymir; 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | CDBlockFiltersWindow::CDBlockFiltersWindow(SharedContext &context) 10 | : CDBlockWindowBase(context) 11 | , m_filtersView(context) { 12 | 13 | m_windowConfig.name = "CD Block filters"; 14 | m_windowConfig.flags = ImGuiWindowFlags_AlwaysAutoResize; 15 | } 16 | 17 | void CDBlockFiltersWindow::PrepareWindow() {} 18 | 19 | void CDBlockFiltersWindow::DrawContents() { 20 | m_filtersView.Display(); 21 | } 22 | 23 | } // namespace app::ui 24 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/scu_registers_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace app::ui { 10 | 11 | class SCURegistersWindow : public WindowBase { 12 | public: 13 | SCURegistersWindow(SharedContext &context); 14 | 15 | protected: 16 | void DrawContents() override; 17 | 18 | private: 19 | SCURegistersView m_regsView; 20 | SCUInterruptsView m_intrView; 21 | SCUTimersView m_timersView; 22 | }; 23 | 24 | } // namespace app::ui 25 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/settings/settings_view_base.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class SettingsViewBase { 8 | public: 9 | SettingsViewBase(SharedContext &context); 10 | virtual ~SettingsViewBase() = default; 11 | 12 | protected: 13 | // Makes the settings dirty unconditionally. 14 | void MakeDirty(); 15 | 16 | // Makes the settings dirty if the given value is true and returns the value. 17 | // Useful to inline with interactible ImGui widgets. 18 | bool MakeDirty(bool value); 19 | 20 | protected: 21 | SharedContext &m_context; 22 | }; 23 | 24 | } // namespace app::ui 25 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/vdp/vdp_internal_callbacks.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | @file 5 | @brief Internal callback definitions used by the VDP. 6 | */ 7 | 8 | #include 9 | 10 | namespace ymir::vdp { 11 | 12 | /// @brief Invoked when the HBlank signal changes. 13 | using CBHBlankStateChange = util::RequiredCallback; 14 | 15 | /// @brief Invoked when the VBlank signal changes. 16 | using CBVBlankStateChange = util::RequiredCallback; 17 | 18 | /// @brief Invoked when specific events occur while processing. 19 | using CBTriggerEvent = util::RequiredCallback; 20 | 21 | } // namespace ymir::vdp 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/settings/cdblock_settings_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "settings_view_base.hpp" 4 | 5 | namespace app::ui { 6 | 7 | class CDBlockSettingsView : public SettingsViewBase { 8 | public: 9 | CDBlockSettingsView(SharedContext &context); 10 | 11 | void Display(); 12 | 13 | private: 14 | static void ProcessLoadCDBlockROM(void *userdata, std::filesystem::path file, int filter); 15 | static void ProcessLoadCDBlockROMError(void *userdata, const char *message, int filter); 16 | 17 | void LoadCDBlockROM(std::filesystem::path file); 18 | void ShowCDBlockROMLoadError(const char *message); 19 | }; 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/settings/mission_stick_config_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "settings_view_base.hpp" 4 | 5 | #include 6 | #include 7 | 8 | namespace app::ui { 9 | 10 | class MissionStickConfigView : public SettingsViewBase { 11 | public: 12 | MissionStickConfigView(SharedContext &context); 13 | 14 | void Display(Settings::Input::Port::MissionStick &controllerSettings, uint32 portIndex); 15 | 16 | private: 17 | widgets::InputCaptureWidget m_inputCaptureWidget; 18 | widgets::UnboundActionsWidget m_unboundActionsWidget; 19 | }; 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/settings/ipl_settings_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "settings_view_base.hpp" 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class IPLSettingsView : public SettingsViewBase { 10 | public: 11 | IPLSettingsView(SharedContext &context); 12 | 13 | void Display(); 14 | 15 | private: 16 | static void ProcessLoadIPLROM(void *userdata, std::filesystem::path file, int filter); 17 | static void ProcessLoadIPLROMError(void *userdata, const char *message, int filter); 18 | 19 | void LoadIPLROM(std::filesystem::path file); 20 | void ShowIPLROMLoadError(const char *message); 21 | }; 22 | 23 | } // namespace app::ui 24 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/sh2_dmac_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sh2_window_base.hpp" 4 | 5 | #include 6 | #include 7 | 8 | namespace app::ui { 9 | 10 | class SH2DMAControllerWindow : public SH2WindowBase { 11 | public: 12 | SH2DMAControllerWindow(SharedContext &context, bool master); 13 | 14 | protected: 15 | void DrawContents() override; 16 | 17 | private: 18 | SH2DMAControllerRegistersView m_dmacRegsView; 19 | SH2DMAControllerChannelView m_dmacChannel0View; 20 | SH2DMAControllerChannelView m_dmacChannel1View; 21 | }; 22 | 23 | } // namespace app::ui 24 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/scsp_window_set.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "scsp_kyonex_trace_window.hpp" 4 | #include "scsp_output_window.hpp" 5 | #include "scsp_slots_window.hpp" 6 | 7 | namespace app::ui { 8 | 9 | struct SCSPWindowSet { 10 | SCSPWindowSet(SharedContext &context) 11 | : output(context) 12 | , slots(context) 13 | , kyonexTrace(context) {} 14 | 15 | void DisplayAll() { 16 | output.Display(); 17 | slots.Display(); 18 | kyonexTrace.Display(); 19 | } 20 | 21 | SCSPOutputWindow output; 22 | SCSPSlotsWindow slots; 23 | SCSPKeyOnExecuteTraceWindow kyonexTrace; 24 | }; 25 | 26 | } // namespace app::ui 27 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/cdblock/cd_drive_internal_callbacks.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | @file 5 | @brief Internal callback definitions used by the CD drive. 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | 12 | namespace ymir::cdblock { 13 | 14 | /// @brief Invoked when the CD Block changes the COMSYNC# signal state mapped to the PB2 pin on the SH-1. 15 | using CBSetCOMSYNCn = util::RequiredCallback; 16 | 17 | /// @brief Invoked when the CD Block changes the COMREQ# signal state mapped to the TIOCB3 pin on the SH-1. 18 | using CBSetCOMREQn = util::RequiredCallback; 19 | 20 | } // namespace ymir::cdblock 21 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/scu_dma_trace_window.cpp: -------------------------------------------------------------------------------- 1 | #include "scu_dma_trace_window.hpp" 2 | 3 | namespace app::ui { 4 | 5 | SCUDMATraceWindow::SCUDMATraceWindow(SharedContext &context) 6 | : WindowBase(context) 7 | , m_dmaTraceView(context) { 8 | 9 | m_windowConfig.name = "SCU DMA trace"; 10 | } 11 | 12 | void SCUDMATraceWindow::PrepareWindow() { 13 | ImGui::SetNextWindowSizeConstraints(ImVec2(425 * m_context.displayScale, 200 * m_context.displayScale), 14 | ImVec2(525 * m_context.displayScale, FLT_MAX)); 15 | } 16 | 17 | void SCUDMATraceWindow::DrawContents() { 18 | m_dmaTraceView.Display(); 19 | } 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/debug_output_window.cpp: -------------------------------------------------------------------------------- 1 | #include "debug_output_window.hpp" 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | DebugOutputWindow::DebugOutputWindow(SharedContext &context) 8 | : WindowBase(context) 9 | , m_debugOutputView(context) { 10 | 11 | m_windowConfig.name = "Debug output"; 12 | } 13 | 14 | void DebugOutputWindow::PrepareWindow() { 15 | ImGui::SetNextWindowSizeConstraints(ImVec2(365 * m_context.displayScale, 150 * m_context.displayScale), 16 | ImVec2(FLT_MAX, FLT_MAX)); 17 | } 18 | 19 | void DebugOutputWindow::DrawContents() { 20 | m_debugOutputView.Display(); 21 | } 22 | 23 | } // namespace app::ui 24 | -------------------------------------------------------------------------------- /docs/dev-notes/finicky-games/vdp2-vertical-cell-scroll.txt: -------------------------------------------------------------------------------- 1 | ===================================================================== 2 | Games that use VDP2 vertical cell scroll with questionable parameters 3 | ===================================================================== 4 | 5 | Several Capcom games 6 | The game enables vertical cell scroll on NBG0 and sets up two read cycles on T0 and T1 during the intro FMVs. 7 | This causes two entries to be read, discarding the first entry. 8 | 9 | Magical Night Dreams - Cotton 2 10 | On stage 2, the game sets up a read cycle for NBG0 vertical cell scroll on T6 and one for NBG1 on T7, but only enables 11 | the effect on NBG0, which leads to only NBG0 entries being read. 12 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/util/unreachable.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | @file 5 | @brief Defines the `util::unreachable()` function that marks code as unreachable. 6 | */ 7 | 8 | namespace util { 9 | 10 | /// @brief Marks a point in code as unreachable. 11 | /// 12 | /// This is a compiler hint that should be used in places where it is never expected to be reached. 13 | /// It invokes undefined behavior, which may lead to crashes if execution *does* reach this function. 14 | [[noreturn]] inline void unreachable() { 15 | #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) 16 | __builtin_unreachable(); 17 | #elif defined(_MSC_VER) 18 | __assume(0); 19 | #endif 20 | } 21 | 22 | } // namespace util 23 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/settings/arcade_racer_config_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "settings_view_base.hpp" 4 | 5 | #include 6 | #include 7 | 8 | namespace app::ui { 9 | 10 | class ArcadeRacerConfigView : public SettingsViewBase { 11 | public: 12 | ArcadeRacerConfigView(SharedContext &context); 13 | 14 | void Display(Settings::Input::Port::ArcadeRacer &controllerSettings, uint32 portIndex); 15 | 16 | private: 17 | widgets::InputCaptureWidget m_inputCaptureWidget; 18 | widgets::UnboundActionsWidget m_unboundActionsWidget; 19 | 20 | bool m_showRawValueInMeter = false; 21 | }; 22 | 23 | } // namespace app::ui 24 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/system_state_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | class SystemStateWindow : public WindowBase { 8 | public: 9 | SystemStateWindow(SharedContext &context); 10 | 11 | protected: 12 | void PrepareWindow() override; 13 | void DrawContents() override; 14 | 15 | private: 16 | void DrawSMPCParameters(); 17 | void DrawScreen(); 18 | void DrawRealTimeClock(); 19 | void DrawClocks(); 20 | void DrawCDBlock(); 21 | void DrawCDDrive(); 22 | void DrawBackupMemory(); 23 | void DrawCartridge(); 24 | void DrawPeripherals(); 25 | void DrawActions(); 26 | }; 27 | 28 | } // namespace app::ui 29 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/settings/system_settings_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "settings_view_base.hpp" 4 | 5 | namespace app::ui { 6 | 7 | class SystemSettingsView : public SettingsViewBase { 8 | public: 9 | SystemSettingsView(SharedContext &context); 10 | 11 | void Display(); 12 | 13 | private: 14 | static void ProcessLoadBackupImage(void *userdata, std::filesystem::path file, int filter); 15 | static void ProcessLoadBackupImageError(void *userdata, const char *message, int filter); 16 | 17 | void LoadBackupImage(std::filesystem::path file); 18 | void ShowLoadBackupImageError(const char *message); 19 | 20 | bool m_bupSettingsDirty = false; 21 | }; 22 | 23 | } // namespace app::ui 24 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/cdblock_ygr_cmd_trace_window.cpp: -------------------------------------------------------------------------------- 1 | #include "cdblock_ygr_cmd_trace_window.hpp" 2 | 3 | namespace app::ui { 4 | 5 | YGRCommandTraceWindow::YGRCommandTraceWindow(SharedContext &context) 6 | : CDBlockWindowBase(context) 7 | , m_cmdTraceView(context) { 8 | 9 | m_windowConfig.name = "YGR command trace"; 10 | } 11 | 12 | void YGRCommandTraceWindow::PrepareWindow() { 13 | ImGui::SetNextWindowSizeConstraints(ImVec2(450 * m_context.displayScale, 180 * m_context.displayScale), 14 | ImVec2(FLT_MAX, FLT_MAX)); 15 | } 16 | 17 | void YGRCommandTraceWindow::DrawContents() { 18 | m_cmdTraceView.Display(); 19 | } 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/widgets/audio_widgets.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace app::ui::widgets { 8 | 9 | struct StereoSample { 10 | float left, right; 11 | }; 12 | 13 | // Draws a monaural oscilloscope with the specified dimensions. 14 | // The waveform data will be clamped to -1.0..+1.0. 15 | void Oscilloscope(SharedContext &ctx, std::span waveform, ImVec2 size = {0, 0}); 16 | 17 | // Draws a stereo oscilloscope with the specified dimensions. 18 | // The waveform data will be clamped to -1.0..+1.0. 19 | void Oscilloscope(SharedContext &ctx, std::span waveform, ImVec2 size = {0, 0}); 20 | 21 | } // namespace app::ui::widgets 22 | -------------------------------------------------------------------------------- /docs/dev-notes/finicky-games/vdp2-midframe-effects.txt: -------------------------------------------------------------------------------- 1 | ============================================================== 2 | Games that change VDP2 registers mid-frame for special effects 3 | ============================================================== 4 | 5 | Marvel Super Heroes 6 | During Shuma-Gorath's "Chaos Dimension" super move, the background is split into multiple bands, accomplished by 7 | writing to SCYIN0, SCYIN1 and SCYN2 mid-frame. The renderer must account for these offsets throughout the frame. 8 | 9 | Road & Track Presents: The Need for Speed 10 | Changes RBG0 priority mid-frame when the camera is following the car. 11 | 12 | Street Fighter - Real Battle on Film 13 | Changes NBG1 priority mid-frame on Ken's stage. 14 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/cdblock_cmd_trace_window.cpp: -------------------------------------------------------------------------------- 1 | #include "cdblock_cmd_trace_window.hpp" 2 | 3 | namespace app::ui { 4 | 5 | CDBlockCommandTraceWindow::CDBlockCommandTraceWindow(SharedContext &context) 6 | : CDBlockWindowBase(context) 7 | , m_cmdTraceView(context) { 8 | 9 | m_windowConfig.name = "CD Block command trace"; 10 | } 11 | 12 | void CDBlockCommandTraceWindow::PrepareWindow() { 13 | ImGui::SetNextWindowSizeConstraints(ImVec2(450 * m_context.displayScale, 180 * m_context.displayScale), 14 | ImVec2(FLT_MAX, FLT_MAX)); 15 | } 16 | 17 | void CDBlockCommandTraceWindow::DrawContents() { 18 | m_cmdTraceView.Display(); 19 | } 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/vdp/vdp_callbacks.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | @file 5 | @brief VDP callbacks. 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | 12 | namespace ymir::vdp { 13 | 14 | // Invoked when the VDP1 finishes drawing a frame. 15 | using CBVDP1DrawFinished = util::OptionalCallback; 16 | 17 | // Invoked when the VDP1 swaps framebuffers. 18 | using CBVDP1FramebufferSwap = util::OptionalCallback; 19 | 20 | // Invoked when the VDP2 renderer finishes rendering a frame. 21 | // Framebuffer data is in little-endian XRGB8888 format. 22 | using CBFrameComplete = util::OptionalCallback; 23 | 24 | } // namespace ymir::vdp 25 | -------------------------------------------------------------------------------- /vendor/lz4/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(lz4 2 | lz4/lib/lz4.c 3 | lz4/lib/lz4.h 4 | ) 5 | 6 | add_library(lz4::lz4 ALIAS lz4) 7 | target_include_directories(lz4 8 | PUBLIC 9 | "$" 10 | PRIVATE 11 | "$" 12 | ) 13 | target_compile_features(lz4 PUBLIC cxx_std_20) 14 | 15 | ## Apply performance options 16 | if (Ymir_AVX2) 17 | if (MSVC) 18 | target_compile_options(lz4 PUBLIC "/arch:AVX2") 19 | else () 20 | target_compile_options(lz4 PUBLIC "-mavx2") 21 | target_compile_options(lz4 PUBLIC "-mfma") 22 | target_compile_options(lz4 PUBLIC "-mbmi") 23 | endif () 24 | endif () 25 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/widgets/unbound_actions_widget.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace app::ui::widgets { 12 | 13 | class UnboundActionsWidget { 14 | public: 15 | UnboundActionsWidget(SharedContext &context); 16 | 17 | void Display(); 18 | 19 | void Capture(const std::optional &unboundAction); 20 | void Capture(const std::unordered_set &unboundActions); 21 | 22 | private: 23 | SharedContext &m_context; 24 | 25 | std::set m_unboundActions; 26 | }; 27 | 28 | } // namespace app::ui::widgets 29 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/cdblock_drive_state_trace_window.cpp: -------------------------------------------------------------------------------- 1 | #include "cdblock_drive_state_trace_window.hpp" 2 | 3 | namespace app::ui { 4 | 5 | CDDriveStateTraceWindow::CDDriveStateTraceWindow(SharedContext &context) 6 | : CDBlockWindowBase(context) 7 | , m_stateTraceView(context) { 8 | 9 | m_windowConfig.name = "CD drive state trace"; 10 | } 11 | 12 | void CDDriveStateTraceWindow::PrepareWindow() { 13 | ImGui::SetNextWindowSizeConstraints(ImVec2(720 * m_context.displayScale, 250 * m_context.displayScale), 14 | ImVec2(FLT_MAX, FLT_MAX)); 15 | } 16 | 17 | void CDDriveStateTraceWindow::DrawContents() { 18 | m_stateTraceView.Display(); 19 | } 20 | 21 | } // namespace app::ui 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/settings/general_settings_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "settings_view_base.hpp" 4 | 5 | namespace app::ui { 6 | 7 | class GeneralSettingsView : public SettingsViewBase { 8 | public: 9 | GeneralSettingsView(SharedContext &context); 10 | 11 | void Display(); 12 | 13 | private: 14 | ProfilePath m_selectedProfPath; 15 | 16 | static void ProcessPathOverrideSelection(void *userdata, std::filesystem::path file, int filter); 17 | static void ProcessPathOverrideSelectionError(void *userdata, const char *message, int filter); 18 | 19 | void SelectPathOverride(std::filesystem::path file); 20 | void ShowPathOverrideSelectionError(const char *message); 21 | }; 22 | 23 | } // namespace app::ui 24 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/sh2_timers_window.cpp: -------------------------------------------------------------------------------- 1 | #include "sh2_timers_window.hpp" 2 | 3 | using namespace ymir; 4 | 5 | namespace app::ui { 6 | 7 | SH2TimersWindow::SH2TimersWindow(SharedContext &context, bool master) 8 | : SH2WindowBase(context, master) 9 | , m_frtView(context, m_sh2) 10 | , m_wdtView(context, m_sh2) { 11 | 12 | m_windowConfig.name = fmt::format("{}SH2 timers", master ? 'M' : 'S'); 13 | m_windowConfig.flags = ImGuiWindowFlags_AlwaysAutoResize; 14 | } 15 | 16 | void SH2TimersWindow::DrawContents() { 17 | ImGui::SeparatorText("Free-running timer"); 18 | m_frtView.Display(); 19 | 20 | ImGui::SeparatorText("Watchdog timer"); 21 | m_wdtView.Display(); 22 | } 23 | 24 | } // namespace app::ui 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | 30 | # Visual Studio files 31 | .vs/ 32 | out/ 33 | enc_temp_folder/ 34 | CMakeSettings.json 35 | 36 | # CMake build folders 37 | build/ 38 | build-*/ 39 | cmake-build-*/ 40 | 41 | # CLion project metadata 42 | .idea/ 43 | 44 | # macOS custom folder attributes 45 | .DS_Store 46 | 47 | # Generated mig files on MacOS 48 | apps/ymir-sdl3/src/util/mig/a64 49 | apps/ymir-sdl3/src/util/mig/x64 50 | -------------------------------------------------------------------------------- /libs/ymir-core/src/ymir/media/cdrom_crc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | namespace ymir::media { 6 | 7 | static constexpr auto kCRCTable = [] { 8 | std::array crcTable{}; 9 | for (uint32 i = 0; i < 256; ++i) { 10 | uint32 c = i; 11 | for (uint32 j = 0; j < 8; ++j) { 12 | c = (c >> 1) ^ ((c & 0x1) ? 0xD8018001 : 0); 13 | } 14 | crcTable[i] = c; 15 | } 16 | return crcTable; 17 | }(); 18 | 19 | uint32 CalcCRC(std::span sector) { 20 | uint32 crc = 0; 21 | for (uint8 b : sector) { 22 | crc ^= b; 23 | crc = (crc >> 8) ^ kCRCTable[crc & 0xFF]; 24 | } 25 | return crc; 26 | } 27 | 28 | } // namespace ymir::media 29 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/vdp2_cram_window.cpp: -------------------------------------------------------------------------------- 1 | #include "vdp2_cram_window.hpp" 2 | 3 | namespace app::ui { 4 | 5 | VDP2CRAMWindow::VDP2CRAMWindow(SharedContext &context) 6 | : VDPWindowBase(context) 7 | , m_cramView(context, m_vdp) { 8 | 9 | m_windowConfig.name = "VDP2 Color RAM palette"; 10 | // m_windowConfig.flags = ImGuiWindowFlags_AlwaysAutoResize; 11 | } 12 | 13 | void VDP2CRAMWindow::PrepareWindow() { 14 | ImGui::SetNextWindowSizeConstraints(ImVec2(860 * m_context.displayScale, 250 * m_context.displayScale), 15 | ImVec2(860 * m_context.displayScale, FLT_MAX)); 16 | } 17 | 18 | void VDP2CRAMWindow::DrawContents() { 19 | m_cramView.Display(); 20 | } 21 | 22 | } // namespace app::ui 23 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/memory_viewer_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "app/events/emu_event_factory.hpp" 8 | 9 | #include 10 | 11 | namespace app::ui { 12 | 13 | class MemoryViewerWindow : public WindowBase { 14 | public: 15 | MemoryViewerWindow(SharedContext &context); 16 | 17 | uint32 Index() const { 18 | return m_index; 19 | } 20 | 21 | protected: 22 | void PrepareWindow() override; 23 | void DrawContents() override; 24 | 25 | private: 26 | static uint32 s_index; 27 | uint32 m_index; 28 | 29 | std::unique_ptr m_memViewState; 30 | }; 31 | 32 | } // namespace app::ui 33 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/scsp_output_view.cpp: -------------------------------------------------------------------------------- 1 | #include "scsp_output_view.hpp" 2 | 3 | namespace app::ui { 4 | 5 | SCSPOutputView::SCSPOutputView(SharedContext &context) 6 | : m_context(context) {} 7 | 8 | void SCSPOutputView::Display(ImVec2 size) { 9 | m_context.audioSystem.Snapshot(m_audioBuffer); 10 | for (uint32 i = 0; i < m_waveform.size(); ++i) { 11 | // The conversion to float is slightly assymetric, but unnoticeable in practice 12 | const auto sample = m_audioBuffer[i]; 13 | m_waveform[i].left = static_cast(sample.left) / 32768.0f; 14 | m_waveform[i].right = static_cast(sample.right) / 32768.0f; 15 | } 16 | widgets::Oscilloscope(m_context, m_waveform, size); 17 | } 18 | 19 | } // namespace app::ui 20 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/sh2_divu_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sh2_window_base.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace app::ui { 10 | 11 | class SH2DivisionUnitWindow : public SH2WindowBase { 12 | public: 13 | SH2DivisionUnitWindow(SharedContext &context, bool master); 14 | 15 | protected: 16 | void PrepareWindow() override; 17 | void DrawContents() override; 18 | 19 | private: 20 | SH2DivisionUnitRegistersView m_divuRegsView; 21 | SH2DivisionUnitStatisticsView m_divuStatsView; 22 | SH2DivisionUnitTraceView m_divuTraceView; 23 | }; 24 | 25 | } // namespace app::ui 26 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/smpc/peripheral/peripheral_impl_null.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peripheral_base.hpp" 4 | 5 | namespace ymir::peripheral { 6 | 7 | class NullPeripheral final : public BasePeripheral { 8 | public: 9 | NullPeripheral() 10 | : BasePeripheral(PeripheralType::None, 0x0, {}) {} 11 | 12 | [[nodiscard]] bool IsConnected() const override { 13 | return false; 14 | } 15 | 16 | void UpdateInputs() override {} 17 | 18 | [[nodiscard]] uint8 GetReportLength() const override { 19 | return 0; 20 | } 21 | 22 | void Read(std::span out) override {} 23 | 24 | [[nodiscard]] uint8 WritePDR(uint8 ddr, uint8 value) override { 25 | return 0; 26 | } 27 | }; 28 | 29 | } // namespace ymir::peripheral 30 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/scsp_output_window.cpp: -------------------------------------------------------------------------------- 1 | #include "scsp_output_window.hpp" 2 | 3 | namespace app::ui { 4 | 5 | SCSPOutputWindow::SCSPOutputWindow(SharedContext &context) 6 | : SCSPWindowBase(context) 7 | , m_outputView(context) { 8 | 9 | m_windowConfig.name = "SCSP output"; 10 | // m_windowConfig.flags = ImGuiWindowFlags_AlwaysAutoResize; 11 | } 12 | 13 | void SCSPOutputWindow::PrepareWindow() { 14 | ImGui::SetNextWindowSizeConstraints(ImVec2(200 * m_context.displayScale, 50 * m_context.displayScale), 15 | ImVec2(FLT_MAX, 200 * m_context.displayScale)); 16 | } 17 | 18 | void SCSPOutputWindow::DrawContents() { 19 | m_outputView.Display(ImGui::GetContentRegionAvail()); 20 | } 21 | 22 | } // namespace app::ui 23 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/scu_registers_window.cpp: -------------------------------------------------------------------------------- 1 | #include "scu_registers_window.hpp" 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | SCURegistersWindow::SCURegistersWindow(SharedContext &context) 8 | : WindowBase(context) 9 | , m_regsView(context) 10 | , m_intrView(context) 11 | , m_timersView(context) { 12 | 13 | m_windowConfig.name = "SCU registers"; 14 | m_windowConfig.flags = ImGuiWindowFlags_AlwaysAutoResize; 15 | } 16 | 17 | void SCURegistersWindow::DrawContents() { 18 | ImGui::SeparatorText("Registers"); 19 | m_regsView.Display(); 20 | 21 | ImGui::SeparatorText("Interrupts"); 22 | m_intrView.Display(); 23 | 24 | ImGui::SeparatorText("Timers"); 25 | m_timersView.Display(); 26 | } 27 | 28 | } // namespace app::ui 29 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/mio"] 2 | path = vendor/mio 3 | url = https://github.com/StrikerX3/mio.git 4 | [submodule "vendor/concurrentqueue"] 5 | path = vendor/concurrentqueue 6 | url = https://github.com/cameron314/concurrentqueue.git 7 | [submodule "vendor/imgui/imgui"] 8 | path = vendor/imgui/imgui 9 | url = https://github.com/ocornut/imgui.git 10 | branch = docking 11 | [submodule "vendor/xxHash/xxHash"] 12 | path = vendor/xxHash/xxHash 13 | url = https://github.com/Cyan4973/xxHash 14 | [submodule "vendor/lz4/lz4"] 15 | path = vendor/lz4/lz4 16 | url = https://github.com/lz4/lz4.git 17 | [submodule "vendor/libchdr"] 18 | path = vendor/libchdr 19 | url = https://github.com/rtissera/libchdr.git 20 | [submodule "vcpkg"] 21 | path = vcpkg 22 | url = https://github.com/microsoft/vcpkg.git 23 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/sh1/sh1_power.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace ymir::sh1 { 6 | 7 | // addr r/w access init code name 8 | // 1BC R/W 8,16,32 1F SBYCR Standby Control Register 9 | // 10 | // bits r/w code description 11 | // 7 R/W SBY Standby (0=SLEEP -> sleep mode, 1=SLEEP -> standby mode) 12 | // 6 R/W HIZ Port High Impedance (0=port state retained in standby mode, 1=ports go to high impedance) 13 | // 5 R - Reserved - must be zero 14 | // 4-0 R - Reserved - must be one 15 | union RegSBYCR { 16 | uint8 u8 = 0x1F; 17 | struct { 18 | uint8 : 6; 19 | uint8 HIZ : 1; 20 | uint8 SBY : 1; 21 | }; 22 | }; 23 | 24 | } // namespace ymir::sh1 25 | -------------------------------------------------------------------------------- /docs/dev-notes/system-info/cdblock-ygr.txt: -------------------------------------------------------------------------------- 1 | YGR registers 2 | ------------- 3 | Addresses 4 | CDB Host Code Name 5 | 00 00 DATA Data transfer 6 | 02 -- TRCTL Transfer control 7 | 04 -- CDIRQL Lower CD IRQ bits 8 | 06 -- CDIRQU Upper CD IRQ bits 9 | 08 -- CDMSKL Lower CD IRQ mask 10 | 0A -- CDMSU Upper CD IRQ mask 11 | 0C -- REG0C Unknown 12 | 0E -- REG0E Unknown 13 | 10 18 CR1/RR1 Command/Response register 1 14 | 12 1C CR2/RR2 Command/Response register 2 15 | 14 20 CR3/RR3 Command/Response register 3 16 | 16 24 CR4/RR4 Command/Response register 4 17 | 18 -- REG18 Unknown 18 | 1A -- REG1A Unknown 19 | 1C -- REG1C Unknown 20 | 1E 08 HIRQ Host IRQ bits 21 | -- 0C HIRQMASK Host IRQ mask 22 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/smpc/peripheral/peripheral_state_common.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace ymir::peripheral { 8 | 9 | enum class Button : uint16 { 10 | None = 0, 11 | 12 | Right = 1u << 15u, 13 | Left = 1u << 14u, 14 | Down = 1u << 13u, 15 | Up = 1u << 12u, 16 | Start = 1u << 11u, 17 | A = 1u << 10u, 18 | C = 1u << 9u, 19 | B = 1u << 8u, 20 | R = 1u << 7u, 21 | X = 1u << 6u, 22 | Y = 1u << 5u, 23 | Z = 1u << 4u, 24 | L = 1u << 3u, 25 | 26 | All = Right | Left | Down | Up | Start | A | C | B | R | X | Y | Z | L, 27 | 28 | Default = All, 29 | }; 30 | 31 | } // namespace ymir::peripheral 32 | 33 | ENABLE_BITMASK_OPERATORS(ymir::peripheral::Button); 34 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/debug_output_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace app::ui { 8 | 9 | class DebugOutputView { 10 | public: 11 | DebugOutputView(SharedContext &context); 12 | 13 | void Display(); 14 | 15 | private: 16 | SharedContext &m_context; 17 | SCUTracer &m_tracer; 18 | 19 | static void ProcessExportDebugOutput(void *userdata, std::filesystem::path file, int filter); 20 | static void ProcessCancelExport(void *userdata, int filter); 21 | static void ProcessExportError(void *userdata, const char *errorMessage, int filter); 22 | 23 | void ExportDebugOutput(std::filesystem::path file); 24 | void ShowErrorDialog(const char *message); 25 | }; 26 | 27 | } // namespace app::ui 28 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/scu_interrupt_trace_window.cpp: -------------------------------------------------------------------------------- 1 | #include "scu_interrupt_trace_window.hpp" 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | SCUInterruptTraceWindow::SCUInterruptTraceWindow(SharedContext &context) 8 | : WindowBase(context) 9 | , m_intrTraceView(context) { 10 | 11 | m_windowConfig.name = "SCU interrupt trace"; 12 | } 13 | 14 | void SCUInterruptTraceWindow::PrepareWindow() { 15 | ImGui::SetNextWindowSizeConstraints(ImVec2(300 * m_context.displayScale, 200 * m_context.displayScale), 16 | ImVec2(450 * m_context.displayScale, FLT_MAX)); 17 | } 18 | 19 | void SCUInterruptTraceWindow::DrawContents() { 20 | ImGui::SeparatorText("Interrupt trace"); 21 | m_intrTraceView.Display(); 22 | } 23 | 24 | } // namespace app::ui 25 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/vdp2_debug_overlay_window.cpp: -------------------------------------------------------------------------------- 1 | #include "vdp2_debug_overlay_window.hpp" 2 | 3 | namespace app::ui { 4 | 5 | VDP2DebugOverlayWindow::VDP2DebugOverlayWindow(SharedContext &context) 6 | : VDPWindowBase(context) 7 | , m_debugOverlayView(context, m_vdp) { 8 | 9 | m_windowConfig.name = "VDP2 debug overlay"; 10 | m_windowConfig.flags = ImGuiWindowFlags_AlwaysAutoResize; 11 | } 12 | 13 | void VDP2DebugOverlayWindow::PrepareWindow() { 14 | /*ImGui::SetNextWindowSizeConstraints(ImVec2(860 * m_context.displayScale, 250 * m_context.displayScale), 15 | ImVec2(860 * m_context.displayScale, FLT_MAX));*/ 16 | } 17 | 18 | void VDP2DebugOverlayWindow::DrawContents() { 19 | m_debugOverlayView.Display(); 20 | } 21 | 22 | } // namespace app::ui 23 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/scsp_kyonex_trace_window.cpp: -------------------------------------------------------------------------------- 1 | #include "scsp_kyonex_trace_window.hpp" 2 | 3 | namespace app::ui { 4 | 5 | SCSPKeyOnExecuteTraceWindow::SCSPKeyOnExecuteTraceWindow(SharedContext &context) 6 | : SCSPWindowBase(context) 7 | , m_kyonexTraceView(context) { 8 | 9 | m_windowConfig.name = "SCSP KYONEX trace"; 10 | // m_windowConfig.flags = ImGuiWindowFlags_AlwaysAutoResize; 11 | } 12 | 13 | void SCSPKeyOnExecuteTraceWindow::PrepareWindow() { 14 | ImGui::SetNextWindowSizeConstraints(ImVec2(300 * m_context.displayScale, 100 * m_context.displayScale), 15 | ImVec2(450 * m_context.displayScale, FLT_MAX)); 16 | } 17 | 18 | void SCSPKeyOnExecuteTraceWindow::DrawContents() { 19 | m_kyonexTraceView.Display(); 20 | } 21 | 22 | } // namespace app::ui 23 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/smpc/peripheral/peripheral_impl_control_pad.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "peripheral_base.hpp" 4 | 5 | namespace ymir::peripheral { 6 | 7 | /// @brief Implements the Saturn Control Pad (ID 0x0/2 bytes) with: 8 | /// - 6 digital buttons: ABC XYZ 9 | /// - 2 shoulder buttons: L R 10 | /// - Directional pad 11 | /// - Start button 12 | class ControlPad final : public BasePeripheral { 13 | public: 14 | explicit ControlPad(CBPeripheralReport callback); 15 | 16 | void UpdateInputs() override; 17 | 18 | [[nodiscard]] uint8 GetReportLength() const override; 19 | 20 | void Read(std::span out) override; 21 | 22 | [[nodiscard]] uint8 WritePDR(uint8 ddr, uint8 value) override; 23 | 24 | private: 25 | ControlPadReport m_report; 26 | }; 27 | 28 | } // namespace ymir::peripheral 29 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/util/std_lib.cpp: -------------------------------------------------------------------------------- 1 | #include "std_lib.hpp" 2 | 3 | #include 4 | 5 | namespace util { 6 | 7 | tm to_local_time(std::chrono::system_clock::time_point tp) { 8 | const time_t time = std::chrono::system_clock::to_time_t(tp); 9 | tm tm; 10 | #if defined(_MSC_VER) || defined(_M_ARM64) 11 | void(localtime_s(&tm, &time)); 12 | #elif defined(__GNUC__) 13 | localtime_r(&time, &tm); 14 | #else 15 | tm = *localtime(&time); 16 | #endif 17 | return tm; 18 | } 19 | 20 | std::optional parse8601(std::string str) { 21 | std::istringstream in{str}; 22 | date::sys_time tp{}; 23 | date::from_stream(in, "%FT%TZ", tp); 24 | if (in) { 25 | return tp.time_since_epoch(); 26 | } 27 | return std::nullopt; 28 | } 29 | 30 | } // namespace util 31 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/sh2_debugger_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sh2_window_base.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace app::ui { 10 | 11 | class SH2DebuggerWindow : public SH2WindowBase { 12 | public: 13 | SH2DebuggerWindow(SharedContext &context, bool master); 14 | 15 | void LoadState(std::filesystem::path path); 16 | void SaveState(std::filesystem::path path); 17 | 18 | protected: 19 | void PrepareWindow() override; 20 | void DrawContents() override; 21 | 22 | private: 23 | SH2DebugToolbarView m_toolbarView; 24 | SH2RegistersView m_regsView; 25 | SH2DisassemblyView m_disasmView; 26 | }; 27 | 28 | } // namespace app::ui 29 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/sh2_interrupt_trace_window.cpp: -------------------------------------------------------------------------------- 1 | #include "sh2_interrupt_trace_window.hpp" 2 | 3 | using namespace ymir; 4 | 5 | namespace app::ui { 6 | 7 | SH2InterruptTraceWindow::SH2InterruptTraceWindow(SharedContext &context, bool master) 8 | : SH2WindowBase(context, master) 9 | , m_intrTraceView(context, m_tracer) { 10 | 11 | m_windowConfig.name = fmt::format("{}SH2 interrupt trace", master ? 'M' : 'S'); 12 | } 13 | 14 | void SH2InterruptTraceWindow::PrepareWindow() { 15 | ImGui::SetNextWindowSizeConstraints(ImVec2(250 * m_context.displayScale, 200 * m_context.displayScale), 16 | ImVec2(600 * m_context.displayScale, FLT_MAX)); 17 | } 18 | 19 | void SH2InterruptTraceWindow::DrawContents() { 20 | m_intrTraceView.Display(); 21 | } 22 | 23 | } // namespace app::ui 24 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/state/state_ygr.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace ymir::state { 8 | 9 | struct YGRState { 10 | struct FIFOState { 11 | std::array data; 12 | uint32 readPos; 13 | uint32 writePos; 14 | uint32 count; 15 | } fifo; 16 | 17 | struct Registers { 18 | uint16 TRCTL; 19 | uint16 CDIRQL; 20 | uint16 CDIRQU; 21 | uint16 CDMSKL; 22 | uint16 CDMSKU; 23 | uint16 REG0C; 24 | uint16 REG0E; 25 | std::array CR; 26 | std::array RR; 27 | uint16 REG18; 28 | uint16 REG1A; 29 | uint16 REG1C; 30 | uint16 HIRQ; 31 | uint16 HIRQMASK; 32 | } regs; 33 | }; 34 | 35 | } // namespace ymir::state 36 | -------------------------------------------------------------------------------- /libs/ymir-core/src/ymir/db/cdb_rom_db.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | namespace ymir::db { 6 | 7 | // clang-format off 8 | static const std::unordered_map kCDBlockROMInfos = { 9 | {MakeXXH128Hash(0xFCEB830FBF504735,0x42CFE8897B5F0092), {"1.04"}}, 10 | {MakeXXH128Hash(0x56E1DBE90A499DA7,0x1BD11A845445188A), {"1.05"}}, 11 | {MakeXXH128Hash(0xA2A824298D3ACFFC,0x3D1CEC215D8531F0), {"1.06"}}, 12 | {MakeXXH128Hash(0x12D7086732B5CC54,0x146D5A7B5223C96B), {"1.06 (alt)"}}, 13 | }; 14 | // clang-format on 15 | 16 | const CDBlockROMInfo *GetCDBlockROMInfo(XXH128Hash hash) { 17 | if (kCDBlockROMInfos.contains(hash)) { 18 | return &kCDBlockROMInfos.at(hash); 19 | } else { 20 | return nullptr; 21 | } 22 | } 23 | 24 | } // namespace ymir::db 25 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/packaging/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(APPLE) 2 | # Install the .app bundle on macOS 3 | install(TARGETS ymir-sdl3 EXPORT Ymir_Targets 4 | BUNDLE DESTINATION . COMPONENT Ymir_Runtime 5 | RUNTIME DESTINATION . COMPONENT Ymir_Runtime 6 | LIBRARY COMPONENT Ymir_Runtime 7 | NAMELINK_COMPONENT Ymir_Development 8 | ARCHIVE COMPONENT Ymir_Development 9 | INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") 10 | else() 11 | # Standard installation for other platforms 12 | install(TARGETS ymir-sdl3 EXPORT Ymir_Targets 13 | RUNTIME COMPONENT Ymir_Runtime 14 | LIBRARY COMPONENT Ymir_Runtime 15 | NAMELINK_COMPONENT Ymir_Development 16 | ARCHIVE COMPONENT Ymir_Runtime 17 | INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") 18 | endif() 19 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/sh1/sh1_regs.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace ymir::sh1 { 6 | 7 | // MACH and MACL 8 | union RegMAC { 9 | uint64 u64; 10 | struct { 11 | uint32 L; 12 | uint32 H; 13 | }; 14 | }; 15 | 16 | union RegSR { 17 | uint32 u32; 18 | struct { 19 | uint32 T : 1; // 0 Test flag 20 | uint32 S : 1; // 1 Saturate - Used by multiply/accumulate 21 | uint32 : 2; // 2-3 (reserved, must be zero) 22 | uint32 ILevel : 4; // 4-7 Interrupt mask 23 | uint32 Q : 1; // 8 Quotient - Used by DIV0U/S and DIV1 24 | uint32 M : 1; // 9 Modulus - Used by DIV0U/S and DIV1 25 | // (remaining bits are reserved and must be zero) 26 | }; 27 | }; 28 | 29 | } // namespace ymir::sh1 30 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/sh2/sh2_regs.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace ymir::sh2 { 6 | 7 | // MACH and MACL 8 | union RegMAC { 9 | uint64 u64; 10 | struct { 11 | uint32 L; 12 | uint32 H; 13 | }; 14 | }; 15 | 16 | union RegSR { 17 | uint32 u32; 18 | struct { 19 | uint32 T : 1; // 0 Test flag 20 | uint32 S : 1; // 1 Saturate - Used by multiply/accumulate 21 | uint32 : 2; // 2-3 (reserved, must be zero) 22 | uint32 ILevel : 4; // 4-7 Interrupt mask 23 | uint32 Q : 1; // 8 Quotient - Used by DIV0U/S and DIV1 24 | uint32 M : 1; // 9 Modulus - Used by DIV0U/S and DIV1 25 | // (remaining bits are reserved and must be zero) 26 | }; 27 | }; 28 | 29 | } // namespace ymir::sh2 30 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/db/cdb_rom_db.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | @file 5 | @brief CD Block ROM database. 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | 12 | namespace ymir::db { 13 | 14 | /// @brief Information about a CD Block ROM in the database. 15 | struct CDBlockROMInfo { 16 | std::string_view version; ///< CD Block ROM version string 17 | }; 18 | 19 | /// @brief Retrieves information about a CD Block ROM image given its XXH128 hash. 20 | /// 21 | /// Returns `nullptr` if there is no information for the given hash. 22 | /// 23 | /// @param[in] hash the CD Block ROM hash to check 24 | /// @return a pointer to `CDBlockROMInfo` containing information about the CD Block ROM, or `nullptr` if no matching ROM 25 | /// was found 26 | const CDBlockROMInfo *GetCDBlockROMInfo(XXH128Hash hash); 27 | 28 | } // namespace ymir::db 29 | -------------------------------------------------------------------------------- /tests/ymir-core-tests/src/hw/sh2/sh2_disasm_tests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | using namespace ymir; 6 | 7 | TEST_CASE("SH2 disassembler smoke tests", "[sh2][disasm][smoke]") { 8 | SECTION("mov.l @(0x0600035C), r6") { 9 | const uint32 pc = 0x0600035A; 10 | const auto &disasm = sh2::Disassemble(0xD600); 11 | 12 | REQUIRE(disasm.mnemonic == sh2::Mnemonic::MOV); 13 | CHECK(disasm.validInDelaySlot == true); 14 | CHECK(disasm.opSize == sh2::OperandSize::Long); 15 | CHECK(disasm.op1.type == sh2::Operand::Type::AtDispPCWordAlign); 16 | CHECK(disasm.op1.immDisp == 4); 17 | CHECK(disasm.op2.type == sh2::Operand::Type::Rn); 18 | CHECK(disasm.op2.reg == 6); 19 | CHECK((pc & ~3) + disasm.op1.immDisp == 0x0600035C); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/scsp/scsp_midi_defs.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "scsp_defs.hpp" 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace ymir::scsp { 10 | 11 | // Maximum length of time MIDI events can be scheduled ahead (0.25s) 12 | inline constexpr uint64 kMaxMidiScheduleTime = kAudioFreq / 4; 13 | 14 | // Constant value added to MIDI schedule time to keep events ahead of real time 15 | inline constexpr uint64 kMidiAheadTime = 1024; 16 | 17 | // Maximum size of MIDI input/output buffers 18 | inline constexpr size_t kMidiBufferSize = 1024; 19 | 20 | struct MidiMessage { 21 | double deltaTime; 22 | std::vector payload; 23 | 24 | MidiMessage(double deltaTime, std::vector &&payload) 25 | : deltaTime(deltaTime) 26 | , payload(std::move(payload)) {} 27 | }; 28 | 29 | } // namespace ymir::scsp -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/sh2_exception_vectors_window.cpp: -------------------------------------------------------------------------------- 1 | #include "sh2_exception_vectors_window.hpp" 2 | 3 | namespace app::ui { 4 | 5 | SH2ExceptionVectorsWindow::SH2ExceptionVectorsWindow(SharedContext &context, bool master) 6 | : SH2WindowBase(context, master) 7 | , m_excptVecView(context, m_sh2) { 8 | 9 | m_windowConfig.name = fmt::format("{}SH2 exception vectors", master ? 'M' : 'S'); 10 | m_windowConfig.flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_AlwaysAutoResize; 11 | } 12 | 13 | void SH2ExceptionVectorsWindow::PrepareWindow() { 14 | const float width = m_excptVecView.GetWidth(); 15 | ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(width * m_context.displayScale, FLT_MAX)); 16 | } 17 | 18 | void SH2ExceptionVectorsWindow::DrawContents() { 19 | m_excptVecView.Display(); 20 | } 21 | 22 | } // namespace app::ui 23 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/debug/cdblock_tracer.cpp: -------------------------------------------------------------------------------- 1 | #include "cdblock_tracer.hpp" 2 | 3 | namespace app { 4 | 5 | void CDBlockTracer::ClearCommands() { 6 | commands.Clear(); 7 | m_commandCounter = 0; 8 | } 9 | 10 | void CDBlockTracer::ProcessCommand(uint16 cr1, uint16 cr2, uint16 cr3, uint16 cr4) { 11 | if (!traceCommands) { 12 | return; 13 | } 14 | 15 | commands.Write({.index = m_commandCounter++, .request = {cr1, cr2, cr3, cr4}, .processed = false}); 16 | } 17 | 18 | void CDBlockTracer::ProcessCommandResponse(uint16 cr1, uint16 cr2, uint16 cr3, uint16 cr4) { 19 | if (!traceCommands) { 20 | return; 21 | } 22 | 23 | auto &cmd = commands.GetLast(); 24 | cmd.response[0] = cr1; 25 | cmd.response[1] = cr2; 26 | cmd.response[2] = cr3; 27 | cmd.response[3] = cr4; 28 | cmd.processed = true; 29 | } 30 | 31 | } // namespace app 32 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/sh2_breakpoints_window.cpp: -------------------------------------------------------------------------------- 1 | #include "sh2_breakpoints_window.hpp" 2 | 3 | using namespace ymir; 4 | 5 | namespace app::ui { 6 | 7 | SH2BreakpointsWindow::SH2BreakpointsWindow(SharedContext &context, bool master) 8 | : SH2WindowBase(context, master) 9 | , m_breakpointsView(context, m_sh2) { 10 | 11 | m_windowConfig.name = fmt::format("{}SH2 breakpoints", master ? 'M' : 'S'); 12 | // m_windowConfig.flags = ImGuiWindowFlags_MenuBar; 13 | } 14 | 15 | void SH2BreakpointsWindow::PrepareWindow() { 16 | ImGui::SetNextWindowSizeConstraints(ImVec2(250 * m_context.displayScale, 250 * m_context.displayScale), 17 | ImVec2(250 * m_context.displayScale, FLT_MAX)); 18 | } 19 | 20 | void SH2BreakpointsWindow::DrawContents() { 21 | m_breakpointsView.Display(); 22 | } 23 | 24 | } // namespace app::ui 25 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/sh2_watchpoints_window.cpp: -------------------------------------------------------------------------------- 1 | #include "sh2_watchpoints_window.hpp" 2 | 3 | using namespace ymir; 4 | 5 | namespace app::ui { 6 | 7 | SH2WatchpointsWindow::SH2WatchpointsWindow(SharedContext &context, bool master) 8 | : SH2WindowBase(context, master) 9 | , m_watchpointsView(context, m_sh2) { 10 | 11 | m_windowConfig.name = fmt::format("{}SH2 watchpoints", master ? 'M' : 'S'); 12 | // m_windowConfig.flags = ImGuiWindowFlags_MenuBar; 13 | } 14 | 15 | void SH2WatchpointsWindow::PrepareWindow() { 16 | ImGui::SetNextWindowSizeConstraints(ImVec2(285 * m_context.displayScale, 300 * m_context.displayScale), 17 | ImVec2(285 * m_context.displayScale, FLT_MAX)); 18 | } 19 | 20 | void SH2WatchpointsWindow::DrawContents() { 21 | m_watchpointsView.Display(); 22 | } 23 | 24 | } // namespace app::ui 25 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/cdblock/cdblock_internal_callbacks.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | @file 5 | @brief Internal callback definitions used by the CD block. 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | 14 | namespace ymir::cdblock { 15 | 16 | /// @brief Invoked when the CD Block raises an interrupt. 17 | using CBTriggerExternalInterrupt0 = util::RequiredCallback; 18 | 19 | /// @brief Invoked when the CD Block reads a CDDA sector. 20 | /// 21 | /// The callback should return how many thirds of the audio buffer are full. 22 | using CBCDDASector = util::RequiredCallback data)>; 23 | 24 | /// @brief Invoked when the CD Block reads a data sector. 25 | using CBDataSector = util::RequiredCallback data)>; 26 | 27 | } // namespace ymir::cdblock 28 | -------------------------------------------------------------------------------- /vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | "catch2", 4 | "cereal", 5 | { 6 | "name": "curl", 7 | "features": [ 8 | "brotli", 9 | "http2", 10 | "http3", 11 | "ssl", 12 | "zstd" 13 | ] 14 | }, 15 | "cxxopts", 16 | "date", 17 | "fmt", 18 | "neargye-semver", 19 | "nlohmann-json", 20 | "openssl", 21 | "rtmidi", 22 | { 23 | "name": "sdl3", 24 | "features": [ 25 | "vulkan" 26 | ] 27 | }, 28 | { 29 | "name": "sdl3", 30 | "features": [ 31 | "ibus", 32 | "wayland" 33 | ], 34 | "platform": "linux" 35 | }, 36 | { 37 | "name": "sdl3", 38 | "features": [ 39 | "alsa", 40 | "x11" 41 | ], 42 | "platform": "!windows" 43 | }, 44 | "stb", 45 | "tomlplusplus" 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /cmake/TargetConditionalSources.cmake: -------------------------------------------------------------------------------- 1 | function (target_conditional_sources project condition) 2 | foreach (input_file IN LISTS ARGN) 3 | if (NOT IS_ABSOLUTE ${input_file}) 4 | set(input_file "${CMAKE_CURRENT_SOURCE_DIR}/${input_file}") 5 | endif () 6 | 7 | set(output_file "${CMAKE_CURRENT_BINARY_DIR}/generated/${input_file}") 8 | add_custom_command( 9 | OUTPUT "${output_file}" 10 | COMMAND 11 | ${CMAKE_COMMAND} 12 | "-Dcondition=${condition}" 13 | "-Dinput_file=${input_file}" 14 | "-Doutput_file=${output_file}" 15 | -P "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/DefineWrapFile.cmake" 16 | DEPENDS "${input_file}" 17 | VERBATIM 18 | ) 19 | target_sources(${project} PRIVATE "${output_file}") 20 | endforeach () 21 | endfunction () 22 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/scu_registers_view.cpp: -------------------------------------------------------------------------------- 1 | #include "scu_registers_view.hpp" 2 | 3 | #include 4 | 5 | namespace app::ui { 6 | 7 | SCURegistersView::SCURegistersView(SharedContext &context) 8 | : m_scu(context.saturn.GetSCU()) {} 9 | 10 | void SCURegistersView::Display() { 11 | auto &probe = m_scu.GetProbe(); 12 | 13 | ImGui::BeginGroup(); 14 | 15 | bool wramSizeSelect = probe.GetWRAMSizeSelect(); 16 | ImGui::AlignTextToFramePadding(); 17 | ImGui::TextUnformatted("WRAM size:"); 18 | ImGui::SameLine(); 19 | if (ImGui::RadioButton("512 KiB (2x2 Mbit)", !wramSizeSelect)) { 20 | probe.SetWRAMSizeSelect(false); 21 | } 22 | ImGui::SameLine(); 23 | if (ImGui::RadioButton("1 MiB (2x4 Mbit)", wramSizeSelect)) { 24 | probe.SetWRAMSizeSelect(true); 25 | } 26 | 27 | ImGui::EndGroup(); 28 | } 29 | 30 | } // namespace app::ui 31 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/cdblock_window_set.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cdblock_cmd_trace_window.hpp" 4 | #include "cdblock_drive_state_trace_window.hpp" 5 | #include "cdblock_filters_window.hpp" 6 | #include "cdblock_ygr_cmd_trace_window.hpp" 7 | 8 | namespace app::ui { 9 | 10 | struct CDBlockWindowSet { 11 | CDBlockWindowSet(SharedContext &context) 12 | : cmdTrace(context) 13 | , filters(context) 14 | , driveStateTrace(context) 15 | , ygrCmdTrace(context) {} 16 | 17 | void DisplayAll() { 18 | cmdTrace.Display(); 19 | filters.Display(); 20 | driveStateTrace.Display(); 21 | ygrCmdTrace.Display(); 22 | } 23 | 24 | CDBlockCommandTraceWindow cmdTrace; 25 | CDBlockFiltersWindow filters; 26 | CDDriveStateTraceWindow driveStateTrace; 27 | YGRCommandTraceWindow ygrCmdTrace; 28 | }; 29 | 30 | } // namespace app::ui 31 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/util/process.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | @file 5 | @brief Process-wide utility functions, including priority control and executable path query. 6 | */ 7 | 8 | #include 9 | 10 | namespace util { 11 | 12 | /// @brief Retrieves the path to the executable of this process. 13 | /// @return the path to the executable 14 | std::filesystem::path GetCurrentProcessExecutablePath(); 15 | 16 | /// @brief Enables or disables the priority boost for the current process. 17 | /// @param[in] boost `true` to boost the priority of the current process, `false` to return to normal 18 | void BoostCurrentProcessPriority(bool boost); 19 | 20 | /// @brief Enables or disables the priority boost for the current thread. 21 | /// @param[in] boost `true` to boost the priority of the current thread, `false` to return to normal 22 | void BoostCurrentThreadPriority(bool boost); 23 | 24 | } // namespace util 25 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/scu_window_set.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "scu_dma_trace_window.hpp" 4 | #include "scu_dma_window.hpp" 5 | #include "scu_dsp_window.hpp" 6 | #include "scu_interrupt_trace_window.hpp" 7 | #include "scu_registers_window.hpp" 8 | 9 | namespace app::ui { 10 | 11 | struct SCUWindowSet { 12 | SCUWindowSet(SharedContext &context) 13 | : regs(context) 14 | , dma(context) 15 | , dmaTrace(context) 16 | , intrTrace(context) 17 | , dsp(context) {} 18 | 19 | void DisplayAll() { 20 | regs.Display(); 21 | dma.Display(); 22 | dmaTrace.Display(); 23 | intrTrace.Display(); 24 | dsp.Display(); 25 | } 26 | 27 | SCURegistersWindow regs; 28 | SCUDMAWindow dma; 29 | SCUDMATraceWindow dmaTrace; 30 | SCUInterruptTraceWindow intrTrace; 31 | SCUDSPWindow dsp; 32 | }; 33 | 34 | } // namespace app::ui 35 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/scu_dsp_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace app::ui { 12 | 13 | class SCUDSPWindow : public WindowBase { 14 | public: 15 | SCUDSPWindow(SharedContext &context); 16 | 17 | protected: 18 | void PrepareWindow() override; 19 | void DrawContents() override; 20 | 21 | private: 22 | SCUDSPRegistersView m_regsView; 23 | SCUDSPDisassemblyView m_disasmView; 24 | SCUDSPDataRAMView m_dataRAMView; 25 | SCUDSPDMARegistersView m_dmaRegsView; 26 | SCUDSPDMATraceView m_dmaTraceView; 27 | }; 28 | 29 | } // namespace app::ui 30 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/sh2_cache_window.cpp: -------------------------------------------------------------------------------- 1 | #include "sh2_cache_window.hpp" 2 | 3 | namespace app::ui { 4 | 5 | SH2CacheWindow::SH2CacheWindow(SharedContext &context, bool master) 6 | : SH2WindowBase(context, master) 7 | , m_cacheRegView(context, m_sh2) 8 | , m_cacheEntriesView(context, m_sh2) { 9 | 10 | m_windowConfig.name = fmt::format("{}SH2 cache", master ? 'M' : 'S'); 11 | } 12 | 13 | void SH2CacheWindow::PrepareWindow() { 14 | ImGui::SetNextWindowSizeConstraints(ImVec2(652 * m_context.displayScale, 246 * m_context.displayScale), 15 | ImVec2(652 * m_context.displayScale, FLT_MAX)); 16 | } 17 | 18 | void SH2CacheWindow::DrawContents() { 19 | ImGui::SeparatorText("Cache Control Register"); 20 | m_cacheRegView.Display(); 21 | 22 | ImGui::SeparatorText("Entries"); 23 | m_cacheEntriesView.Display(); 24 | } 25 | 26 | } // namespace app::ui 27 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/media/loader/loader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "loader_result.hpp" 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace ymir::media { 10 | 11 | // Attempts to load a CD image from any of the supported file formats: 12 | // MAME CHD (if provided a .chd file) 13 | // BIN/CUE (if provided a .cue file) 14 | // MDF/MDS (if provided a .mds file) 15 | // IMG/CCD/SUB (if provided a .ccd file) 16 | // ISO (if provided a .iso file) 17 | // Returns true if loading the file (and any auxiliary files) succeeded. 18 | // If this function returns false, the Disc object is invalidated. 19 | // preloadToRAM specifies if the entire disc image should be preloaded into memory. 20 | // cbMsg is the callback for message reporting. 21 | bool LoadDisc(std::filesystem::path path, Disc &disc, bool preloadToRAM, CbLoaderMessage cbMsg); 22 | 23 | } // namespace ymir::media 24 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/media/loader/loader_chd.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "loader_result.hpp" 4 | 5 | #include 6 | 7 | #include 8 | 9 | // CHD (Compressed Hunks of Data) is a file format created by MAME authors to store compressed CD-ROM data. 10 | // 11 | // This loader uses libchdr (https://github.com/rtissera/libchdr) to load Saturn discs from CHD files. 12 | 13 | namespace ymir::media::loader::chd { 14 | 15 | // Attempts to load a CHD file from chdPath into the specified Disc object. 16 | // Returns true if loading all files succeeded. 17 | // If this function returns false, the Disc object is invalidated. 18 | // preloadToRAM specifies if the entire disc image should be preloaded into memory. 19 | // cbMsg is the callback for message reporting. 20 | bool Load(std::filesystem::path chdPath, Disc &disc, bool preloadToRAM, CbLoaderMessage cbMsg); 21 | 22 | } // namespace ymir::media::loader::chd 23 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/debug/cd_drive_tracer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | namespace app { 11 | 12 | struct CDDriveTracer final : ymir::debug::ICDDriveTracer { 13 | struct StateUpdateInfo { 14 | uint32 index; 15 | std::array command; 16 | std::array status; 17 | }; 18 | 19 | void ClearStateUpdates(); 20 | 21 | bool traceStateUpdates = false; 22 | 23 | util::RingBuffer stateUpdates; 24 | 25 | private: 26 | uint32 m_stateUpdateCounter = 0; 27 | 28 | // ------------------------------------------------------------------------- 29 | // ICDDriveTracer implementation 30 | 31 | void RxCommandTxStatus(std::span command, std::span status) final; 32 | }; 33 | 34 | } // namespace app 35 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/sh2/sh2_ubc.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace ymir::sh2 { 4 | 5 | // Channel A 6 | 7 | // addr r/w access init code name 8 | // 140 ? 16? ?? BARAH ??? 9 | // 142 ? 16? ?? BARAL ??? 10 | // 144 ? 16? ?? BAMRAH ??? 11 | // 146 ? 16? ?? BAMRAL ??? 12 | // 148 ? 16? ?? BBRA ??? 13 | 14 | // Channel B 15 | 16 | // 160 ? 16? ?? BARBH ??? 17 | // 162 ? 16? ?? BARBL ??? 18 | // 164 ? 16? ?? BAMRBH ??? 19 | // 166 ? 16? ?? BAMRBL ??? 20 | // 168 ? 16? ?? BBRB ??? 21 | // 170 ? 16? ?? BDRBH ??? 22 | // 172 ? 16? ?? BDRBL ??? 23 | // 174 ? 16? ?? BDMRBH ??? 24 | // 176 ? 16? ?? BDMRBL ??? 25 | // 178 ? 16? ?? BRCR ??? 26 | 27 | } // namespace ymir::sh2 28 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/util/rom_loader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // --------------------------------------------------------------------------------------------------------------------- 7 | // Forward declarations 8 | 9 | namespace ymir { 10 | struct Saturn; 11 | } 12 | 13 | // --------------------------------------------------------------------------------------------------------------------- 14 | 15 | namespace util { 16 | 17 | struct ROMLoadResult { 18 | bool succeeded; 19 | std::string errorMessage; 20 | 21 | static ROMLoadResult Success() { 22 | return {true}; 23 | } 24 | 25 | static ROMLoadResult Fail(std::string message) { 26 | return {false, message}; 27 | } 28 | }; 29 | 30 | ROMLoadResult LoadIPLROM(std::filesystem::path path, ymir::Saturn &saturn); 31 | ROMLoadResult LoadCDBlockROM(std::filesystem::path path, ymir::Saturn &saturn); 32 | 33 | } // namespace util 34 | -------------------------------------------------------------------------------- /docs/dev-notes/finicky-games/vdp-sprite-data-mismatch.txt: -------------------------------------------------------------------------------- 1 | ========================================================== 2 | Games that use mismatching VDP1 and VDP2 sprite data sizes 3 | ========================================================== 4 | 5 | VDP1 has a flag to draw sprites using 8-bit or 16-bit data. 6 | VDP2 has a setting to select the sprite data format from 16 options, half of which are 8 bits and half 16 bits. 7 | The sprite data sizes are supposed to match, but some games misbehave and set them up incorrectly. 8 | When VDP1 draws 16-bit data but VDP2 uses 8-bit sprite types, the readout occurs at 16-bit intervals and only reads the 9 | least significant 8 bits of the sprite data. 10 | No games so far have done the opposite. 11 | 12 | Sega Ages - I Love Mickey Mouse - Fushigi no Oshiro Daibouken & I Love Donald Duck - Georgia Ou no Hihou 13 | Both games set up VDP1 to render sprites with 16-bit data and VDP2 to use sprite data type 8 (an 8-bit format). 14 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/media/binary_reader/binary_reader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace ymir::media { 8 | 9 | // Interface that specifies the contract for reading binary files. 10 | class IBinaryReader { 11 | public: 12 | virtual ~IBinaryReader() = default; 13 | 14 | // Returns the file size. 15 | virtual uintmax_t Size() const = 0; 16 | 17 | // Reads up to size bytes starting at offset from the file into the output buffer. 18 | // Returns the number of bytes actually read, which may be limited by the output buffer size or the amount of data 19 | // available in the file. If the number of bytes read is less than the output size, only the first bytes of the 20 | // buffer are modified; the rest of the buffer is left untouched. 21 | virtual uintmax_t Read(uintmax_t offset, uintmax_t size, std::span output) const = 0; 22 | }; 23 | 24 | } // namespace ymir::media 25 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/media/loader/loader_mdf_mds.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "loader_result.hpp" 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace ymir::media::loader::mdfmds { 10 | 11 | // Alcohol 120% Media Descriptor File/Sidecar files describe a full CD image with multiple sessions and tracks. 12 | // The MDF file contains the binary contents and the MDS file contains the disc structure. 13 | 14 | // Attempts to load an MDS file and its associated MDF file from mdsPath into the specified Disc object. 15 | // Returns true if loading the files succeeded. 16 | // If this function returns false, the Disc object is invalidated. 17 | // preloadToRAM specifies if the entire disc image should be preloaded into memory. 18 | // cbMsg is the callback for message reporting. 19 | bool Load(std::filesystem::path mdsPath, Disc &disc, bool preloadToRAM, CbLoaderMessage cbMsg); 20 | 21 | } // namespace ymir::media::loader::mdfmds 22 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/util/type_traits_ex.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | @file 5 | @brief Useful type traits. 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | namespace util { 12 | 13 | namespace detail { 14 | 15 | template 16 | struct type_base {}; 17 | 18 | template 19 | struct indexed_type_base : type_base {}; 20 | 21 | template 22 | struct indexed_types; 23 | 24 | template 25 | struct indexed_types, T...> : indexed_type_base... {}; 26 | 27 | } // namespace detail 28 | 29 | /// @brief Determines if the given types are all unique. 30 | /// @tparam ...T the types to check 31 | template 32 | concept unique_types = std::is_standard_layout_v, T...>>; 33 | 34 | } // namespace util 35 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/cart/cart_impl_none.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cart_base.hpp" 4 | 5 | namespace ymir::cart { 6 | 7 | class NoCartridge final : public BaseCartridge { 8 | public: 9 | NoCartridge() 10 | : BaseCartridge(0xFFu, CartType::None) {} 11 | 12 | uint8 ReadByte(uint32 address) const override { 13 | return 0xFFu; 14 | } 15 | uint16 ReadWord(uint32 address) const override { 16 | return 0xFFFFu; 17 | } 18 | 19 | void WriteByte(uint32 address, uint8 value) override {} 20 | void WriteWord(uint32 address, uint16 value) override {} 21 | 22 | uint8 PeekByte(uint32 address) const override { 23 | return 0xFFu; 24 | } 25 | uint16 PeekWord(uint32 address) const override { 26 | return 0xFFFFu; 27 | } 28 | 29 | void PokeByte(uint32 address, uint8 value) override {} 30 | void PokeWord(uint32 address, uint16 value) override {} 31 | }; 32 | 33 | } // namespace ymir::cart 34 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/debug/cdblock_tracer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace app { 10 | 11 | struct CDBlockTracer final : ymir::debug::ICDBlockTracer { 12 | struct CommandInfo { 13 | uint32 index; 14 | std::array request; 15 | std::array response; 16 | bool processed; 17 | }; 18 | 19 | void ClearCommands(); 20 | 21 | bool traceCommands = false; 22 | 23 | util::RingBuffer commands; 24 | 25 | private: 26 | uint32 m_commandCounter = 0; 27 | 28 | // ------------------------------------------------------------------------- 29 | // ICDBlockTracer implementation 30 | 31 | void ProcessCommand(uint16 cr1, uint16 cr2, uint16 cr3, uint16 cr4) final; 32 | void ProcessCommandResponse(uint16 cr1, uint16 cr2, uint16 cr3, uint16 cr4) final; 33 | }; 34 | 35 | } // namespace app 36 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/state/state_smpc.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace ymir::state { 8 | 9 | struct SMPCState { 10 | alignas(16) std::array IREG; 11 | alignas(16) std::array OREG; 12 | uint8 COMREG; 13 | uint8 SR; 14 | bool SF; 15 | uint8 PDR1; 16 | uint8 PDR2; 17 | uint8 DDR1; 18 | uint8 DDR2; 19 | uint8 IOSEL; 20 | uint8 EXLE; 21 | 22 | struct INTBACK { 23 | bool getPeripheralData; 24 | bool optimize; 25 | uint8 port1mode; 26 | uint8 port2mode; 27 | 28 | std::vector report; 29 | size_t reportOffset; 30 | bool inProgress; 31 | } intback; 32 | 33 | uint8 busValue; 34 | bool resetDisable; 35 | uint8 commandEventState; // 0 = OnCommandEvent, 1 = OnINTBACKBreakEvent 36 | 37 | sint64 rtcTimestamp; 38 | uint64 rtcSysClockCount; 39 | }; 40 | 41 | } // namespace ymir::state 42 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/debug/ygr_tracer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace app { 10 | 11 | struct YGRTracer final : ymir::debug::IYGRTracer { 12 | struct CommandInfo { 13 | uint32 index; 14 | std::array request; 15 | std::array response; 16 | bool reqValid; 17 | bool resValid; 18 | }; 19 | 20 | void ClearCommands(); 21 | 22 | bool traceCommands = false; 23 | 24 | util::RingBuffer commands; 25 | 26 | private: 27 | uint32 m_commandCounter = 0; 28 | 29 | // ------------------------------------------------------------------------- 30 | // IYGRTracer implementation 31 | 32 | void ReceiveHostCommand(uint16 cr1, uint16 cr2, uint16 cr3, uint16 cr4) final; 33 | void ReceiveCDBlockResponse(uint16 rr1, uint16 rr2, uint16 rr3, uint16 rr4) final; 34 | }; 35 | 36 | } // namespace app 37 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/sh2_dmac_window.cpp: -------------------------------------------------------------------------------- 1 | #include "sh2_dmac_window.hpp" 2 | 3 | #include 4 | 5 | using namespace ymir; 6 | 7 | namespace app::ui { 8 | 9 | SH2DMAControllerWindow::SH2DMAControllerWindow(SharedContext &context, bool master) 10 | : SH2WindowBase(context, master) 11 | , m_dmacRegsView(context, m_sh2) 12 | , m_dmacChannel0View(context, m_sh2.GetProbe().DMAC0(), 0) 13 | , m_dmacChannel1View(context, m_sh2.GetProbe().DMAC1(), 1) { 14 | 15 | m_windowConfig.name = fmt::format("{}SH2 DMA controller", master ? 'M' : 'S'); 16 | m_windowConfig.flags = ImGuiWindowFlags_AlwaysAutoResize; 17 | } 18 | 19 | void SH2DMAControllerWindow::DrawContents() { 20 | ImGui::SeparatorText("Registers"); 21 | m_dmacRegsView.Display(); 22 | 23 | ImGui::SeparatorText("Channel 0"); 24 | m_dmacChannel0View.Display(); 25 | 26 | ImGui::SeparatorText("Channel 1"); 27 | m_dmacChannel1View.Display(); 28 | } 29 | 30 | } // namespace app::ui 31 | -------------------------------------------------------------------------------- /packaging/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(GNUInstallDirs) 2 | include(CMakePackageConfigHelpers) 3 | 4 | if (NOT DEFINED Ymir_INSTALL_CMAKEDIR) 5 | set(Ymir_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/Ymir" 6 | CACHE STRING "Path to Ymir CMake files") 7 | endif () 8 | 9 | if (BUILD_SHARED_LIBS) 10 | set(type shared) 11 | else () 12 | set(type static) 13 | endif () 14 | 15 | install(EXPORT Ymir_Targets 16 | DESTINATION "${Ymir_INSTALL_CMAKEDIR}" 17 | NAMESPACE ymir:: 18 | FILE Ymir-${type}-targets.cmake 19 | COMPONENT Ymir_Development) 20 | 21 | write_basic_package_version_file( 22 | YmirConfigVersion.cmake 23 | COMPATIBILITY SameMajorVersion) 24 | 25 | install(FILES 26 | "${CMAKE_CURRENT_SOURCE_DIR}/YmirConfig.cmake" 27 | "${CMAKE_CURRENT_BINARY_DIR}/YmirConfigVersion.cmake" 28 | DESTINATION "${Ymir_INSTALL_CMAKEDIR}" 29 | COMPONENT Ymir_Development) 30 | 31 | # TODO: add additional CPack variables here 32 | include(CPack) 33 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/peripheral_config_window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace app::ui { 11 | 12 | class PeripheralConfigWindow : public WindowBase { 13 | public: 14 | PeripheralConfigWindow(SharedContext &context); 15 | 16 | void Open(uint32 portIndex, uint32 slotIndex); 17 | 18 | protected: 19 | void PrepareWindow() override; 20 | void DrawContents() override; 21 | 22 | private: 23 | uint32 m_portIndex = 0; 24 | uint32 m_slotIndex = 0; 25 | 26 | ControlPadConfigView m_controlPadView; 27 | AnalogPadConfigView m_analogPadView; 28 | ArcadeRacerConfigView m_arcadeRacerView; 29 | MissionStickConfigView m_missionStickView; 30 | }; 31 | 32 | } // namespace app::ui 33 | -------------------------------------------------------------------------------- /libs/ymir-core/src/ymir/hw/cart/cart_impl_bup.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace ymir::cart { 8 | 9 | static constexpr uint8 GetCartID(uint32 bupSize) { 10 | if (bupSize <= 512_KiB) { 11 | return 0x21; // 4 Mbit Backup RAM 12 | } else if (bupSize <= 1_MiB) { 13 | return 0x22; // 8 Mbit Backup RAM 14 | } else if (bupSize <= 2_MiB) { 15 | return 0x23; // 16 Mbit Backup RAM 16 | } else { 17 | return 0x24; // 32 Mbit Backup RAM 18 | } 19 | } 20 | 21 | BackupMemoryCartridge::BackupMemoryCartridge(bup::BackupMemory &&backupRAM) 22 | : BaseCartridge(GetCartID(backupRAM.Size()), CartType::BackupMemory) 23 | , m_backupRAM(std::move(backupRAM)) {} 24 | 25 | void BackupMemoryCartridge::CopyBackupMemoryFrom(const bup::IBackupMemory &backupRAM) { 26 | ChangeID(GetCartID(backupRAM.Size())); 27 | m_backupRAM.CopyFrom(backupRAM); 28 | } 29 | 30 | } // namespace ymir::cart 31 | -------------------------------------------------------------------------------- /vcpkg-triplets/x64-win-llvm-static-md.cmake: -------------------------------------------------------------------------------- 1 | # This triplet is tested in vcpkg ci via https://github.com/microsoft/vcpkg/pull/25897 2 | set(VCPKG_TARGET_ARCHITECTURE x64) 3 | set(VCPKG_CRT_LINKAGE dynamic) 4 | set(VCPKG_LIBRARY_LINKAGE static) 5 | 6 | ## Toolchain setup 7 | set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE "${CMAKE_CURRENT_LIST_DIR}/x64-win-llvm/x64-win-llvm.toolchain.cmake") 8 | set(VCPKG_LOAD_VCVARS_ENV ON) # Setting VCPKG_CHAINLOAD_TOOLCHAIN_FILE deactivates automatic vcvars setup so reenable it! 9 | 10 | if(DEFINED VCPKG_PLATFORM_TOOLSET) 11 | set(VCPKG_PLATFORM_TOOLSET ClangCL) 12 | endif() 13 | set(VCPKG_ENV_PASSTHROUGH_UNTRACKED "LLVMInstallDir;LLVMToolsVersion") # For the ClangCL toolset 14 | set(VCPKG_QT_TARGET_MKSPEC win32-clang-msvc) # For Qt5 15 | 16 | ## Policy settings 17 | set(VCPKG_POLICY_SKIP_ARCHITECTURE_CHECK enabled) 18 | set(VCPKG_POLICY_SKIP_DUMPBIN_CHECKS enabled) 19 | 20 | include("${CMAKE_CURRENT_LIST_DIR}/x64-win-llvm/extra_setup.cmake") 21 | include("${CMAKE_CURRENT_LIST_DIR}/x64-win-llvm/port_specialization.cmake") 22 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/sh1/sh1_internal_callbacks.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | @file 5 | @brief Internal callback definitions used by the SH-1. 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | 12 | namespace ymir::sh1 { 13 | 14 | /// @brief Receive a bit from one of the SH-1's SCI channels. 15 | using CbSerialRx = util::RequiredCallback; 16 | 17 | /// @brief Send a bit to one of the SH-1's SCI channels. 18 | using CbSerialTx = util::RequiredCallback; 19 | 20 | /// @brief Invoked to raise an IRQ signal on the SH-1. 21 | using CBAssertIRQ = util::RequiredCallback; 22 | 23 | /// @brief Invoked to set the DREQ0/1# signals on the SH-1. 24 | using CBSetDREQn = util::RequiredCallback; 25 | 26 | /// @brief Invoked to step a DMA channel on the SH-1 until the specified number of bytes have been transferred or the 27 | /// DREQ# signal is deasserted. 28 | using CBStepDMAC = util::RequiredCallback; 29 | 30 | } // namespace ymir::sh1 31 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/window_base.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace app::ui { 10 | 11 | class WindowBase { 12 | public: 13 | WindowBase(SharedContext &context); 14 | virtual ~WindowBase() = default; 15 | 16 | void Display(); 17 | 18 | void RequestFocus(); 19 | 20 | bool Open = false; 21 | 22 | protected: 23 | SharedContext &m_context; 24 | 25 | struct WindowConfig { 26 | std::string name; 27 | ImGuiWindowFlags flags = ImGuiWindowFlags_None; 28 | bool allowClosingWithGamepad = true; 29 | }; 30 | 31 | WindowConfig m_windowConfig; 32 | 33 | // Invoked before ImGui::Begin(...). 34 | // Can be used to set up window constraints, update name and flags dynamically, etc. 35 | virtual void PrepareWindow() {} 36 | 37 | // Draws the window's contents. 38 | virtual void DrawContents() = 0; 39 | 40 | private: 41 | bool m_focusRequested = false; 42 | }; 43 | 44 | } // namespace app::ui 45 | -------------------------------------------------------------------------------- /cmake/VSHelpers.cmake: -------------------------------------------------------------------------------- 1 | # Add Visual Studio filters to better organize the code 2 | function(vs_set_filters) 3 | cmake_parse_arguments(ARG "" "TARGET;BASE_DIR" "" ${ARGN}) 4 | if(MSVC) 5 | get_target_property(VS_TARGET_SOURCES ${ARG_TARGET} SOURCES) 6 | string(REPLACE "\\" "/" ARG_BASE_DIR "${ARG_BASE_DIR}") 7 | if(NOT "${ARG_BASE_DIR}" MATCHES "/$") 8 | string(APPEND ARG_BASE_DIR "/") 9 | endif() 10 | 11 | # Organize files into folders (filters) mirroring the file system structure 12 | foreach(FILE IN ITEMS ${VS_TARGET_SOURCES}) 13 | # Get file directory 14 | get_filename_component(FILE_DIR "${FILE}" DIRECTORY) 15 | 16 | # Normalize path separators 17 | string(REPLACE "\\" "/" FILE_DIR "${FILE_DIR}") 18 | 19 | # Erase base directory from the beginning of the path 20 | string(REGEX REPLACE "^${ARG_BASE_DIR}" "" FILE_DIR "${FILE_DIR}") 21 | 22 | # Put files into folders mirroring the file system structure 23 | source_group("${FILE_DIR}" FILES "${FILE}") 24 | endforeach() 25 | endif() 26 | endfunction() 27 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/widgets/settings_widgets.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui::widgets { 6 | 7 | namespace settings::system { 8 | 9 | void EmulateSH2Cache(SharedContext &ctx); 10 | 11 | } // namespace settings::system 12 | 13 | namespace settings::video { 14 | 15 | void DisplayRotation(SharedContext &ctx, bool newLine = false); 16 | void Deinterlace(SharedContext &ctx); 17 | void TransparentMeshes(SharedContext &ctx); 18 | void ThreadedVDP(SharedContext &ctx); 19 | 20 | } // namespace settings::video 21 | 22 | namespace settings::audio { 23 | 24 | void InterpolationMode(SharedContext &ctx); 25 | void StepGranularity(SharedContext &ctx); 26 | 27 | std::string StepGranularityToString(uint32 stepGranularity); 28 | 29 | } // namespace settings::audio 30 | 31 | namespace settings::cdblock { 32 | 33 | void CDReadSpeed(SharedContext &ctx); 34 | void CDBlockLLE(SharedContext &ctx); 35 | 36 | } // namespace settings::cdblock 37 | 38 | } // namespace app::ui::widgets 39 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/debug/cd_drive_tracer_base.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | @file 5 | @brief Defines `ymir::debug::ICDDriveTracer`, the LLE CD drive tracer interface. 6 | */ 7 | 8 | #include 9 | 10 | #include 11 | 12 | namespace ymir::debug { 13 | 14 | /// @brief Interface for LLE CD drive tracers. 15 | /// 16 | /// Must be implemented by users of the core library. 17 | /// 18 | /// Attach to an instance of `ymir::cdblock::CDDrive` with its `UseTracer(ICDDriveTracer *)` method. 19 | struct ICDDriveTracer { 20 | /// @brief Default virtual destructor. Required for inheritance. 21 | virtual ~ICDDriveTracer() = default; 22 | 23 | /// @brief Invoked when the CD drive receives a command and transmits its status through the serial interface. 24 | /// @param[in] command the command sent to the CD drive 25 | /// @param[in] status the status sent to the SH-1 26 | virtual void RxCommandTxStatus(std::span command, std::span status) {} 27 | }; 28 | 29 | } // namespace ymir::debug 30 | -------------------------------------------------------------------------------- /cmake/CopyRuntimeDLLs.cmake: -------------------------------------------------------------------------------- 1 | # Taken from https://github.com/alexreinking/cmrk/blob/main/src/CopyRuntimeDLLs.cmake 2 | function(cmrk_copy_runtime_dlls target) 3 | # Not a DLL system 4 | if (NOT CMAKE_IMPORT_LIBRARY_SUFFIX) 5 | return() 6 | endif () 7 | 8 | # Only applies to dynamic libraries 9 | set(allowed_types SHARED_LIBRARY MODULE_LIBRARY EXECUTABLE) 10 | get_property(type TARGET ${target} PROPERTY TYPE) 11 | if (NOT type IN_LIST allowed_types) 12 | return() 13 | endif () 14 | 15 | # Don't add the command twice 16 | get_property(has_cmd TARGET "${target}" PROPERTY _${CMAKE_CURRENT_FUNCTION}_has_cmd) 17 | if (has_cmd) 18 | return() 19 | endif () 20 | 21 | set(dlls "$") 22 | set(dir "$") 23 | add_custom_command( 24 | TARGET "${target}" POST_BUILD 25 | COMMAND "$<$:${CMAKE_COMMAND};-E;copy;${dlls};${dir}>" 26 | COMMAND_EXPAND_LISTS 27 | ) 28 | 29 | set_property(TARGET "${target}" PROPERTY _${CMAKE_CURRENT_FUNCTION}_has_cmd 1) 30 | endfunction() 31 | -------------------------------------------------------------------------------- /apps/ymdasm/src/utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | template 10 | std::optional ParseHex(std::string_view opcode) { 11 | static constexpr size_t kHexOpcodeLength = sizeof(T) * 2; 12 | 13 | if (opcode.size() > kHexOpcodeLength) { 14 | fmt::println("Value \"{}\" exceeds maximum length of {} hex digits", opcode, kHexOpcodeLength); 15 | return std::nullopt; 16 | } 17 | 18 | T value = 0; 19 | for (char c : opcode) { 20 | if (c >= '0' && c <= '9') { 21 | value = (value << 4ull) + c - '0'; 22 | } else if (c >= 'A' && c <= 'F') { 23 | value = (value << 4ull) + c - 'A' + 10; 24 | } else if (c >= 'a' && c <= 'f') { 25 | value = (value << 4ull) + c - 'a' + 10; 26 | } else { 27 | fmt::println("Value \"{}\" is not a valid hexadecimal number", opcode); 28 | return std::nullopt; 29 | } 30 | } 31 | return value; 32 | } 33 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/scu_dma_window.cpp: -------------------------------------------------------------------------------- 1 | #include "scu_dma_window.hpp" 2 | 3 | namespace app::ui { 4 | 5 | SCUDMAWindow::SCUDMAWindow(SharedContext &context) 6 | : WindowBase(context) 7 | , m_dmaRegsView(context) 8 | , m_dmaStateView(context) { 9 | 10 | m_windowConfig.name = "SCU DMA"; 11 | m_windowConfig.flags = ImGuiWindowFlags_AlwaysAutoResize; 12 | } 13 | 14 | void SCUDMAWindow::DrawContents() { 15 | ImGui::BeginGroup(); 16 | 17 | if (ImGui::BeginTable("scu_dma", 3, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_BordersInnerV)) { 18 | ImGui::TableNextRow(); 19 | for (uint32 i = 0; i < 3; i++) { 20 | if (ImGui::TableNextColumn()) { 21 | ImGui::SeparatorText(fmt::format("Channel {}", i).c_str()); 22 | m_dmaRegsView.Display(i); 23 | ImGui::Separator(); 24 | m_dmaStateView.Display(i); 25 | } 26 | } 27 | 28 | ImGui::EndTable(); 29 | } 30 | 31 | ImGui::EndGroup(); 32 | } 33 | 34 | } // namespace app::ui 35 | -------------------------------------------------------------------------------- /apps/ymdasm/src/ansi.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define ANSI_ESCAPE "\x1b" 4 | 5 | #define ANSI_RESET ANSI_ESCAPE "[0m" 6 | 7 | #define ANSI_FGCOLOR_BLACK ANSI_ESCAPE "[30m" 8 | #define ANSI_FGCOLOR_RED ANSI_ESCAPE "[31m" 9 | #define ANSI_FGCOLOR_GREEN ANSI_ESCAPE "[32m" 10 | #define ANSI_FGCOLOR_YELLOW ANSI_ESCAPE "[33m" 11 | #define ANSI_FGCOLOR_BLUE ANSI_ESCAPE "[34m" 12 | #define ANSI_FGCOLOR_MAGENTA ANSI_ESCAPE "[35m" 13 | #define ANSI_FGCOLOR_CYAN ANSI_ESCAPE "[36m" 14 | #define ANSI_FGCOLOR_WHITE ANSI_ESCAPE "[37m" 15 | 16 | #define ANSI_FGCOLOR_BRIGHT_BLACK ANSI_ESCAPE "[1;30m" 17 | #define ANSI_FGCOLOR_BRIGHT_RED ANSI_ESCAPE "[1;31m" 18 | #define ANSI_FGCOLOR_BRIGHT_GREEN ANSI_ESCAPE "[1;32m" 19 | #define ANSI_FGCOLOR_BRIGHT_YELLOW ANSI_ESCAPE "[1;33m" 20 | #define ANSI_FGCOLOR_BRIGHT_BLUE ANSI_ESCAPE "[1;34m" 21 | #define ANSI_FGCOLOR_BRIGHT_MAGENTA ANSI_ESCAPE "[1;35m" 22 | #define ANSI_FGCOLOR_BRIGHT_CYAN ANSI_ESCAPE "[1;36m" 23 | #define ANSI_FGCOLOR_BRIGHT_WHITE ANSI_ESCAPE "[1;37m" 24 | 25 | #define ANSI_FGCOLOR_24B(r, g, b) ANSI_ESCAPE "[38;2;" #r ";" #g ";" #b "m" 26 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/debug/scsp_tracer_base.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | @file 5 | @brief Defines `ymir::debug::ISCSPTracer`, the SCSP tracer interface. 6 | */ 7 | 8 | #include 9 | 10 | namespace ymir::debug { 11 | 12 | /// @brief Interface for SCSP tracers. 13 | /// 14 | /// Must be implemented by users of the core library. 15 | /// 16 | /// Attach to an instance of `ymir::cdblock::CDBlock` with its `UseTracer(ISCSPTracer *)` method. 17 | struct ISCSPTracer { 18 | /// @brief Default virtual destructor. Required for inheritance. 19 | virtual ~ISCSPTracer() = default; 20 | 21 | /// @brief Invoked for each slot when the SCSP outputs a sample. 22 | /// @param[in] index the slot index 23 | /// @param[in] left the slot output sample 24 | virtual void SlotSample(uint32 index, sint16 sample) = 0; 25 | 26 | /// @brief Invoked when KYONEX is processed. 27 | /// @param[in] slotsMask a bitmask with state of KYONB for each slot 28 | virtual void KeyOnExecute(uint32 slotsMask) = 0; 29 | }; 30 | 31 | } // namespace ymir::debug 32 | -------------------------------------------------------------------------------- /docs/dev-notes/finicky-games/cdblock-nontrivial-filters.txt: -------------------------------------------------------------------------------- 1 | ======================================================== 2 | Games that use nontrivial CD Block filter configurations 3 | ======================================================== 4 | 5 | NiGHTS into Dreams... 6 | Christmas NiGHTS into Dreams... 7 | The intro FMV is contained in a CD-ROM Mode 2 track with special subheader information that is used by the game to 8 | split the video and audio tracks -- specifically, they are encoded in the file number field of the subheader. 9 | The game then sets up two filters: 10 | 00 -> filter by file number = 1 and frame addresses 4DDF to 4F19. Pass = output 0; fail = filter 1. 11 | 01 -> filter by file number = 2 and frame addresses 4DE1 to 4EF1. Pass = output 1; fail = discard. 12 | With this, the game can process the two streams separately. 13 | File number 1 contains a 320x184 24bpp @ 30 fps Cinepak video stream. 14 | File number 2 contains a 32000 Hz, 2 channels, 16-bit signed PCM integer AIFF audio stream at 512 kbps. 15 | The entire track 2 of the disc is in this format. 16 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/state/state_scsp_dsp.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace ymir::state { 8 | 9 | struct SCSPDSP { 10 | alignas(16) std::array MPRO; 11 | alignas(16) std::array TEMP; 12 | alignas(16) std::array MEMS; 13 | alignas(16) std::array COEF; 14 | alignas(16) std::array MADRS; 15 | alignas(16) std::array MIXS; 16 | alignas(16) std::array EFREG; 17 | alignas(16) std::array EXTS; 18 | 19 | uint8 MIXSGen; 20 | uint16 MIXSNull; 21 | 22 | uint8 RBP; 23 | uint8 RBL; 24 | 25 | uint8 PC; 26 | sint32 INPUTS; 27 | 28 | uint32 SFT_REG; 29 | uint16 FRC_REG; 30 | uint32 Y_REG; 31 | uint16 ADRS_REG; 32 | 33 | uint16 MDEC_CT; 34 | 35 | bool readPending; 36 | bool readNOFL; 37 | uint32 readValue; 38 | 39 | bool writePending; 40 | uint16 writeValue; 41 | 42 | uint32 readWriteAddr; 43 | }; 44 | 45 | } // namespace ymir::state 46 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/util/os_features.cpp: -------------------------------------------------------------------------------- 1 | #include "os_features.hpp" 2 | 3 | #if defined(_WIN32) 4 | #include 5 | #include 6 | #endif 7 | 8 | namespace util::os { 9 | 10 | void ConfigureWindowDecorations(SDL_Window *window) { 11 | #if defined(_WIN32) 12 | SDL_PropertiesID props = SDL_GetWindowProperties(window); 13 | auto hwnd = 14 | static_cast(SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, INVALID_HANDLE_VALUE)); 15 | DWM_WINDOW_CORNER_PREFERENCE cornerPref = DWMWCP_DONOTROUND; 16 | DwmSetWindowAttribute(hwnd, DWMWA_WINDOW_CORNER_PREFERENCE, &cornerPref, sizeof(cornerPref)); 17 | #endif 18 | } 19 | 20 | void SetFileHidden(std::filesystem::path path, bool hidden) { 21 | #if defined(_WIN32) 22 | DWORD attrs = GetFileAttributesW(path.wstring().c_str()); 23 | const bool isHidden = attrs & FILE_ATTRIBUTE_HIDDEN; 24 | if (isHidden != hidden) { 25 | attrs ^= FILE_ATTRIBUTE_HIDDEN; 26 | SetFileAttributesW(path.wstring().c_str(), attrs); 27 | } 28 | #endif 29 | } 30 | 31 | } // namespace util::os 32 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/util/file_loader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | namespace util { 6 | 7 | std::vector LoadFile(std::filesystem::path romPath) { 8 | std::vector data{}; 9 | std::ifstream stream{romPath, std::ios::binary | std::ios::ate}; 10 | if (stream.is_open()) { 11 | auto size = stream.tellg(); 12 | stream.seekg(0, std::ios::beg); 13 | data.resize(size); 14 | stream.read(reinterpret_cast(data.data()), size); 15 | } 16 | return data; 17 | } 18 | 19 | std::vector LoadFile(std::filesystem::path romPath, std::error_code &error) { 20 | std::vector data{}; 21 | std::ifstream stream{romPath, std::ios::binary | std::ios::ate}; 22 | if (!stream) { 23 | error.assign(errno, std::generic_category()); 24 | } else { 25 | auto size = stream.tellg(); 26 | stream.seekg(0, std::ios::beg); 27 | data.resize(size); 28 | stream.read(reinterpret_cast(data.data()), size); 29 | } 30 | return data; 31 | } 32 | 33 | } // namespace util 34 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/res/ymir.rc: -------------------------------------------------------------------------------- 1 | IDI_ICON1 ICON "ymir.ico" 2 | 3 | 1 VERSIONINFO 4 | FILEVERSION Ymir_VERSION_MAJOR,Ymir_VERSION_MINOR,Ymir_VERSION_PATCH,0 5 | PRODUCTVERSION Ymir_VERSION_MAJOR,Ymir_VERSION_MINOR,Ymir_VERSION_PATCH,0 6 | FILEFLAGSMASK 0x3fL 7 | #ifdef _DEBUG 8 | FILEFLAGS 0x1L 9 | #else 10 | FILEFLAGS 0x0L 11 | #endif 12 | FILEOS 0x40004L 13 | FILETYPE 0x1L 14 | FILESUBTYPE 0x0L 15 | BEGIN 16 | BLOCK "StringFileInfo" 17 | BEGIN 18 | BLOCK "040904b0" 19 | BEGIN 20 | VALUE "CompanyName", "StrikerX3\0" 21 | VALUE "FileDescription", "Ymir - A Sega Saturn Emulator\0" 22 | VALUE "FileVersion", Ymir_VERSION "\0" 23 | VALUE "InternalName", "Ymir\0" 24 | VALUE "LegalCopyright", "Copyright (C) 2024-2025 Ymir contributors\0" 25 | VALUE "OriginalFilename", "ymir-sdl3.exe\0" 26 | VALUE "ProductName", "Ymir\0" 27 | VALUE "ProductVersion", Ymir_VERSION "\0" 28 | END 29 | END 30 | BLOCK "VarFileInfo" 31 | BEGIN 32 | VALUE "Translation", 0x409, 1200 33 | END 34 | END 35 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/debug/scsp_tracer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace app { 10 | 11 | struct SCSPTracer final : ymir::debug::ISCSPTracer { 12 | struct KeyOnExecuteInfo { 13 | uint64 sampleCounter; 14 | uint32 slotsMask; 15 | }; 16 | 17 | std::array, 32> slotOutputs; 18 | util::RingBuffer kyonexTrace; 19 | 20 | uint64 GetSampleCounter() const noexcept { 21 | return m_sampleCounter; 22 | } 23 | 24 | void ClearAll() { 25 | for (auto &slot : slotOutputs) { 26 | slot.Clear(); 27 | } 28 | m_sampleCounter = 0; 29 | } 30 | 31 | private: 32 | uint64 m_sampleCounter = 0; 33 | 34 | // ------------------------------------------------------------------------- 35 | // ISCSPTracer implementation 36 | 37 | void SlotSample(uint32 index, sint16 output) final; 38 | void KeyOnExecute(uint32 slotsMask) final; 39 | }; 40 | 41 | } // namespace app 42 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/debug/vdp2_layer_visibility_view.cpp: -------------------------------------------------------------------------------- 1 | #include "vdp2_layer_visibility_view.hpp" 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | 9 | using namespace ymir; 10 | 11 | namespace app::ui { 12 | 13 | VDP2LayerVisibilityView::VDP2LayerVisibilityView(SharedContext &context, vdp::VDP &vdp) 14 | : m_context(context) 15 | , m_vdp(vdp) {} 16 | 17 | void VDP2LayerVisibilityView::Display() { 18 | auto checkbox = [&](const char *name, vdp::Layer layer) { 19 | bool enabled = m_vdp.IsLayerEnabled(layer); 20 | if (ImGui::Checkbox(name, &enabled)) { 21 | m_context.EnqueueEvent(events::emu::debug::SetLayerEnabled(layer, enabled)); 22 | } 23 | }; 24 | checkbox("Sprite", vdp::Layer::Sprite); 25 | checkbox("RBG0", vdp::Layer::RBG0); 26 | checkbox("NBG0/RBG1", vdp::Layer::NBG0_RBG1); 27 | checkbox("NBG1/EXBG", vdp::Layer::NBG1_EXBG); 28 | checkbox("NBG2", vdp::Layer::NBG2); 29 | checkbox("NBG3", vdp::Layer::NBG3); 30 | } 31 | 32 | } // namespace app::ui 33 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/views/settings/cartridge_settings_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "settings_view_base.hpp" 4 | 5 | namespace app::ui { 6 | 7 | class CartridgeSettingsView : public SettingsViewBase { 8 | public: 9 | CartridgeSettingsView(SharedContext &context); 10 | 11 | void Display(); 12 | 13 | private: 14 | void DrawBackupRAMSettings(); 15 | void DrawDRAMSettings(); 16 | void DrawROMSettings(); 17 | 18 | static void ProcessLoadBackupImage(void *userdata, std::filesystem::path file, int filter); 19 | static void ProcessLoadBackupImageError(void *userdata, const char *message, int filter); 20 | 21 | void LoadBackupImage(std::filesystem::path file); 22 | void ShowLoadBackupImageError(const char *message); 23 | 24 | static void ProcessLoadROMImage(void *userdata, std::filesystem::path file, int filter); 25 | static void ProcessLoadROMImageError(void *userdata, const char *message, int filter); 26 | 27 | void LoadROMImage(std::filesystem::path file); 28 | void ShowLoadROMImageError(const char *message); 29 | }; 30 | 31 | } // namespace app::ui 32 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/windows/debug/sh2_divu_window.cpp: -------------------------------------------------------------------------------- 1 | #include "sh2_divu_window.hpp" 2 | 3 | using namespace ymir; 4 | 5 | namespace app::ui { 6 | 7 | SH2DivisionUnitWindow::SH2DivisionUnitWindow(SharedContext &context, bool master) 8 | : SH2WindowBase(context, master) 9 | , m_divuRegsView(context, m_sh2) 10 | , m_divuStatsView(context, m_tracer) 11 | , m_divuTraceView(context, m_tracer) { 12 | 13 | m_windowConfig.name = fmt::format("{}SH2 division unit (DIVU)", master ? 'M' : 'S'); 14 | } 15 | 16 | void SH2DivisionUnitWindow::PrepareWindow() { 17 | ImGui::SetNextWindowSizeConstraints(ImVec2(570 * m_context.displayScale, 356 * m_context.displayScale), 18 | ImVec2(570 * m_context.displayScale, FLT_MAX)); 19 | } 20 | 21 | void SH2DivisionUnitWindow::DrawContents() { 22 | ImGui::SeparatorText("Registers"); 23 | m_divuRegsView.Display(); 24 | 25 | ImGui::SeparatorText("Statistics"); 26 | m_divuStatsView.Display(); 27 | 28 | ImGui::SeparatorText("Trace"); 29 | m_divuTraceView.Display(); 30 | } 31 | 32 | } // namespace app::ui 33 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/util/regions.cpp: -------------------------------------------------------------------------------- 1 | #include "regions.hpp" 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace util { 8 | 9 | static constexpr struct { 10 | char charCode; 11 | const char *name; 12 | } kRegions[] = { 13 | {/*0x0*/ '?', "Invalid"}, {/*0x1*/ 'J', "Japan"}, 14 | {/*0x2*/ 'T', "Asia NTSC"}, {/*0x3*/ '?', "Invalid"}, 15 | {/*0x4*/ 'U', "North America"}, {/*0x5*/ 'B', "Central/South America NTSC"}, 16 | {/*0x6*/ 'K', "Korea"}, {/*0x7*/ '?', "Invalid"}, 17 | {/*0x8*/ '?', "Invalid"}, {/*0x9*/ '?', "Invalid"}, 18 | {/*0xA*/ 'A', "Asia PAL"}, {/*0xB*/ '?', "Invalid"}, 19 | {/*0xC*/ 'E', "Europe PAL"}, {/*0xD*/ 'L', "Central/South America PAL"}, 20 | {/*0xE*/ '?', "Invalid"}, {/*0xF*/ '?', "Invalid"}, 21 | }; 22 | 23 | std::string RegionToString(ymir::core::config::sys::Region region) { 24 | auto areaCode = static_cast(region); 25 | areaCode &= 0xF; 26 | return fmt::format("({:c}) {}", kRegions[areaCode].charCode, kRegions[areaCode].name); 27 | } 28 | 29 | } // namespace util 30 | -------------------------------------------------------------------------------- /libs/ymir-core/src/ymir/core/hash.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | namespace ymir { 7 | 8 | XXH128Hash CalcHash128(const void *input, size_t len, uint64_t seed) { 9 | const XXH128_hash_t hash = XXH128(input, len, seed); 10 | XXH128_canonical_t canonicalHash{}; 11 | XXH128_canonicalFromHash(&canonicalHash, hash); 12 | 13 | XXH128Hash out{}; 14 | std::copy_n(canonicalHash.digest, out.size(), out.begin()); 15 | return out; 16 | } 17 | 18 | std::string ToString(const XXH128Hash &hash) { 19 | fmt::memory_buffer buf{}; 20 | auto inserter = std::back_inserter(buf); 21 | for (uint8_t b : hash) { 22 | fmt::format_to(inserter, "{:02X}", b); 23 | } 24 | return fmt::to_string(buf); 25 | } 26 | 27 | XXH128Hash MakeXXH128Hash(uint64_t hi, uint64_t lo) { 28 | XXH128Hash out{}; 29 | for (uint64_t byte = 0ull; byte < 8ull; ++byte) { 30 | out[byte + 0] = hi >> ((7ull - byte) * 8ull); 31 | out[byte + 8] = lo >> ((7ull - byte) * 8ull); 32 | } 33 | return out; 34 | } 35 | 36 | } // namespace ymir 37 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/events/emu_debug_event_factory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "emu_event.hpp" 8 | 9 | #include 10 | 11 | namespace app::events::emu::debug { 12 | 13 | EmuEvent ExecuteSH2Division(bool master, bool div64); 14 | 15 | EmuEvent WriteMainMemory(uint32 address, uint8 value, bool enableSideEffects); 16 | EmuEvent WriteSH1Memory(uint32 address, uint8 value, bool enableSideEffects); 17 | EmuEvent WriteSH2Memory(uint32 address, uint8 value, bool enableSideEffects, bool master, bool bypassCache); 18 | 19 | EmuEvent AddSH2Breakpoint(bool master, uint32 address); 20 | EmuEvent RemoveSH2Breakpoint(bool master, uint32 address); 21 | EmuEvent ReplaceSH2Breakpoints(bool master, const std::set &addresses); 22 | EmuEvent ClearSH2Breakpoints(bool master); 23 | 24 | EmuEvent SetLayerEnabled(ymir::vdp::Layer layer, bool enabled); 25 | 26 | EmuEvent VDP2SetCRAMColor555(uint32 index, ymir::vdp::Color555 color); 27 | EmuEvent VDP2SetCRAMColor888(uint32 index, ymir::vdp::Color888 color); 28 | 29 | } // namespace app::events::emu::debug 30 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/widgets/cartridge_widgets.cpp: -------------------------------------------------------------------------------- 1 | #include "cartridge_widgets.hpp" 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace app::ui::widgets { 8 | 9 | void CartridgeInfo(SharedContext &ctx) { 10 | using namespace ymir; 11 | 12 | std::unique_lock lock{ctx.locks.cart}; 13 | auto &cart = ctx.saturn.GetCartridge(); 14 | switch (cart.GetType()) { 15 | case cart::CartType::None: ImGui::TextUnformatted("None"); break; 16 | case cart::CartType::BackupMemory: // 17 | { 18 | auto &bupCart = *cart.As(); 19 | ImGui::Text("%u Mbit Backup RAM", bupCart.GetBackupMemory().Size() * 8u / 1024u / 1024u); 20 | break; 21 | } 22 | case cart::CartType::DRAM8Mbit: ImGui::TextUnformatted("8 Mbit DRAM"); break; 23 | case cart::CartType::DRAM32Mbit: ImGui::TextUnformatted("32 Mbit DRAM"); break; 24 | case cart::CartType::DRAM48Mbit: ImGui::TextUnformatted("48 Mbit DRAM"); break; 25 | case cart::CartType::ROM: ImGui::TextUnformatted("16 Mbit ROM"); break; 26 | } 27 | } 28 | 29 | } // namespace app::ui::widgets 30 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/util/backup_datetime.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | @file 5 | @brief Utilities for dealing with Saturn backup memory date/time values. 6 | */ 7 | 8 | #include 9 | 10 | namespace util { 11 | 12 | /// @brief A structured representation of the Saturn's backup memory date/time value. 13 | struct BackupDateTime { 14 | /// @brief Constructs a backup memory date/time value from a raw value. 15 | /// @param[in] raw the raw value 16 | BackupDateTime(uint32 raw) noexcept; 17 | 18 | uint32 year; ///< The full year (e.g. 1994, 2025, etc.) 19 | uint32 month; ///< The month of the year, from 1 to 12 20 | uint32 day; ///< The day of the month, from 1 to 31 21 | uint32 hour; ///< The hour of the day, from 00 (midnight) to 23 22 | uint32 minute; ///< The minutes in the hour, from 00 to 59 23 | 24 | /// @brief Converts the date/time values from this struct into the raw representation used by the Saturn. 25 | /// @return the raw representation of this backup memory date/time 26 | [[nodiscard]] uint32 ToRaw() const noexcept; 27 | }; 28 | 29 | } // namespace util 30 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/media/binary_reader/binary_reader_zero.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "binary_reader.hpp" 4 | 5 | #include 6 | #include 7 | 8 | namespace ymir::media { 9 | 10 | // Implementation of IBinaryReader that reads all zeros. 11 | class ZeroBinaryReader final : public IBinaryReader { 12 | public: 13 | ZeroBinaryReader(uintmax_t size) 14 | : m_size(size) {} 15 | 16 | // Retrieves the total size of the combined BinaryReaders 17 | uintmax_t Size() const final { 18 | return m_size; 19 | } 20 | 21 | uintmax_t Read(uintmax_t offset, uintmax_t size, std::span output) const final { 22 | if (offset >= m_size) { 23 | return 0; 24 | } 25 | 26 | // Limit size to the smallest of the requested size and the output buffer size 27 | size = std::min(size, m_size - offset); 28 | size = std::min(size, output.size()); 29 | 30 | // Fill output with zeros 31 | std::fill_n(output.begin(), size, 0); 32 | return size; 33 | } 34 | 35 | private: 36 | uintmax_t m_size = 0; 37 | }; 38 | 39 | } // namespace ymir::media 40 | -------------------------------------------------------------------------------- /vcpkg-triplets/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Alexander Neumann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/media/frame_address.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | @file 5 | @brief Frame address utilities. 6 | */ 7 | 8 | #include 9 | 10 | namespace ymir::media { 11 | 12 | /// @brief Converts a MM:SS:FF timestamp into the corresponding frame address. 13 | /// @param[in] min the minutes 14 | /// @param[in] sec the seconds 15 | /// @param[in] frame the frames 16 | /// @return the frame address corresponding to the given MM:SS:FF 17 | inline uint32 TimestampToFrameAddress(uint32 min, uint32 sec, uint32 frame) { 18 | return min * 75 * 60 + sec * 75 + frame; 19 | } 20 | 21 | /// @brief Converts a MM:SS:FF timestamp into a file offset. 22 | /// @param[in] min the minutes 23 | /// @param[in] sec the seconds 24 | /// @param[in] frame the frames 25 | /// @param[in] sectorSize the sector size 26 | /// @return the offset from the start of the file to the start of the sector corresponding to MM:SS:FF 27 | inline uintmax_t TimestampToFileOffset(uint32 min, uint32 sec, uint32 frame, uint32 sectorSize) { 28 | return static_cast(TimestampToFrameAddress(min, sec, frame)) * sectorSize; 29 | } 30 | 31 | } // namespace ymir::media 32 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/widgets/savestate_widgets.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace app::ui::widgets { 6 | 7 | struct RewindBarStyle { 8 | struct Colors { 9 | ImVec4 background; 10 | ImVec4 border; 11 | ImVec4 bar; 12 | ImVec4 secondsMarker; 13 | ImVec4 text; 14 | } colors; 15 | 16 | float padding = 10.0f; 17 | float height = 50.0f; 18 | float rounding = 2.0f; 19 | float borderThickness = 2.0f; 20 | float secondsMarkerThickness = 1.5f; 21 | }; 22 | 23 | #define C(r, g, b, a) (r / 255.0f), (g / 255.0f), (b / 255.0f), a 24 | 25 | inline RewindBarStyle g_defaultRewindBarStyle = { 26 | .colors = 27 | { 28 | .background{C(21, 31, 33, 0.67f)}, 29 | .border{C(87, 149, 255, 0.85f)}, 30 | .bar{C(34, 115, 255, 0.75f)}, 31 | .secondsMarker{C(15, 63, 145, 0.75f)}, 32 | .text{C(191, 215, 255, 1.00f)}, 33 | }, 34 | }; 35 | 36 | #undef C 37 | 38 | void RewindBar(SharedContext &context, float alpha = 1.0f, const RewindBarStyle &style = g_defaultRewindBarStyle); 39 | 40 | } // namespace app::ui::widgets 41 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/hw/sh2/sh2_sci.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace ymir::sh2 { 4 | 5 | // addr r/w access init code name 6 | // 000 R/W 8 00 SMR Serial Mode Register 7 | // 8 | // b r/w code description 9 | // 7 R/W C/nA Communication Mode (0=async, 1=clocked sync) 10 | // 6 R/W CHR Character Length (0=8-bit, 1=7-bit) 11 | // 5 R/W PE Parity Enable (0=disable, 1=enable) 12 | // 4 R/W O/nE Parity Mode (0=even, 1=odd) 13 | // 3 R/W STOP Stop Bit Length (0=one, 1=two) 14 | // 2 R/W MP Multiprocessor Mode (0=disabled, 1=enabled) 15 | // 1 R/W CKS1 Clock Select bit 1 (00=phi/4, 01=phi/16, 16 | // 0 R/W CKS0 Clock Select bit 0 10=phi/64, 11=phi/256) 17 | 18 | // 001 R/W 8 FF BRR Bit Rate Register 19 | 20 | // 002 R/W 8 00 SCR Serial Control Register 21 | 22 | // 003 R/W 8 FF TDR Transmit Data Register 23 | 24 | // 004 R/W* 8 84 SSR Serial Status Register 25 | // * Can only write a 0 to clear the flags 26 | 27 | // 005 R 8 00 RDR Receive Data Register 28 | 29 | } // namespace ymir::sh2 30 | -------------------------------------------------------------------------------- /libs/ymir-core/include/ymir/media/loader/loader_iso.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "loader_result.hpp" 4 | 5 | #include 6 | 7 | #include 8 | 9 | // An ISO file contains raw sectors of a data track. There's no specific format to the file itself, but the contained 10 | // data must use a sector-based filesystem such as IS0 9660. 11 | // 12 | // ISO files do not store track or session information, therefore the entire file describes a disc with a single session 13 | // and one data track. 14 | // 15 | // The sector size must be derived from the total file size, which must be a multiple of 2048 or 2352. 16 | 17 | namespace ymir::media::loader::iso { 18 | 19 | // Attempts to load an ISO file from isoPath into the specified Disc object. 20 | // Returns true if loading the file succeeded. 21 | // If this function returns false, the Disc object is invalidated. 22 | // preloadToRAM specifies if the entire disc image should be preloaded into memory. 23 | // cbMsg is the callback for message reporting. 24 | bool Load(std::filesystem::path isoPath, Disc &disc, bool preloadToRAM, CbLoaderMessage cbMsg); 25 | 26 | } // namespace ymir::media::loader::iso 27 | -------------------------------------------------------------------------------- /apps/ymir-sdl3/src/app/ui/window_base.cpp: -------------------------------------------------------------------------------- 1 | #include "window_base.hpp" 2 | 3 | namespace app::ui { 4 | 5 | WindowBase::WindowBase(SharedContext &context) 6 | : m_context(context) {} 7 | 8 | void WindowBase::Display() { 9 | if (!Open) { 10 | return; 11 | } 12 | 13 | PrepareWindow(); 14 | if (!Open) { 15 | // Second opportunity to close the window 16 | return; 17 | } 18 | 19 | if (m_focusRequested) { 20 | m_focusRequested = false; 21 | ImGui::SetNextWindowFocus(); 22 | } 23 | 24 | if (ImGui::Begin(m_windowConfig.name.c_str(), &Open, m_windowConfig.flags)) { 25 | DrawContents(); 26 | 27 | // Close the window if nothing is focused and B/Circle is pressed 28 | 29 | if (m_windowConfig.allowClosingWithGamepad && ImGui::IsWindowFocused() && !ImGui::IsAnyItemFocused() && 30 | !ImGui::GetIO().NavVisible && ImGui::IsKeyPressed(ImGuiKey_GamepadFaceRight)) { 31 | Open = false; 32 | } 33 | } 34 | ImGui::End(); 35 | } 36 | 37 | void WindowBase::RequestFocus() { 38 | if (Open) { 39 | m_focusRequested = true; 40 | } 41 | } 42 | 43 | } // namespace app::ui 44 | -------------------------------------------------------------------------------- /vcpkg-triplets/x64-win-llvm-lto-static-md.cmake: -------------------------------------------------------------------------------- 1 | # This triplet is tested in vcpkg ci via https://github.com/microsoft/vcpkg/pull/25897 2 | set(VCPKG_TARGET_ARCHITECTURE x64) 3 | set(VCPKG_CRT_LINKAGE dynamic) 4 | set(VCPKG_LIBRARY_LINKAGE static) 5 | #set(VCPKG_BUILD_TYPE release) 6 | 7 | ## Toolchain setup 8 | set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE "${CMAKE_CURRENT_LIST_DIR}/x64-win-llvm/x64-win-llvm.toolchain.cmake") 9 | set(VCPKG_LOAD_VCVARS_ENV ON) # Setting VCPKG_CHAINLOAD_TOOLCHAIN_FILE deactivates automatic vcvars setup so reenable it! 10 | 11 | if(DEFINED VCPKG_PLATFORM_TOOLSET) 12 | set(VCPKG_PLATFORM_TOOLSET ClangCL) 13 | endif() 14 | set(VCPKG_ENV_PASSTHROUGH_UNTRACKED "LLVMInstallDir;LLVMToolsVersion") # For the ClangCL toolset 15 | set(VCPKG_QT_TARGET_MKSPEC win32-clang-msvc) # For Qt5 16 | 17 | ## Policy settings 18 | set(VCPKG_POLICY_SKIP_ARCHITECTURE_CHECK enabled) 19 | set(VCPKG_POLICY_SKIP_DUMPBIN_CHECKS enabled) 20 | 21 | list(APPEND VCPKG_CMAKE_CONFIGURE_OPTIONS 22 | "-DCMAKE_TRY_COMPILE_CONFIGURATION=Release" 23 | ) 24 | 25 | include("${CMAKE_CURRENT_LIST_DIR}/x64-win-llvm/extra_setup.cmake") 26 | include("${CMAKE_CURRENT_LIST_DIR}/x64-win-llvm/port_specialization.cmake") 27 | -------------------------------------------------------------------------------- /vcpkg-triplets/x64-win-llvm-san-static-md.cmake: -------------------------------------------------------------------------------- 1 | # This triplet is tested in vcpkg ci via https://github.com/microsoft/vcpkg/pull/25897 2 | set(VCPKG_TARGET_ARCHITECTURE x64) 3 | set(VCPKG_CRT_LINKAGE dynamic) 4 | set(VCPKG_LIBRARY_LINKAGE static) 5 | #set(VCPKG_BUILD_TYPE release) 6 | 7 | ## Toolchain setup 8 | set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE "${CMAKE_CURRENT_LIST_DIR}/x64-win-llvm/x64-win-llvm.toolchain.cmake") 9 | set(VCPKG_LOAD_VCVARS_ENV ON) # Setting VCPKG_CHAINLOAD_TOOLCHAIN_FILE deactivates automatic vcvars setup so reenable it! 10 | 11 | if(DEFINED VCPKG_PLATFORM_TOOLSET) 12 | set(VCPKG_PLATFORM_TOOLSET ClangCL) 13 | endif() 14 | set(VCPKG_ENV_PASSTHROUGH_UNTRACKED "LLVMInstallDir;LLVMToolsVersion") # For the ClangCL toolset 15 | set(VCPKG_QT_TARGET_MKSPEC win32-clang-msvc) # For Qt5 16 | 17 | ## Policy settings 18 | set(VCPKG_POLICY_SKIP_ARCHITECTURE_CHECK enabled) 19 | set(VCPKG_POLICY_SKIP_DUMPBIN_CHECKS enabled) 20 | 21 | list(APPEND VCPKG_CMAKE_CONFIGURE_OPTIONS 22 | "-DCMAKE_TRY_COMPILE_CONFIGURATION=Release" 23 | ) 24 | 25 | include("${CMAKE_CURRENT_LIST_DIR}/x64-win-llvm/extra_setup.cmake") 26 | include("${CMAKE_CURRENT_LIST_DIR}/x64-win-llvm/port_specialization.cmake") 27 | -------------------------------------------------------------------------------- /vcpkg-triplets/x64-win-llvm-lto-san-static-md.cmake: -------------------------------------------------------------------------------- 1 | # This triplet is tested in vcpkg ci via https://github.com/microsoft/vcpkg/pull/25897 2 | set(VCPKG_TARGET_ARCHITECTURE x64) 3 | set(VCPKG_CRT_LINKAGE dynamic) 4 | set(VCPKG_LIBRARY_LINKAGE static) 5 | #set(VCPKG_BUILD_TYPE release) 6 | 7 | ## Toolchain setup 8 | set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE "${CMAKE_CURRENT_LIST_DIR}/x64-win-llvm/x64-win-llvm.toolchain.cmake") 9 | set(VCPKG_LOAD_VCVARS_ENV ON) # Setting VCPKG_CHAINLOAD_TOOLCHAIN_FILE deactivates automatic vcvars setup so reenable it! 10 | 11 | if(DEFINED VCPKG_PLATFORM_TOOLSET) 12 | set(VCPKG_PLATFORM_TOOLSET ClangCL) 13 | endif() 14 | set(VCPKG_ENV_PASSTHROUGH_UNTRACKED "LLVMInstallDir;LLVMToolsVersion") # For the ClangCL toolset 15 | set(VCPKG_QT_TARGET_MKSPEC win32-clang-msvc) # For Qt5 16 | 17 | ## Policy settings 18 | set(VCPKG_POLICY_SKIP_ARCHITECTURE_CHECK enabled) 19 | set(VCPKG_POLICY_SKIP_DUMPBIN_CHECKS enabled) 20 | 21 | list(APPEND VCPKG_CMAKE_CONFIGURE_OPTIONS 22 | "-DCMAKE_TRY_COMPILE_CONFIGURATION=Release" 23 | ) 24 | 25 | include("${CMAKE_CURRENT_LIST_DIR}/x64-win-llvm/extra_setup.cmake") 26 | include("${CMAKE_CURRENT_LIST_DIR}/x64-win-llvm/port_specialization.cmake") 27 | -------------------------------------------------------------------------------- /docs/dev-notes/finicky-games/vdp2-bad-window-params.txt: -------------------------------------------------------------------------------- 1 | ============================================= 2 | Games that set invalid VDP2 window parameters 3 | ============================================= 4 | 5 | These games specify invalid window ranges according to the official docs. 6 | It would seem as though window coordinates are handled as signed integers. 7 | 8 | Panzer Dragoon II Zwei 9 | Builds line window tables that contain invalid start or end coordinates in Episode 3 10 | 0000 to FFFE -> empty window (all outside) 11 | FFFE to 02C0 -> full line (all inside) 12 | 13 | Panzer Dragoon Saga 14 | The orange swirling background when entering the player name uses a line window table. 15 | The bottom half of the table has invalid entries: 16 | 0000 to FFFF -> empty window (all outside) 17 | 18 | Radiant Silvergun 19 | The transition to the start of Stage 2C involves a background rotation that uses illegal window coordinates: 20 | 0000 to FFFF -> outside 21 | 22 | Snatcher 23 | Sets up window parameters such that the starting point is greater than the ending point. 24 | Specifically: 25 | 01FF to 0001 -> empty window (all outside) 26 | FFFC to 0286 -> full line (all inside) 27 | --------------------------------------------------------------------------------