diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 50a8e9b3..a084866d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -284,6 +284,13 @@ jobs: env: CC: ${{ steps.install_cc.outputs.cc }} run: | + . .ci/common.sh + export LATEST_RELEASE=$(download_with_headers "https://api.github.com/repos/sysprog21/rv32emu-prebuilt/releases" \ + "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ + | grep '"tag_name"' \ + | grep "sail" \ + | head -n 1 \ + | sed -E 's/.*"tag_name": "([^"]+)".*/\1/') .ci/riscv-tests.sh if: ${{ always() }} @@ -341,7 +348,11 @@ jobs: - uses: actions/checkout@v4 - name: install-dependencies run: | - brew install make dtc expect sdl2 sdl2_mixer bc e2fsprogs p7zip llvm@18 dcfldd + brew install make dtc expect sdl2 bc e2fsprogs p7zip llvm@18 dcfldd + brew install sdl2_mixer || { + echo "Warning: sdl2_mixer installation failed, continuing without SDL_MIXER support" + echo "ENABLE_SDL_MIXER=0" >> $GITHUB_ENV + } .ci/riscv-toolchain-install.sh echo "${{ github.workspace }}/toolchain/bin" >> $GITHUB_PATH echo "$(brew --prefix llvm@18)/bin" >> $GITHUB_PATH @@ -489,6 +500,13 @@ jobs: env: CC: ${{ steps.install_cc.outputs.cc }} run: | + . .ci/common.sh + export LATEST_RELEASE=$(download_with_headers "https://api.github.com/repos/sysprog21/rv32emu-prebuilt/releases" \ + "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ + | grep '"tag_name"' \ + | grep "sail" \ + | head -n 1 \ + | sed -E 's/.*"tag_name": "([^"]+)".*/\1/') python3 -m venv venv . venv/bin/activate .ci/riscv-tests.sh @@ -525,8 +543,12 @@ jobs: sudo apt-get install -q=2 clang-18 clang-tools-18 shell: bash - name: run scan-build without JIT + env: + LATEST_RELEASE: dummy run: make distclean && scan-build-18 -v -o ~/scan-build --status-bugs --use-cc=clang-18 --force-analyze-debug-code --show-description -analyzer-config stable-report-filename=true -enable-checker valist,nullability make ENABLE_EXT_F=0 ENABLE_SDL=0 ENABLE_JIT=0 - name: run scan-build with JIT + env: + LATEST_RELEASE: dummy run: | make ENABLE_JIT=1 distclean && scan-build-18 -v -o ~/scan-build --status-bugs --use-cc=clang-18 --force-analyze-debug-code --show-description -analyzer-config stable-report-filename=true -enable-checker valist,nullability make ENABLE_EXT_F=0 ENABLE_SDL=0 ENABLE_JIT=1 diff --git a/Makefile b/Makefile index e3eae877..a81a4125 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,11 @@ include mk/common.mk include mk/toolchain.mk +# Verify GNU make version (3.81+ required) +ifeq ($(filter 3.81 3.82 3.83 3.84 4.% 5.%,$(MAKE_VERSION)),) +$(error GNU make 3.81 or higher is required. Current version: $(MAKE_VERSION)) +endif + OUT ?= build BIN := $(OUT)/rv32emu @@ -46,6 +51,7 @@ endif # Device Tree(initrd, memory range) # src/io.c(memory init) # src/riscv.c(system emulation layout init) +# Note: These memory settings are for SYSTEM mode only (when ELF_LOADER=0) ifeq ($(call has, SYSTEM), 1) ifeq ($(call has, ELF_LOADER), 0) MiB = 1024*1024 @@ -66,6 +72,7 @@ CFLAGS_dt += -DMEM_START=0x$(MEM_START) \ -DINITRD_END=0x$(shell echo "obase=16; ibase=16; \ $(REAL_MEM_SIZE) - $(call compute_size, $(DTB_SIZE)) - 1" | bc) +# Memory size for SYSTEM mode (may be overridden by FULL4G if ENABLE_ELF_LOADER=1) CFLAGS += -DMEM_SIZE=0x$(REAL_MEM_SIZE) -DDTB_SIZE=0x$(REAL_DTB_SIZE) -DINITRD_SIZE=0x$(REAL_INITRD_SIZE) endif endif @@ -192,27 +199,58 @@ ENABLE_FULL4G ?= 0 # Experimental SDL oriented system calls ENABLE_SDL ?= 1 -ifneq ("$(CC_IS_EMCC)", "1") # note that emcc generates port SDL headers/library, so it does not requires system SDL headers/library -ifeq ($(call has, SDL), 1) -ifeq (, $(shell which sdl2-config)) +ENABLE_SDL_MIXER ?= 1 + +# Step 1: Detect SDL availability +ifneq ("$(CC_IS_EMCC)", "1") +# Native build: require sdl2-config for SDL support +ifeq (, $(shell command -v sdl2-config)) $(warning No sdl2-config in $$PATH. Check SDL2 installation in advance) override ENABLE_SDL := 0 +override ENABLE_SDL_MIXER := 0 endif -ifeq (1, $(shell pkg-config --exists SDL2_mixer; echo $$?)) -$(warning No SDL2_mixer lib installed. Check SDL2_mixer installation in advance) -override ENABLE_SDL := 0 +else +# Emscripten build: SDL is available via ports, but SDL_MIXER has unfixable warnings +# The emscripten-ports/SDL2_mixer was archived in Jan 2024 with compilation issues +override ENABLE_SDL_MIXER := 0 +endif + +# Enforce dependency: SDL_MIXER requires SDL +ifeq ($(ENABLE_SDL), 0) +override ENABLE_SDL_MIXER := 0 +endif + +# Step 2: If SDL is enabled on native builds, check for SDL_MIXER +ifneq ("$(CC_IS_EMCC)", "1") +ifeq ($(ENABLE_SDL), 1) +ifneq (, $(shell command -v pkg-config)) +ifneq (0, $(shell pkg-config --exists SDL2_mixer; echo $$?)) +$(warning No SDL2_mixer lib installed. SDL2_mixer support will be disabled) +override ENABLE_SDL_MIXER := 0 endif +else +$(warning No pkg-config found. Disabling SDL2_mixer support) +override ENABLE_SDL_MIXER := 0 endif +endif +endif + $(call set-feature, SDL) +$(call set-feature, SDL_MIXER) ifeq ($(call has, SDL), 1) OBJS_EXT += syscall_sdl.o +ifneq ("$(CC_IS_EMCC)", "1") $(OUT)/syscall_sdl.o: CFLAGS += $(shell sdl2-config --cflags) +endif # 4 GiB of memory is required to run video games. ENABLE_FULL4G := 1 +ifneq ("$(CC_IS_EMCC)", "1") LDFLAGS += $(shell sdl2-config --libs) -pthread +ifeq ($(call has, SDL_MIXER), 1) LDFLAGS += $(shell pkg-config --libs SDL2_mixer) endif endif +endif # If SYSTEM is enabled and ELF_LOADER is not, then skip FULL4G bacause guestOS # has dedicated memory mapping range. @@ -225,7 +263,22 @@ endif # Full access to a 4 GiB address space, necessitating more memory mapping # during emulator initialization. $(call set-feature, FULL4G) + +# Configuration validation and conflict detection +ifeq ($(call has, SDL), 1) +ifeq ($(call has, SYSTEM), 1) +ifeq ($(call has, ELF_LOADER), 0) +ifeq ($(call has, FULL4G), 0) + $(warning SDL requires FULL4G=1 but SYSTEM forces FULL4G=0. Set ENABLE_ELF_LOADER=1 or disable SDL/SYSTEM) +endif +endif +endif +endif + ifeq ($(call has, FULL4G), 1) +# Note: If both SYSTEM and FULL4G are enabled with ELF_LOADER=1, +# this MEM_SIZE definition will override the SYSTEM mode definition. +# This is intentional for ELF loader use cases. CFLAGS += -DMEM_SIZE=0xFFFFFFFFULL # 2^{32} - 1 endif @@ -278,6 +331,8 @@ ifeq ($(call has, JIT), 1) else $(error No llvm-config-18 installed. Check llvm-config-18 installation in advance, or use "ENABLE_T2C=0" to disable tier-2 LLVM compiler) endif + else + $(warning T2C (tier-2 compiler) is disabled. Using tier-1 JIT only.) endif ifneq ($(processor),$(filter $(processor),x86_64 aarch64 arm64)) $(error JIT mode only supports for x64 and arm64 target currently.) @@ -353,19 +408,31 @@ ifeq ($(call has, GDBSTUB), 1) $(OBJS): $(GDBSTUB_LIB) endif -$(OUT)/%.o: src/%.c $(deps_emcc) - $(Q)mkdir -p $(shell dirname $@) +$(OUT)/%.o: src/%.c $(CONFIG_FILE) $(deps_emcc) | $(OUT) + $(Q)mkdir -p $(dir $@) $(VECHO) " CC\t$@\n" $(Q)$(CC) -o $@ $(CFLAGS) $(CFLAGS_emcc) -c -MMD -MF $@.d $< -$(BIN): $(OBJS) $(DEV_OBJS) +$(OUT): + $(Q)mkdir -p $@ + +$(BIN): $(OBJS) $(DEV_OBJS) | $(OUT) $(VECHO) " LD\t$@\n" $(Q)$(CC) -o $@ $(CFLAGS_emcc) $^ $(LDFLAGS) +$(CONFIG_FILE): FORCE + $(Q)mkdir -p $(OUT) + $(Q)echo "$(CFLAGS)" | xargs -n1 | sort | sed -n 's/^RV32_FEATURE/ENABLE/p' > $@.tmp + $(Q)if ! cmp -s $@ $@.tmp 2>/dev/null; then \ + mv $@.tmp $@; \ + $(PRINTF) "Configuration updated. Check $(OUT)/.config for configured items.\n"; \ + else \ + $(RM) $@.tmp; \ + fi + +.PHONY: FORCE config +FORCE: config: $(CONFIG_FILE) -$(CONFIG_FILE): - $(Q)echo "$(CFLAGS)" | xargs -n1 | sort | sed -n 's/^RV32_FEATURE/ENABLE/p' > $@ - $(VECHO) "Check the file $(OUT)/.config for configured items.\n" # Tools include mk/tools.mk @@ -460,4 +527,7 @@ distclean: clean $(Q)-$(RM) -r $(SOFTFLOAT_DUMMY_PLAT) $(OUT)/softfloat $(Q)$(call notice, [OK]) +.PHONY: all config tool check check-hello misalign misalign-in-blk-emu mmu-test +.PHONY: gdbstub-test doom quake clean distclean artifact + -include $(deps) diff --git a/mk/external.mk b/mk/external.mk index 16f9fa44..b4d7f68e 100644 --- a/mk/external.mk +++ b/mk/external.mk @@ -4,10 +4,10 @@ COMPRESSED_IS_DIR := EXTRACTOR := VERIFIER := -# temporarily files to store correct SHA value and computed SHA value +# Temporary files to store correct SHA value and computed SHA value # respectively for verification of directory source -$(eval SHA_FILE1 := $(shell mktemp)) -$(eval SHA_FILE2 := $(shell mktemp)) +SHA_FILE1 := $(shell mktemp) +SHA_FILE2 := $(shell mktemp) # $(1): compressed source define prologue @@ -17,7 +17,7 @@ endef # $(1), $(2), $(3): files to be deleted define epilogue - $(eval _ := $(shell $(RM) $(1) $(2) $(3))) + $(RM) $(1) $(2) $(3) endef # $(1): compressed source URL diff --git a/mk/riscv-arch-test.mk b/mk/riscv-arch-test.mk index 5ca0941d..941e5009 100644 --- a/mk/riscv-arch-test.mk +++ b/mk/riscv-arch-test.mk @@ -1,8 +1,8 @@ riscof-check: $(Q)if [ "$(shell pip show riscof 2>&1 | head -n 1 | cut -d' ' -f1)" = "WARNING:" ]; then \ - $(PRINTF) "Run 'pip3 install -r requirements.txt to install dependencies.\n"; \ - exit 1; \ - fi; + $(PRINTF) "Run 'pip3 install -r requirements.txt' to install dependencies.\n"; \ + exit 1; \ + fi ARCH_TEST_DIR ?= tests/riscv-arch-test ARCH_TEST_SUITE ?= $(ARCH_TEST_DIR)/riscv-test-suite @@ -27,3 +27,5 @@ endif --config=$(RISCV_TARGET)/config.ini \ --suite=$(ARCH_TEST_SUITE) \ --env=$(ARCH_TEST_DIR)/riscv-test-suite/env + +.PHONY: riscof-check arch-test diff --git a/mk/softfloat.mk b/mk/softfloat.mk index acc9a9e2..cb82a5e0 100644 --- a/mk/softfloat.mk +++ b/mk/softfloat.mk @@ -334,11 +334,13 @@ $(SOFTFLOAT_DUMMY_PLAT): $(Q)touch $@ $(SOFTFLOAT_FILES): $(SOFTFLOAT_SENTINEL) -$(OUT)/softfloat/%.o: $(SOFTFLOAT_DIR)/%.c $(SOFTFLOAT_SENTINEL) $(SOFTFLOAT_DUMMY_PLAT) - $(Q)mkdir -p $(shell dirname $@) +$(OUT)/softfloat/%.o: $(SOFTFLOAT_DIR)/%.c $(CONFIG_FILE) $(SOFTFLOAT_SENTINEL) $(SOFTFLOAT_DUMMY_PLAT) | $(OUT)/softfloat $(OUT)/softfloat/RISCV $(VECHO) " CC\t$@\n" $(Q)$(CC) -o $@ $(CFLAGS) $(CFLAGS_softfloat) -c -MMD -MF $@.d $< +$(OUT)/softfloat $(OUT)/softfloat/RISCV: + $(Q)mkdir -p $@ + SOFTFLOAT_LIB := $(OUT)/softfloat/softfloat.a $(SOFTFLOAT_LIB): $(SOFTFLOAT_OBJS) $(VECHO) " AR\t$@\n" diff --git a/mk/system.mk b/mk/system.mk index 14d73591..6f63fb44 100644 --- a/mk/system.mk +++ b/mk/system.mk @@ -29,10 +29,13 @@ $(BUILD_DTB2C): $(BIN_TO_C) $(BUILD_DTB) $(VECHO) " BIN2C\t$@\n" $(Q)$(BIN_TO_C) $(BUILD_DTB) > $@ -$(DEV_OUT)/%.o: $(DEV_SRC)/%.c $(deps_emcc) - $(Q)mkdir -p $(DEV_OUT) +$(DEV_OUT)/%.o: $(DEV_SRC)/%.c $(CONFIG_FILE) $(deps_emcc) | $(DEV_OUT) $(VECHO) " CC\t$@\n" $(Q)$(CC) -o $@ $(CFLAGS) $(CFLAGS_emcc) -c -MMD -MF $@.d $< + +$(DEV_OUT): + $(Q)mkdir -p $@ + DEV_OBJS := $(patsubst $(DEV_SRC)/%.c, $(DEV_OUT)/%.o, $(wildcard $(DEV_SRC)/*.c)) deps := $(DEV_OBJS:%.o=%.o.d) @@ -46,4 +49,6 @@ system_deps += artifact $(BUILD_DTB) $(BUILD_DTB2C) $(BIN) system: $(system_deps) $(system_action) +.PHONY: system + endif diff --git a/mk/tests.mk b/mk/tests.mk index 29816e3c..cf89f82a 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -85,11 +85,13 @@ $(CACHE_TEST_TARGET): $(CACHE_TEST_OBJS) $(VECHO) " CC\t$@\n" $(Q)$(CC) $^ -o $@ $(LDFLAGS) -$(CACHE_TEST_OUTDIR)/%.o: $(CACHE_TEST_SRCDIR)/%.c +$(CACHE_TEST_OUTDIR)/%.o: $(CACHE_TEST_SRCDIR)/%.c $(CONFIG_FILE) | $(CACHE_TEST_OUTDIR) $(CACHE_TEST_OUTDIR)/lfu $(VECHO) " CC\t$@\n" - $(Q)mkdir -p $(dir $@)/lfu $(Q)$(CC) -o $@ $(CFLAGS) -I./src -c -MMD -MF $@.d $< +$(CACHE_TEST_OUTDIR) $(CACHE_TEST_OUTDIR)/lfu: + $(Q)mkdir -p $@ + $(MAP_TEST_OUT): $(MAP_TEST_TARGET) $(Q)touch $@ @@ -97,11 +99,13 @@ $(MAP_TEST_TARGET): $(MAP_TEST_OBJS) $(VECHO) " CC\t$@\n" $(Q)$(CC) $^ -o $@ $(LDFLAGS) -$(MAP_TEST_OUTDIR)/%.o: $(MAP_TEST_SRCDIR)/%.c +$(MAP_TEST_OUTDIR)/%.o: $(MAP_TEST_SRCDIR)/%.c $(CONFIG_FILE) | $(MAP_TEST_OUTDIR) $(VECHO) " CC\t$@\n" - $(Q)mkdir -p $(dir $@) $(Q)$(CC) -o $@ $(CFLAGS) -I./src -c -MMD -MF $@.d $< +$(MAP_TEST_OUTDIR): + $(Q)mkdir -p $@ + $(PATH_TEST_OUT): $(PATH_TEST_TARGET) $(Q)touch $@ @@ -109,7 +113,11 @@ $(PATH_TEST_TARGET): $(PATH_TEST_OBJS) $(VECHO) " CC\t$@\n" $(Q)$(CC) $^ -o $@ $(LDFLAGS) -$(PATH_TEST_OUTDIR)/%.o: $(PATH_TEST_SRCDIR)/%.c +$(PATH_TEST_OUTDIR)/%.o: $(PATH_TEST_SRCDIR)/%.c $(CONFIG_FILE) | $(PATH_TEST_OUTDIR) $(VECHO) " CC\t$@\n" - $(Q)mkdir -p $(dir $@) $(Q)$(CC) -o $@ $(CFLAGS) -I./src -c -MMD -MF $@.d $< + +$(PATH_TEST_OUTDIR): + $(Q)mkdir -p $@ + +.PHONY: tests run-test-cache run-test-map run-test-path diff --git a/mk/tools.mk b/mk/tools.mk index 0d6a2157..fbef691f 100644 --- a/mk/tools.mk +++ b/mk/tools.mk @@ -23,7 +23,7 @@ HIST_OBJS := \ HIST_OBJS := $(addprefix $(OUT)/, $(HIST_OBJS)) deps += $(HIST_OBJS:%.o=%.o.d) -$(OUT)/%.o: tools/%.c +$(OUT)/%.o: tools/%.c | $(OUT) $(VECHO) " CC\t$@\n" $(Q)$(CC) -o $@ $(CFLAGS) -Wno-missing-field-initializers -Isrc -c -MMD -MF $@.d $< @@ -39,3 +39,5 @@ LINUX_IMAGE_SRC = $(BUILDROOT_DATA) $(LINUX_DATA) build-linux-image: $(LINUX_IMAGE_SRC) $(Q)./tools/build-linux-image.sh $(Q)$(PRINTF) "Build done.\n" + +.PHONY: build-linux-image diff --git a/mk/wasm.mk b/mk/wasm.mk index 21c0d2d6..1bbfcaaa 100644 --- a/mk/wasm.mk +++ b/mk/wasm.mk @@ -18,9 +18,15 @@ CFLAGS += -mtail-call # Build emscripten-port SDL ifeq ($(call has, SDL), 1) -CFLAGS_emcc += -sUSE_SDL=2 -sSDL2_MIXER_FORMATS=wav,mid -sUSE_SDL_MIXER=2 +CFLAGS_emcc += -sUSE_SDL=2 OBJS_EXT += syscall_sdl.o LDFLAGS += -pthread +# SDL_MIXER is disabled by default for emscripten due to archived port with warnings +ifeq ($(call has, SDL_MIXER), 1) +# Note: Enabling SDL_MIXER requires -sSTRICT=0 due to unfixable warnings in music_modplug.c +# The emscripten-ports/SDL2_mixer was archived in Jan 2024 +CFLAGS_emcc += -sSTRICT=0 -sSDL2_MIXER_FORMATS=wav,mid -sUSE_SDL_MIXER=2 +endif endif # More build flags @@ -159,4 +165,7 @@ start-web: $(start_web_deps) $(foreach T, $(STATIC_WEB_FILES), $(call cp-web-file, $(T))) $(Q)mv $(DEMO_DIR)/*.html $(DEMO_DIR)/index.html $(Q)python3 -m http.server --bind $(DEMO_IP) $(DEMO_PORT) --directory $(DEMO_DIR) + +.PHONY: check-demo-dir-exist start-web + endif diff --git a/src/feature.h b/src/feature.h index df384ca3..1b63dfe9 100644 --- a/src/feature.h +++ b/src/feature.h @@ -67,6 +67,17 @@ #define RV32_FEATURE_SDL 1 #endif +/* SDL2_mixer audio support (requires SDL) */ +#ifndef RV32_FEATURE_SDL_MIXER +#define RV32_FEATURE_SDL_MIXER 1 +#endif + +/* Enforce dependency: SDL_MIXER requires SDL */ +#if RV32_FEATURE_SDL_MIXER && !RV32_FEATURE_SDL +#undef RV32_FEATURE_SDL_MIXER +#define RV32_FEATURE_SDL_MIXER 0 +#endif + /* GDB remote debugging */ #ifndef RV32_FEATURE_GDBSTUB #define RV32_FEATURE_GDBSTUB 0 diff --git a/src/syscall_sdl.c b/src/syscall_sdl.c index ae95b37f..0fff951e 100644 --- a/src/syscall_sdl.c +++ b/src/syscall_sdl.c @@ -15,7 +15,9 @@ #include #include +#if RV32_HAS(SDL_MIXER) #include +#endif #include "riscv.h" #include "riscv_private.h" @@ -92,6 +94,7 @@ typedef struct sound { } sound_t; /* SDL-mixer-related and music-related variables */ +#if RV32_HAS(SDL_MIXER) static pthread_t music_thread; static uint8_t *music_midi_data; static Mix_Music *mid; @@ -107,6 +110,7 @@ static int chan; static bool audio_init = false; static bool sfx_thread_init = false; static bool music_thread_init = false; +#endif typedef struct { void *data; @@ -718,6 +722,7 @@ uint8_t *mus2midi(uint8_t *data, int *length) return midi_data; } +#if RV32_HAS(SDL_MIXER) static void *sfx_handler(void *arg) { sound_t *sfx = (sound_t *) arg; @@ -916,7 +921,9 @@ static void set_music_volume(riscv_t *rv) /* multiplied by 8 because volume's max is 15 */ Mix_VolumeMusic(volume * 8); } +#endif /* RV32_HAS(SDL_MIXER) */ +#if RV32_HAS(SDL_MIXER) static void init_audio(void) { if (!(SDL_WasInit(-1) & SDL_INIT_AUDIO)) { @@ -977,6 +984,7 @@ static void shutdown_audio() audio_init = sfx_thread_init = music_thread_init = false; } +#endif void sdl_video_audio_cleanup() { @@ -984,6 +992,8 @@ void sdl_video_audio_cleanup() SDL_DestroyWindow(window); window = NULL; } + +#if RV32_HAS(SDL_MIXER) /* * The sfx_or_music_thread_init flag might not be set if a quick ctrl-c * occurs while the audio configuration is being initialized. Therefore, @@ -992,6 +1002,7 @@ void sdl_video_audio_cleanup() bool sfx_or_music_thread_init = sfx_thread_init | music_thread_init; if (sfx_or_music_thread_init || (!sfx_or_music_thread_init && audio_init)) shutdown_audio(); +#endif SDL_Quit(); } @@ -1002,10 +1013,14 @@ void syscall_setup_audio(riscv_t *rv) switch (request) { case INIT_AUDIO: +#if RV32_HAS(SDL_MIXER) init_audio(); +#endif break; case SHUTDOWN_AUDIO: +#if RV32_HAS(SDL_MIXER) shutdown_audio(); +#endif break; default: rv_log_error("Unknown sound request: %d", request); @@ -1020,16 +1035,24 @@ void syscall_control_audio(riscv_t *rv) switch (request) { case PLAY_MUSIC: +#if RV32_HAS(SDL_MIXER) play_music(rv); +#endif break; case PLAY_SFX: +#if RV32_HAS(SDL_MIXER) play_sfx(rv); +#endif break; case SET_MUSIC_VOLUME: +#if RV32_HAS(SDL_MIXER) set_music_volume(rv); +#endif break; case STOP_MUSIC: +#if RV32_HAS(SDL_MIXER) stop_music(); +#endif break; default: rv_log_error("Unknown sound control request: %d", request);